From bd834220959bf7edcc4cce4bfe6c77e0a4649b8b Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 7 Sep 2012 18:30:48 +0200 Subject: [PATCH 001/272] put filestorages in a namespace --- apps/files_encryption/tests/proxy.php | 2 +- apps/files_external/appinfo/app.php | 16 ++--- apps/files_external/lib/amazons3.php | 14 ++-- apps/files_external/lib/config.php | 18 ++--- apps/files_external/lib/dropbox.php | 18 ++--- apps/files_external/lib/ftp.php | 6 +- apps/files_external/lib/google.php | 18 ++--- apps/files_external/lib/smb.php | 4 +- apps/files_external/lib/streamwrapper.php | 5 +- apps/files_external/lib/swift.php | 12 ++-- apps/files_external/lib/webdav.php | 12 ++-- apps/files_external/personal.php | 2 +- apps/files_external/tests/amazons3.php | 2 +- apps/files_external/tests/ftp.php | 2 +- apps/files_external/tests/google.php | 2 +- apps/files_external/tests/smb.php | 2 +- apps/files_external/tests/swift.php | 2 +- apps/files_external/tests/webdav.php | 2 +- apps/files_sharing/appinfo/app.php | 4 +- apps/files_sharing/lib/sharedstorage.php | 68 ++++++++++--------- lib/base.php | 3 + lib/{filestorage => files/storage}/common.php | 44 +++++------- .../storage}/commontest.php | 10 +-- lib/{filestorage => files/storage}/local.php | 17 +++-- .../storage/storage.php} | 28 +++----- lib/files/storage/temporary.php | 26 +++++++ lib/filestorage/temporary.php | 17 ----- lib/filesystem.php | 12 ++-- lib/filesystemview.php | 12 ++-- lib/util.php | 4 +- tests/lib/cache/file.php | 2 +- tests/lib/filestorage.php | 2 +- tests/lib/filestorage/commontest.php | 2 +- tests/lib/filestorage/local.php | 2 +- tests/lib/filesystem.php | 4 +- 35 files changed, 205 insertions(+), 191 deletions(-) rename lib/{filestorage => files/storage}/common.php (83%) rename lib/{filestorage => files/storage}/commontest.php (88%) rename lib/{filestorage => files/storage}/local.php (90%) rename lib/{filestorage.php => files/storage/storage.php} (71%) create mode 100644 lib/files/storage/temporary.php delete mode 100644 lib/filestorage/temporary.php diff --git a/apps/files_encryption/tests/proxy.php b/apps/files_encryption/tests/proxy.php index 042011a6c8..d600bbc407 100644 --- a/apps/files_encryption/tests/proxy.php +++ b/apps/files_encryption/tests/proxy.php @@ -30,7 +30,7 @@ class Test_CryptProxy extends UnitTestCase { //set up temporary storage OC_Filesystem::clearMounts(); - OC_Filesystem::mount('OC_Filestorage_Temporary',array(),'/'); + OC_Filesystem::mount('\OC\Files\Storage\Temporary',array(),'/'); OC_Filesystem::init('/'.$user.'/files'); diff --git a/apps/files_external/appinfo/app.php b/apps/files_external/appinfo/app.php index 837d35c9c6..c58cfcd0f5 100644 --- a/apps/files_external/appinfo/app.php +++ b/apps/files_external/appinfo/app.php @@ -6,14 +6,14 @@ * See the COPYING-README file. */ -OC::$CLASSPATH['OC_FileStorage_StreamWrapper']='apps/files_external/lib/streamwrapper.php'; -OC::$CLASSPATH['OC_Filestorage_FTP']='apps/files_external/lib/ftp.php'; -OC::$CLASSPATH['OC_Filestorage_DAV']='apps/files_external/lib/webdav.php'; -OC::$CLASSPATH['OC_Filestorage_Google']='apps/files_external/lib/google.php'; -OC::$CLASSPATH['OC_Filestorage_SWIFT']='apps/files_external/lib/swift.php'; -OC::$CLASSPATH['OC_Filestorage_SMB']='apps/files_external/lib/smb.php'; -OC::$CLASSPATH['OC_Filestorage_AmazonS3']='apps/files_external/lib/amazons3.php'; -OC::$CLASSPATH['OC_Filestorage_Dropbox']='apps/files_external/lib/dropbox.php'; +OC::$CLASSPATH['OC\Files\Storage\StreamWrapper']='apps/files_external/lib/streamwrapper.php'; +OC::$CLASSPATH['OC\Files\Storage\FTP']='apps/files_external/lib/ftp.php'; +OC::$CLASSPATH['OC\Files\Storage\DAV']='apps/files_external/lib/webdav.php'; +OC::$CLASSPATH['OC\Files\Storage\Google']='apps/files_external/lib/google.php'; +OC::$CLASSPATH['OC\Files\Storage\SWIFT']='apps/files_external/lib/swift.php'; +OC::$CLASSPATH['OC\Files\Storage\SMB']='apps/files_external/lib/smb.php'; +OC::$CLASSPATH['OC\Files\Storage\AmazonS3']='apps/files_external/lib/amazons3.php'; +OC::$CLASSPATH['OC\Files\Storage\Dropbox']='apps/files_external/lib/dropbox.php'; OC::$CLASSPATH['OC_Mount_Config']='apps/files_external/lib/config.php'; OCP\App::registerAdmin('files_external', 'settings'); diff --git a/apps/files_external/lib/amazons3.php b/apps/files_external/lib/amazons3.php index 41ec3c70b4..a17a70ed38 100644 --- a/apps/files_external/lib/amazons3.php +++ b/apps/files_external/lib/amazons3.php @@ -22,7 +22,9 @@ require_once 'aws-sdk/sdk.class.php'; -class OC_Filestorage_AmazonS3 extends OC_Filestorage_Common { +namespace OC\Files\Storage; + +class AmazonS3 extends \OC\Files\Storage\Common { private $s3; private $bucket; @@ -33,7 +35,7 @@ class OC_Filestorage_AmazonS3 extends OC_Filestorage_Common { // TODO options: storage class, encryption server side, encrypt before upload? public function __construct($params) { - $this->s3 = new AmazonS3(array('key' => $params['key'], 'secret' => $params['secret'])); + $this->s3 = new \AmazonS3(array('key' => $params['key'], 'secret' => $params['secret'])); $this->bucket = $params['bucket']; } @@ -96,7 +98,7 @@ class OC_Filestorage_AmazonS3 extends OC_Filestorage_Common { foreach ($response->body->CommonPrefixes as $object) { $files[] = basename($object->Prefix); } - OC_FakeDirStream::$dirs['amazons3'.$path] = $files; + \OC_FakeDirStream::$dirs['amazons3'.$path] = $files; return opendir('fakedir://amazons3'.$path); } return false; @@ -160,7 +162,7 @@ class OC_Filestorage_AmazonS3 extends OC_Filestorage_Common { switch ($mode) { case 'r': case 'rb': - $tmpFile = OC_Helper::tmpFile(); + $tmpFile = \OC_Helper::tmpFile(); $handle = fopen($tmpFile, 'w'); $response = $this->s3->get_object($this->bucket, $path, array('fileDownload' => $handle)); if ($response->isOK()) { @@ -184,8 +186,8 @@ class OC_Filestorage_AmazonS3 extends OC_Filestorage_Common { } else { $ext = ''; } - $tmpFile = OC_Helper::tmpFile($ext); - OC_CloseStreamWrapper::$callBacks[$tmpFile] = array($this, 'writeBack'); + $tmpFile = \OC_Helper::tmpFile($ext); + \OC_CloseStreamWrapper::$callBacks[$tmpFile] = array($this, 'writeBack'); if ($this->file_exists($path)) { $source = $this->fopen($path, 'r'); file_put_contents($tmpFile, $source); diff --git a/apps/files_external/lib/config.php b/apps/files_external/lib/config.php index eec31ec2ef..db09fbdd82 100755 --- a/apps/files_external/lib/config.php +++ b/apps/files_external/lib/config.php @@ -39,14 +39,14 @@ class OC_Mount_Config { */ public static function getBackends() { return array( - 'OC_Filestorage_Local' => array('backend' => 'Local', 'configuration' => array('datadir' => 'Location')), - 'OC_Filestorage_AmazonS3' => array('backend' => 'Amazon S3', 'configuration' => array('key' => 'Key', 'secret' => '*Secret', 'bucket' => 'Bucket')), - 'OC_Filestorage_Dropbox' => array('backend' => 'Dropbox', 'configuration' => array('configured' => '#configured','app_key' => 'App key', 'app_secret' => 'App secret', 'token' => '#token', 'token_secret' => '#token_secret'), 'custom' => 'dropbox'), - 'OC_Filestorage_FTP' => array('backend' => 'FTP', 'configuration' => array('host' => 'URL', 'user' => 'Username', 'password' => '*Password', 'root' => '&Root', 'secure' => '!Secure ftps://')), - 'OC_Filestorage_Google' => array('backend' => 'Google Drive', 'configuration' => array('configured' => '#configured', 'token' => '#token', 'token_secret' => '#token secret'), 'custom' => 'google'), - 'OC_Filestorage_SWIFT' => array('backend' => 'OpenStack Swift', 'configuration' => array('host' => 'URL', 'user' => 'Username', 'token' => '*Token', 'root' => '&Root', 'secure' => '!Secure ftps://')), - 'OC_Filestorage_SMB' => array('backend' => 'SMB', 'configuration' => array('host' => 'URL', 'user' => 'Username', 'password' => '*Password', 'share' => 'Share', 'root' => '&Root')), - 'OC_Filestorage_DAV' => array('backend' => 'WebDAV', 'configuration' => array('host' => 'URL', 'user' => 'Username', 'password' => '*Password', 'root' => '&Root', 'secure' => '!Secure https://')) + '\OC\Files\Storage\Local' => array('backend' => 'Local', 'configuration' => array('datadir' => 'Location')), + '\OC\Files\Storage\AmazonS3' => array('backend' => 'Amazon S3', 'configuration' => array('key' => 'Key', 'secret' => '*Secret', 'bucket' => 'Bucket')), + '\OC\Files\Storage\Dropbox' => array('backend' => 'Dropbox', 'configuration' => array('configured' => '#configured','app_key' => 'App key', 'app_secret' => 'App secret', 'token' => '#token', 'token_secret' => '#token_secret'), 'custom' => 'dropbox'), + '\OC\Files\Storage\FTP' => array('backend' => 'FTP', 'configuration' => array('host' => 'URL', 'user' => 'Username', 'password' => '*Password', 'root' => '&Root', 'secure' => '!Secure ftps://')), + '\OC\Files\Storage\Google' => array('backend' => 'Google Drive', 'configuration' => array('configured' => '#configured', 'token' => '#token', 'token_secret' => '#token secret'), 'custom' => 'google'), + '\OC\Files\Storage\SWIFT' => array('backend' => 'OpenStack Swift', 'configuration' => array('host' => 'URL', 'user' => 'Username', 'token' => '*Token', 'root' => '&Root', 'secure' => '!Secure ftps://')), + '\OC\Files\Storage\SMB' => array('backend' => 'SMB', 'configuration' => array('host' => 'URL', 'user' => 'Username', 'password' => '*Password', 'share' => 'Share', 'root' => '&Root')), + '\OC\Files\Storage\DAV' => array('backend' => 'WebDAV', 'configuration' => array('host' => 'URL', 'user' => 'Username', 'password' => '*Password', 'root' => '&Root', 'secure' => '!Secure https://')) ); } @@ -124,7 +124,7 @@ class OC_Mount_Config { if ($isPersonal) { // Verify that the mount point applies for the current user // Prevent non-admin users from mounting local storage - if ($applicable != OCP\User::getUser() || $class == 'OC_Filestorage_Local') { + if ($applicable != OCP\User::getUser() || $class == '\OC\Files\Storage\Local') { return false; } $mountPoint = '/'.$applicable.'/files/'.ltrim($mountPoint, '/'); diff --git a/apps/files_external/lib/dropbox.php b/apps/files_external/lib/dropbox.php index bb86894e55..746364668f 100755 --- a/apps/files_external/lib/dropbox.php +++ b/apps/files_external/lib/dropbox.php @@ -22,7 +22,9 @@ require_once 'Dropbox/autoload.php'; -class OC_Filestorage_Dropbox extends OC_Filestorage_Common { +namespace OC\Files\Storage; + +class Dropbox extends \OC\Files\Storage\Common { private $dropbox; private $metaData = array(); @@ -31,11 +33,11 @@ class OC_Filestorage_Dropbox extends OC_Filestorage_Common { public function __construct($params) { if (isset($params['configured']) && $params['configured'] == 'true' && isset($params['app_key']) && isset($params['app_secret']) && isset($params['token']) && isset($params['token_secret'])) { - $oauth = new Dropbox_OAuth_Curl($params['app_key'], $params['app_secret']); + $oauth = new \Dropbox_OAuth_Curl($params['app_key'], $params['app_secret']); $oauth->setToken($params['token'], $params['token_secret']); - $this->dropbox = new Dropbox_API($oauth, 'dropbox'); + $this->dropbox = new \Dropbox_API($oauth, 'dropbox'); } else { - throw new Exception('Creating OC_Filestorage_Dropbox storage failed'); + throw new \Exception('Creating \OC\Files\Storage\Dropbox storage failed'); } } @@ -95,7 +97,7 @@ class OC_Filestorage_Dropbox extends OC_Filestorage_Common { foreach ($contents as $file) { $files[] = basename($file['path']); } - OC_FakeDirStream::$dirs['dropbox'.$path] = $files; + \OC_FakeDirStream::$dirs['dropbox'.$path] = $files; return opendir('fakedir://dropbox'.$path); } return false; @@ -177,7 +179,7 @@ class OC_Filestorage_Dropbox extends OC_Filestorage_Common { switch ($mode) { case 'r': case 'rb': - $tmpFile = OC_Helper::tmpFile(); + $tmpFile = \OC_Helper::tmpFile(); try { $data = $this->dropbox->getFile($path); file_put_contents($tmpFile, $data); @@ -203,8 +205,8 @@ class OC_Filestorage_Dropbox extends OC_Filestorage_Common { } else { $ext = ''; } - $tmpFile = OC_Helper::tmpFile($ext); - OC_CloseStreamWrapper::$callBacks[$tmpFile] = array($this, 'writeBack'); + $tmpFile = \OC_Helper::tmpFile($ext); + \OC_CloseStreamWrapper::$callBacks[$tmpFile] = array($this, 'writeBack'); if ($this->file_exists($path)) { $source = $this->fopen($path, 'r'); file_put_contents($tmpFile, $source); diff --git a/apps/files_external/lib/ftp.php b/apps/files_external/lib/ftp.php index 261141455c..140a21ffe1 100644 --- a/apps/files_external/lib/ftp.php +++ b/apps/files_external/lib/ftp.php @@ -6,7 +6,9 @@ * See the COPYING-README file. */ -class OC_FileStorage_FTP extends OC_FileStorage_StreamWrapper{ +namespace OC\Files\Storage; + +class FTP extends \OC\Files\Storage\StreamWrapper{ private $password; private $user; private $host; @@ -69,7 +71,7 @@ class OC_FileStorage_FTP extends OC_FileStorage_StreamWrapper{ $ext=''; } $tmpFile=OCP\Files::tmpFile($ext); - OC_CloseStreamWrapper::$callBacks[$tmpFile]=array($this,'writeBack'); + \OC_CloseStreamWrapper::$callBacks[$tmpFile]=array($this,'writeBack'); if($this->file_exists($path)) { $this->getFile($path,$tmpFile); } diff --git a/apps/files_external/lib/google.php b/apps/files_external/lib/google.php index 9b83dcee53..51c5d219aa 100644 --- a/apps/files_external/lib/google.php +++ b/apps/files_external/lib/google.php @@ -22,7 +22,9 @@ require_once 'Google/common.inc.php'; -class OC_Filestorage_Google extends OC_Filestorage_Common { +namespace OC\Files\Storage; + +class Google extends \OC\Files\Storage\Common { private $consumer; private $oauth_token; @@ -35,12 +37,12 @@ class OC_Filestorage_Google extends OC_Filestorage_Common { if (isset($params['configured']) && $params['configured'] == 'true' && isset($params['token']) && isset($params['token_secret'])) { $consumer_key = isset($params['consumer_key']) ? $params['consumer_key'] : 'anonymous'; $consumer_secret = isset($params['consumer_secret']) ? $params['consumer_secret'] : 'anonymous'; - $this->consumer = new OAuthConsumer($consumer_key, $consumer_secret); - $this->oauth_token = new OAuthToken($params['token'], $params['token_secret']); - $this->sig_method = new OAuthSignatureMethod_HMAC_SHA1(); + $this->consumer = new \OAuthConsumer($consumer_key, $consumer_secret); + $this->oauth_token = new \OAuthToken($params['token'], $params['token_secret']); + $this->sig_method = new \OAuthSignatureMethod_HMAC_SHA1(); $this->entries = array(); } else { - throw new Exception('Creating OC_Filestorage_Google storage failed'); + throw new \Exception('Creating \OC\Files\Storage\Google storage failed'); } } @@ -96,7 +98,7 @@ class OC_Filestorage_Google extends OC_Filestorage_Common { curl_setopt($curl, CURLOPT_HTTPHEADER, $headers); } if ($isDownload) { - $tmpFile = OC_Helper::tmpFile(); + $tmpFile = \OC_Helper::tmpFile(); $handle = fopen($tmpFile, 'w'); curl_setopt($curl, CURLOPT_FILE, $handle); } @@ -399,7 +401,7 @@ class OC_Filestorage_Google extends OC_Filestorage_Common { } else { $ext = ''; } - $tmpFile = OC_Helper::tmpFile($ext); + $tmpFile = \OC_Helper::tmpFile($ext); OC_CloseStreamWrapper::$callBacks[$tmpFile] = array($this, 'writeBack'); if ($this->file_exists($path)) { $source = $this->fopen($path, 'r'); @@ -438,7 +440,7 @@ class OC_Filestorage_Google extends OC_Filestorage_Common { } if (isset($uploadUri) && $handle = fopen($path, 'r')) { $uploadUri .= '?convert=false'; - $mimetype = OC_Helper::getMimeType($path); + $mimetype = \OC_Helper::getMimeType($path); $size = filesize($path); $headers = array('X-Upload-Content-Type: ' => $mimetype, 'X-Upload-Content-Length: ' => $size); $postData = ''; diff --git a/apps/files_external/lib/smb.php b/apps/files_external/lib/smb.php index e5ba7a1774..d4882ed024 100644 --- a/apps/files_external/lib/smb.php +++ b/apps/files_external/lib/smb.php @@ -8,7 +8,9 @@ require_once 'smb4php/smb.php'; -class OC_FileStorage_SMB extends OC_FileStorage_StreamWrapper{ +namespace OC\Files\Storage; + +class SMB extends \OC\Files\Storage\StreamWrapper{ private $password; private $user; private $host; diff --git a/apps/files_external/lib/streamwrapper.php b/apps/files_external/lib/streamwrapper.php index 7263ef2325..750bdbaf4d 100644 --- a/apps/files_external/lib/streamwrapper.php +++ b/apps/files_external/lib/streamwrapper.php @@ -6,8 +6,9 @@ * See the COPYING-README file. */ +namespace OC\Files\Storage; -abstract class OC_FileStorage_StreamWrapper extends OC_Filestorage_Common{ +abstract class StreamWrapper extends \OC\Files\Storage\Common{ abstract public function constructUrl($path); public function mkdir($path) { @@ -84,6 +85,4 @@ abstract class OC_FileStorage_StreamWrapper extends OC_Filestorage_Common{ return stat($this->constructUrl($path)); } - - } diff --git a/apps/files_external/lib/swift.php b/apps/files_external/lib/swift.php index c29d28b44c..c67cf59df6 100644 --- a/apps/files_external/lib/swift.php +++ b/apps/files_external/lib/swift.php @@ -8,7 +8,9 @@ require_once 'php-cloudfiles/cloudfiles.php'; -class OC_FileStorage_SWIFT extends OC_Filestorage_Common{ +namespace OC\Files\Storage; + +class SWIFT extends \OC\Files\Storage\Common{ private $host; private $root; private $user; @@ -272,10 +274,10 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{ if(!$this->root || $this->root[0]!='/') { $this->root='/'.$this->root; } - $this->auth = new CF_Authentication($this->user, $this->token, null, $this->host); + $this->auth = new \CF_Authentication($this->user, $this->token, null, $this->host); $this->auth->authenticate(); - $this->conn = new CF_Connection($this->auth); + $this->conn = new \CF_Connection($this->auth); if(!$this->containerExists($this->root)) { $this->rootContainer=$this->createContainer('/'); @@ -341,7 +343,7 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{ $subContainers=$this->getSubContainers($container); $files=array_merge($files,$subContainers); $id=$this->getContainerName($path); - OC_FakeDirStream::$dirs[$id]=$files; + \OC_FakeDirStream::$dirs[$id]=$files; return opendir('fakedir://'.$id); } @@ -426,7 +428,7 @@ class OC_FileStorage_SWIFT extends OC_Filestorage_Common{ case 'c': case 'c+': $tmpFile=$this->getTmpFile($path); - OC_CloseStreamWrapper::$callBacks[$tmpFile]=array($this,'writeBack'); + \OC_CloseStreamWrapper::$callBacks[$tmpFile]=array($this,'writeBack'); self::$tempFiles[$tmpFile]=$path; return fopen('close://'.$tmpFile,$mode); } diff --git a/apps/files_external/lib/webdav.php b/apps/files_external/lib/webdav.php index 3c18b227fa..70210b49ee 100644 --- a/apps/files_external/lib/webdav.php +++ b/apps/files_external/lib/webdav.php @@ -6,7 +6,9 @@ * See the COPYING-README file. */ -class OC_FileStorage_DAV extends OC_Filestorage_Common{ +namespace OC\Files\Storage; + +class DAV extends \OC\Files\Storage\Common{ private $password; private $user; private $host; @@ -42,7 +44,7 @@ class OC_FileStorage_DAV extends OC_Filestorage_Common{ 'password' => $this->password, ); - $this->client = new OC_Connector_Sabre_Client($settings); + $this->client = new \OC_Connector_Sabre_Client($settings); if($caview = \OCP\Files::getStorage('files_external')) { $certPath=\OCP\Config::getSystemValue('datadirectory').$caview->getAbsolutePath("").'rootcerts.crt'; @@ -78,12 +80,12 @@ class OC_FileStorage_DAV extends OC_Filestorage_Common{ try{ $response=$this->client->propfind($path, array(),1); $id=md5('webdav'.$this->root.$path); - OC_FakeDirStream::$dirs[$id]=array(); + \OC_FakeDirStream::$dirs[$id]=array(); $files=array_keys($response); array_shift($files);//the first entry is the current directory foreach($files as $file) { $file = urldecode(basename($file)); - OC_FakeDirStream::$dirs[$id][]=$file; + \OC_FakeDirStream::$dirs[$id][]=$file; } return opendir('fakedir://'.$id); }catch(Exception $e) { @@ -161,7 +163,7 @@ class OC_FileStorage_DAV extends OC_Filestorage_Common{ $ext=''; } $tmpFile=OCP\Files::tmpFile($ext); - OC_CloseStreamWrapper::$callBacks[$tmpFile]=array($this,'writeBack'); + \OC_CloseStreamWrapper::$callBacks[$tmpFile]=array($this,'writeBack'); if($this->file_exists($path)) { $this->getFile($path,$tmpFile); } diff --git a/apps/files_external/personal.php b/apps/files_external/personal.php index f0d76460f5..a678f345c8 100755 --- a/apps/files_external/personal.php +++ b/apps/files_external/personal.php @@ -24,7 +24,7 @@ OCP\Util::addScript('files_external', 'settings'); OCP\Util::addStyle('files_external', 'settings'); $backends = OC_Mount_Config::getBackends(); // Remove local storage -unset($backends['OC_Filestorage_Local']); +unset($backends['\OC\Files\Storage\Local']); $tmpl = new OCP\Template('files_external', 'settings'); $tmpl->assign('isAdminPage', false, false); $tmpl->assign('mounts', OC_Mount_Config::getPersonalMountPoints()); diff --git a/apps/files_external/tests/amazons3.php b/apps/files_external/tests/amazons3.php index b9b4cf65bd..ad1c453abb 100644 --- a/apps/files_external/tests/amazons3.php +++ b/apps/files_external/tests/amazons3.php @@ -34,7 +34,7 @@ if (!is_array($config) or !isset($config['amazons3']) or !$config['amazons3']['r $id = uniqid(); $this->config = include('apps/files_external/tests/config.php'); $this->config['amazons3']['bucket'] = $id; // Make sure we have a new empty bucket to work in - $this->instance = new OC_Filestorage_AmazonS3($this->config['amazons3']); + $this->instance = new \OC\Files\Storage\AmazonS3($this->config['amazons3']); } public function tearDown() { diff --git a/apps/files_external/tests/ftp.php b/apps/files_external/tests/ftp.php index 12f3ec3908..105f7b485b 100644 --- a/apps/files_external/tests/ftp.php +++ b/apps/files_external/tests/ftp.php @@ -18,7 +18,7 @@ if(!is_array($config) or !isset($config['ftp']) or !$config['ftp']['run']) { $id=uniqid(); $this->config=include('apps/files_external/tests/config.php'); $this->config['ftp']['root'].='/'.$id;//make sure we have an new empty folder to work in - $this->instance=new OC_Filestorage_FTP($this->config['ftp']); + $this->instance=new \OC\Files\Storage\FTP($this->config['ftp']); } public function tearDown() { diff --git a/apps/files_external/tests/google.php b/apps/files_external/tests/google.php index d2a6358ade..e96c2263e1 100644 --- a/apps/files_external/tests/google.php +++ b/apps/files_external/tests/google.php @@ -33,7 +33,7 @@ if(!is_array($config) or !isset($config['google']) or !$config['google']['run']) $id=uniqid(); $this->config=include('apps/files_external/tests/config.php'); $this->config['google']['root'].='/'.$id;//make sure we have an new empty folder to work in - $this->instance=new OC_Filestorage_Google($this->config['google']); + $this->instance=new \OC\Files\Storage\Google($this->config['google']); } public function tearDown() { diff --git a/apps/files_external/tests/smb.php b/apps/files_external/tests/smb.php index 7de4fddbb3..a0c5d83473 100644 --- a/apps/files_external/tests/smb.php +++ b/apps/files_external/tests/smb.php @@ -19,7 +19,7 @@ if(!is_array($config) or !isset($config['smb']) or !$config['smb']['run']) { $id=uniqid(); $this->config=include('apps/files_external/tests/config.php'); $this->config['smb']['root'].=$id;//make sure we have an new empty folder to work in - $this->instance=new OC_Filestorage_SMB($this->config['smb']); + $this->instance=new \OC\Files\Storage\SMB($this->config['smb']); } public function tearDown() { diff --git a/apps/files_external/tests/swift.php b/apps/files_external/tests/swift.php index a6f5eace1c..3e1485b0f3 100644 --- a/apps/files_external/tests/swift.php +++ b/apps/files_external/tests/swift.php @@ -18,7 +18,7 @@ if(!is_array($config) or !isset($config['swift']) or !$config['swift']['run']) { $id=uniqid(); $this->config=include('apps/files_external/tests/config.php'); $this->config['swift']['root'].='/'.$id;//make sure we have an new empty folder to work in - $this->instance=new OC_Filestorage_SWIFT($this->config['swift']); + $this->instance=new \OC\Files\Storage\SWIFT($this->config['swift']); } diff --git a/apps/files_external/tests/webdav.php b/apps/files_external/tests/webdav.php index 74d980aa3f..6b1121c9d0 100644 --- a/apps/files_external/tests/webdav.php +++ b/apps/files_external/tests/webdav.php @@ -18,7 +18,7 @@ if(!is_array($config) or !isset($config['webdav']) or !$config['webdav']['run']) $id=uniqid(); $this->config=include('apps/files_external/tests/config.php'); $this->config['webdav']['root'].='/'.$id;//make sure we have an new empty folder to work in - $this->instance=new OC_Filestorage_DAV($this->config['webdav']); + $this->instance=new \OC\Files\Storage\DAV($this->config['webdav']); } public function tearDown() { diff --git a/apps/files_sharing/appinfo/app.php b/apps/files_sharing/appinfo/app.php index 109f86b2e8..210c78ad17 100644 --- a/apps/files_sharing/appinfo/app.php +++ b/apps/files_sharing/appinfo/app.php @@ -2,8 +2,8 @@ OC::$CLASSPATH['OC_Share_Backend_File'] = "apps/files_sharing/lib/share/file.php"; OC::$CLASSPATH['OC_Share_Backend_Folder'] = 'apps/files_sharing/lib/share/folder.php'; -OC::$CLASSPATH['OC_Filestorage_Shared'] = "apps/files_sharing/lib/sharedstorage.php"; -OCP\Util::connectHook('OC_Filesystem', 'setup', 'OC_Filestorage_Shared', 'setup'); +OC::$CLASSPATH['OC\Files\Storage\Shared'] = "apps/files_sharing/lib/sharedstorage.php"; +OCP\Util::connectHook('OC_Filesystem', 'setup', '\OC\Files\Storage\Shared', 'setup'); OCP\Share::registerBackend('file', 'OC_Share_Backend_File'); OCP\Share::registerBackend('folder', 'OC_Share_Backend_Folder', 'file'); OCP\Util::addScript('files_sharing', 'share'); diff --git a/apps/files_sharing/lib/sharedstorage.php b/apps/files_sharing/lib/sharedstorage.php index 6dba76955a..f1539610b0 100644 --- a/apps/files_sharing/lib/sharedstorage.php +++ b/apps/files_sharing/lib/sharedstorage.php @@ -20,10 +20,12 @@ * */ +namespace OC\Files\Storage; + /** * Convert target path to source path and pass the function call to the correct storage provider */ -class OC_Filestorage_Shared extends OC_Filestorage_Common { +class Shared extends \OC\Files\Storage\Common { private $sharedFolder; private $files = array(); @@ -50,7 +52,7 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { if (isset($this->files[$folder])) { $file = $this->files[$folder]; } else { - $file = OCP\Share::getItemSharedWith('folder', $folder, OC_Share_Backend_File::FORMAT_SHARED_STORAGE); + $file = OCP\Share::getItemSharedWith('folder', $folder, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE); } if ($file) { $this->files[$target]['path'] = $file['path'].substr($target, strlen($folder)); @@ -58,7 +60,7 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { return $this->files[$target]; } } else { - $file = OCP\Share::getItemSharedWith('file', $target, OC_Share_Backend_File::FORMAT_SHARED_STORAGE); + $file = OCP\Share::getItemSharedWith('file', $target, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE); if ($file) { $this->files[$target] = $file; return $this->files[$target]; @@ -78,7 +80,7 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { $file = $this->getFile($target); if (isset($file['path'])) { $uid = substr($file['path'], 1, strpos($file['path'], '/', 1) - 1); - OC_Filesystem::mount('OC_Filestorage_Local', array('datadir' => OC_User::getHome($uid)), $uid); + \OC_Filesystem::mount('\OC\Files\Storage\Local', array('datadir' => \OC_User::getHome($uid)), $uid); return $file['path']; } return false; @@ -103,7 +105,7 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { * @return Source file path with mount point stripped out */ private function getInternalPath($path) { - $mountPoint = OC_Filesystem::getMountPoint($path); + $mountPoint = \OC_Filesystem::getMountPoint($path); $internalPath = substr($path, strlen($mountPoint)); return $internalPath; } @@ -112,7 +114,7 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { if ($path == '' || $path == '/' || !$this->isCreatable(dirname($path))) { return false; } else if ($source = $this->getSourcePath($path)) { - $storage = OC_Filesystem::getStorage($source); + $storage = \OC_Filesystem::getStorage($source); return $storage->mkdir($this->getInternalPath($source)); } return false; @@ -120,7 +122,7 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { public function rmdir($path) { if (($source = $this->getSourcePath($path)) && $this->isDeletable($path)) { - $storage = OC_Filesystem::getStorage($source); + $storage = \OC_Filesystem::getStorage($source); return $storage->rmdir($this->getInternalPath($source)); } return false; @@ -128,11 +130,11 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { public function opendir($path) { if ($path == '' || $path == '/') { - $files = OCP\Share::getItemsSharedWith('file', OC_Share_Backend_Folder::FORMAT_OPENDIR); - OC_FakeDirStream::$dirs['shared'] = $files; + $files = OCP\Share::getItemsSharedWith('file', \OC_Share_Backend_Folder::FORMAT_OPENDIR); + \OC_FakeDirStream::$dirs['shared'] = $files; return opendir('fakedir://shared'); } else if ($source = $this->getSourcePath($path)) { - $storage = OC_Filesystem::getStorage($source); + $storage = \OC_Filesystem::getStorage($source); return $storage->opendir($this->getInternalPath($source)); } return false; @@ -142,7 +144,7 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { if ($path == '' || $path == '/') { return true; } else if ($source = $this->getSourcePath($path)) { - $storage = OC_Filesystem::getStorage($source); + $storage = \OC_Filesystem::getStorage($source); return $storage->is_dir($this->getInternalPath($source)); } return false; @@ -150,7 +152,7 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { public function is_file($path) { if ($source = $this->getSourcePath($path)) { - $storage = OC_Filesystem::getStorage($source); + $storage = \OC_Filesystem::getStorage($source); return $storage->is_file($this->getInternalPath($source)); } return false; @@ -163,7 +165,7 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { $stat['ctime'] = $this->filectime($path); return $stat; } else if ($source = $this->getSourcePath($path)) { - $storage = OC_Filesystem::getStorage($source); + $storage = \OC_Filesystem::getStorage($source); return $storage->stat($this->getInternalPath($source)); } return false; @@ -173,7 +175,7 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { if ($path == '' || $path == '/') { return 'dir'; } else if ($source = $this->getSourcePath($path)) { - $storage = OC_Filesystem::getStorage($source); + $storage = \OC_Filesystem::getStorage($source); return $storage->filetype($this->getInternalPath($source)); } return false; @@ -183,7 +185,7 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { if ($path == '' || $path == '/' || $this->is_dir($path)) { return 0; } else if ($source = $this->getSourcePath($path)) { - $storage = OC_Filesystem::getStorage($source); + $storage = \OC_Filesystem::getStorage($source); return $storage->filesize($this->getInternalPath($source)); } return false; @@ -225,7 +227,7 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { if ($path == '' || $path == '/') { return true; } else if ($source = $this->getSourcePath($path)) { - $storage = OC_Filesystem::getStorage($source); + $storage = \OC_Filesystem::getStorage($source); return $storage->file_exists($this->getInternalPath($source)); } return false; @@ -246,7 +248,7 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { } else { $source = $this->getSourcePath($path); if ($source) { - $storage = OC_Filesystem::getStorage($source); + $storage = \OC_Filesystem::getStorage($source); return $storage->filectime($this->getInternalPath($source)); } } @@ -267,7 +269,7 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { } else { $source = $this->getSourcePath($path); if ($source) { - $storage = OC_Filesystem::getStorage($source); + $storage = \OC_Filesystem::getStorage($source); return $storage->filemtime($this->getInternalPath($source)); } } @@ -280,8 +282,8 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { 'target' => $this->sharedFolder.$path, 'source' => $source, ); - OCP\Util::emitHook('OC_Filestorage_Shared', 'file_get_contents', $info); - $storage = OC_Filesystem::getStorage($source); + OCP\Util::emitHook('\OC\Files\Storage\Shared', 'file_get_contents', $info); + $storage = \OC_Filesystem::getStorage($source); return $storage->file_get_contents($this->getInternalPath($source)); } } @@ -296,8 +298,8 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { 'target' => $this->sharedFolder.$path, 'source' => $source, ); - OCP\Util::emitHook('OC_Filestorage_Shared', 'file_put_contents', $info); - $storage = OC_Filesystem::getStorage($source); + OCP\Util::emitHook('\OC\Files\Storage\Shared', 'file_put_contents', $info); + $storage = \OC_Filesystem::getStorage($source); $result = $storage->file_put_contents($this->getInternalPath($source), $data); return $result; } @@ -308,7 +310,7 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { // Delete the file if DELETE permission is granted if ($source = $this->getSourcePath($path)) { if ($this->isDeletable($path)) { - $storage = OC_Filesystem::getStorage($source); + $storage = \OC_Filesystem::getStorage($source); return $storage->unlink($this->getInternalPath($source)); } else if (dirname($path) == '/' || dirname($path) == '.') { // Unshare the file from the user if in the root of the Shared folder @@ -332,7 +334,7 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { if (dirname($path1) == dirname($path2)) { // Rename the file if UPDATE permission is granted if ($this->isUpdatable($path1)) { - $storage = OC_Filesystem::getStorage($oldSource); + $storage = \OC_Filesystem::getStorage($oldSource); return $storage->rename($this->getInternalPath($oldSource), $this->getInternalPath($newSource)); } } else { @@ -347,7 +349,7 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { return $this->unlink($path1); } } else { - $storage = OC_Filesystem::getStorage($oldSource); + $storage = \OC_Filesystem::getStorage($oldSource); return $storage->rename($this->getInternalPath($oldSource), $this->getInternalPath($newSource)); } } @@ -361,7 +363,7 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { if ($this->isCreatable(dirname($path2))) { $source = $this->fopen($path1, 'r'); $target = $this->fopen($path2, 'w'); - return OC_Helper::streamCopy($source, $target); + return \OC_Helper::streamCopy($source, $target); } return false; } @@ -392,8 +394,8 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { 'source' => $source, 'mode' => $mode, ); - OCP\Util::emitHook('OC_Filestorage_Shared', 'fopen', $info); - $storage = OC_Filesystem::getStorage($source); + OCP\Util::emitHook('\OC\Files\Storage\Shared', 'fopen', $info); + $storage = \OC_Filesystem::getStorage($source); return $storage->fopen($this->getInternalPath($source), $mode); } return false; @@ -404,7 +406,7 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { return 'httpd/unix-directory'; } if ($source = $this->getSourcePath($path)) { - $storage = OC_Filesystem::getStorage($source); + $storage = \OC_Filesystem::getStorage($source); return $storage->getMimeType($this->getInternalPath($source)); } return false; @@ -413,21 +415,21 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { public function free_space($path) { $source = $this->getSourcePath($path); if ($source) { - $storage = OC_Filesystem::getStorage($source); + $storage = \OC_Filesystem::getStorage($source); return $storage->free_space($this->getInternalPath($source)); } } public function getLocalFile($path) { if ($source = $this->getSourcePath($path)) { - $storage = OC_Filesystem::getStorage($source); + $storage = \OC_Filesystem::getStorage($source); return $storage->getLocalFile($this->getInternalPath($source)); } return false; } public function touch($path, $mtime = null) { if ($source = $this->getSourcePath($path)) { - $storage = OC_Filesystem::getStorage($source); + $storage = \OC_Filesystem::getStorage($source); return $storage->touch($this->getInternalPath($source), $mtime); } return false; @@ -435,7 +437,7 @@ class OC_Filestorage_Shared extends OC_Filestorage_Common { public static function setup($options) { $user_dir = $options['user_dir']; - OC_Filesystem::mount('OC_Filestorage_Shared', array('sharedFolder' => '/Shared'), $user_dir.'/Shared/'); + \OC_Filesystem::mount('\OC\Files\Storage\Shared', array('sharedFolder' => '/Shared'), $user_dir.'/Shared/'); } /** diff --git a/lib/base.php b/lib/base.php index 1c6cc70b0e..3b2ab8d5eb 100644 --- a/lib/base.php +++ b/lib/base.php @@ -81,6 +81,9 @@ class OC{ elseif(strpos($className, 'OC_')===0) { $path = strtolower(str_replace('_', '/', substr($className, 3)) . '.php'); } + elseif(strpos($className, 'OC\\')===0) { + $path = strtolower(str_replace('\\', '/', substr($className, 3)) . '.php'); + } elseif(strpos($className, 'OCP\\')===0) { $path = 'public/'.strtolower(str_replace('\\', '/', substr($className, 3)) . '.php'); } diff --git a/lib/filestorage/common.php b/lib/files/storage/common.php similarity index 83% rename from lib/filestorage/common.php rename to lib/files/storage/common.php index 351714437c..0a60ff757e 100644 --- a/lib/filestorage/common.php +++ b/lib/files/storage/common.php @@ -1,38 +1,26 @@ . -*/ + * Copyright (c) 2012 Robin Appelman + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Files\Storage; /** * Storage backend class for providing common filesystem operation methods * which are not storage-backend specific. * - * OC_Filestorage_Common is never used directly; it is extended by all other + * \OC\Files\Storage\Common is never used directly; it is extended by all other * storage backends, where its methods may be overridden, and additional * (backend-specific) methods are defined. * - * Some OC_Filestorage_Common methods call functions which are first defined + * Some \OC\Files\Storage\Common methods call functions which are first defined * in classes which extend it, e.g. $this->stat() . */ -abstract class OC_Filestorage_Common extends OC_Filestorage { +abstract class Common extends \OC\Files\Storage\Storage { public function __construct($parameters) {} // abstract public function mkdir($path); @@ -104,7 +92,7 @@ abstract class OC_Filestorage_Common extends OC_Filestorage { public function copy($path1,$path2) { $source=$this->fopen($path1,'r'); $target=$this->fopen($path2,'w'); - $count=OC_Helper::streamCopy($source,$target); + $count=\OC_Helper::streamCopy($source,$target); return $count>0; } // abstract public function fopen($path,$mode); @@ -198,9 +186,9 @@ abstract class OC_Filestorage_Common extends OC_Filestorage { }else{ $extension=''; } - $tmpFile=OC_Helper::tmpFile($extension); + $tmpFile=\OC_Helper::tmpFile($extension); file_put_contents($tmpFile,$head); - $mime=OC_Helper::getMimeType($tmpFile); + $mime=\OC_Helper::getMimeType($tmpFile); unlink($tmpFile); return $mime; } @@ -227,13 +215,13 @@ abstract class OC_Filestorage_Common extends OC_Filestorage { }else{ $extension=''; } - $tmpFile=OC_Helper::tmpFile($extension); + $tmpFile=\OC_Helper::tmpFile($extension); $target=fopen($tmpFile,'w'); - OC_Helper::streamCopy($source,$target); + \OC_Helper::streamCopy($source,$target); return $tmpFile; } public function getLocalFolder($path) { - $baseDir=OC_Helper::tmpFolder(); + $baseDir=\OC_Helper::tmpFolder(); $this->addLocalFolder($path,$baseDir); return $baseDir; } diff --git a/lib/filestorage/commontest.php b/lib/files/storage/commontest.php similarity index 88% rename from lib/filestorage/commontest.php rename to lib/files/storage/commontest.php index b88bb232c3..773bfffcb4 100644 --- a/lib/filestorage/commontest.php +++ b/lib/files/storage/commontest.php @@ -22,18 +22,20 @@ */ /** - * test implementation for OC_FileStorage_Common with OC_FileStorage_Local + * test implementation for \OC\Files\Storage\Common with \OC\Files\Storage\Local */ -class OC_Filestorage_CommonTest extends OC_Filestorage_Common{ +namespace OC\Files\Storage; + +class CommonTest extends \OC\Files\Storage\Common{ /** * underlying local storage used for missing functions - * @var OC_FileStorage_Local + * @var \OC\Files\Storage\Local */ private $storage; public function __construct($params) { - $this->storage=new OC_Filestorage_Local($params); + $this->storage=new \OC\Files\Storage\Local($params); } public function mkdir($path) { diff --git a/lib/filestorage/local.php b/lib/files/storage/local.php similarity index 90% rename from lib/filestorage/local.php rename to lib/files/storage/local.php index e26d3d3ef9..e47441499a 100644 --- a/lib/filestorage/local.php +++ b/lib/files/storage/local.php @@ -1,8 +1,17 @@ + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Files\Storage; + /** * for local filestore, we only have to map the paths */ -class OC_Filestorage_Local extends OC_Filestorage_Common{ +class Local extends \OC\Files\Storage\Common{ protected $datadir; public function __construct($arguments) { $this->datadir=$arguments['datadir']; @@ -86,11 +95,11 @@ class OC_Filestorage_Local extends OC_Filestorage_Common{ } public function rename($path1,$path2) { if (!$this->isUpdatable($path1)) { - OC_Log::write('core','unable to rename, file is not writable : '.$path1,OC_Log::ERROR); + \OC_Log::write('core','unable to rename, file is not writable : '.$path1,\OC_Log::ERROR); return false; } if(! $this->file_exists($path1)) { - OC_Log::write('core','unable to rename, file does not exists : '.$path1,OC_Log::ERROR); + \OC_Log::write('core','unable to rename, file does not exists : '.$path1,\OC_Log::ERROR); return false; } @@ -129,7 +138,7 @@ class OC_Filestorage_Local extends OC_Filestorage_Common{ public function getMimeType($path) { if($this->isReadable($path)) { - return OC_Helper::getMimeType($this->datadir.$path); + return \OC_Helper::getMimeType($this->datadir.$path); }else{ return false; } diff --git a/lib/filestorage.php b/lib/files/storage/storage.php similarity index 71% rename from lib/filestorage.php rename to lib/files/storage/storage.php index 5bfd09253d..a2f6cb7ec3 100644 --- a/lib/filestorage.php +++ b/lib/files/storage/storage.php @@ -1,29 +1,17 @@ . -*/ + * Copyright (c) 2012 Robin Appelman + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Files\Storage; /** * Provde a common interface to all different storage options */ -abstract class OC_Filestorage{ +abstract class Storage{ abstract public function __construct($parameters); abstract public function mkdir($path); abstract public function rmdir($path); diff --git a/lib/files/storage/temporary.php b/lib/files/storage/temporary.php new file mode 100644 index 0000000000..0e69c9112a --- /dev/null +++ b/lib/files/storage/temporary.php @@ -0,0 +1,26 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Files\Storage; + +/** + * local storage backnd in temporary folder for testing purpores + */ +class Temporary extends OC\Files\Storage\Local{ + public function __construct($arguments) { + $this->datadir=\OC_Helper::tmpFolder(); + } + + public function cleanUp() { + \OC_Helper::rmdirr($this->datadir); + } + + public function __destruct() { + $this->cleanUp(); + } +} diff --git a/lib/filestorage/temporary.php b/lib/filestorage/temporary.php deleted file mode 100644 index 876ba045a6..0000000000 --- a/lib/filestorage/temporary.php +++ /dev/null @@ -1,17 +0,0 @@ -datadir=OC_Helper::tmpFolder(); - } - - public function cleanUp() { - OC_Helper::rmdirr($this->datadir); - } - - public function __destruct() { - $this->cleanUp(); - } -} diff --git a/lib/filesystem.php b/lib/filesystem.php index 92eb4fa477..9b022d8fef 100644 --- a/lib/filesystem.php +++ b/lib/filesystem.php @@ -48,7 +48,7 @@ class OC_Filesystem{ static private $mounts=array(); public static $loaded=false; /** - * @var OC_Filestorage $defaultInstance + * @var \OC\Files\Storage\Storage $defaultInstance */ static private $defaultInstance; @@ -181,7 +181,7 @@ class OC_Filesystem{ /** * get the storage object for a path * @param string path - * @return OC_Filestorage + * @return \OC\Files\Storage\Storage */ static public function getStorage($path) { $mountpoint=self::getMountPoint($path); @@ -276,7 +276,7 @@ class OC_Filesystem{ * create a new storage of a specific type * @param string type * @param array arguments - * @return OC_Filestorage + * @return \OC\Files\Storage\Storage */ static private function createStorage($class,$arguments) { if(class_exists($class)) { @@ -320,9 +320,9 @@ class OC_Filesystem{ } /** - * mount an OC_Filestorage in our virtual filesystem - * @param OC_Filestorage storage - * @param string mountpoint + * mount an \OC\Files\Storage\Storage in our virtual filesystem + * @param \OC\Files\Storage\Storage storage + * @param string mountpoint */ static public function mount($class,$arguments,$mountpoint) { if($mountpoint[0]!='/') { diff --git a/lib/filesystemview.php b/lib/filesystemview.php index 743f940301..36fef48551 100644 --- a/lib/filesystemview.php +++ b/lib/filesystemview.php @@ -35,7 +35,7 @@ * are triggered correctly. * * Filesystem functions are not called directly; they are passed to the correct - * OC_Filestorage object + * \OC\Files\Storage\Storage object */ class OC_FilesystemView { @@ -115,7 +115,7 @@ class OC_FilesystemView { /** * get the storage object for a path * @param string path - * @return OC_Filestorage + * @return \OC\Files\Storage\Storage */ public function getStorage($path) { if (!isset($this->storage_cache[$path])) { @@ -161,7 +161,7 @@ class OC_FilesystemView { /** * the following functions operate with arguments and return values identical * to those of their PHP built-in equivalents. Mostly they are merely wrappers - * for OC_Filestorage via basicOperation(). + * for \OC\Files\Storage\Storage via basicOperation(). */ public function mkdir($path) { return $this->basicOperation('mkdir', $path, array('create', 'write')); @@ -173,7 +173,7 @@ class OC_FilesystemView { return $this->basicOperation('opendir', $path, array('read')); } public function readdir($handle) { - $fsLocal= new OC_Filestorage_Local( array( 'datadir' => '/' ) ); + $fsLocal= new \OC\Files\Storage\Local( array( 'datadir' => '/' ) ); return $fsLocal->readdir( $handle ); } public function is_dir($path) { @@ -540,7 +540,7 @@ class OC_FilesystemView { } /** - * @brief abstraction layer for basic filesystem functions: wrapper for OC_Filestorage + * @brief abstraction layer for basic filesystem functions: wrapper for \OC\Files\Storage\Storage * @param string $operation * @param string #path * @param array (optional) hooks @@ -549,7 +549,7 @@ class OC_FilesystemView { * * This method takes requests for basic filesystem functions (e.g. reading & writing * files), processes hooks and proxies, sanitises paths, and finally passes them on to - * OC_Filestorage for delegation to a storage backend for execution + * \OC\Files\Storage\Storage for delegation to a storage backend for execution */ private function basicOperation($operation, $path, $hooks=array(), $extraParam=null) { $postFix=(substr($path,-1,1)==='/')?'/':''; diff --git a/lib/util.php b/lib/util.php index 5046550d6a..78976c9660 100755 --- a/lib/util.php +++ b/lib/util.php @@ -34,7 +34,7 @@ class OC_Util { $CONFIG_DATADIRECTORY = OC_Config::getValue( "datadirectory", OC::$SERVERROOT."/data" ); //first set up the local "root" storage if(!self::$rootMounted) { - OC_Filesystem::mount('OC_Filestorage_Local',array('datadir'=>$CONFIG_DATADIRECTORY),'/'); + OC_Filesystem::mount('\OC\Files\Storage\Local',array('datadir'=>$CONFIG_DATADIRECTORY),'/'); self::$rootMounted=true; } @@ -46,7 +46,7 @@ class OC_Util { mkdir( $userdirectory, 0755, true ); } //jail the user into his "home" directory - OC_Filesystem::mount('OC_Filestorage_Local', array('datadir' => $user_root), $user); + OC_Filesystem::mount('\OC\Files\Storage\Local', array('datadir' => $user_root), $user); OC_Filesystem::init($user_dir); $quotaProxy=new OC_FileProxy_Quota(); OC_FileProxy::register($quotaProxy); diff --git a/tests/lib/cache/file.php b/tests/lib/cache/file.php index 00be005d08..1dd1ff7fa8 100644 --- a/tests/lib/cache/file.php +++ b/tests/lib/cache/file.php @@ -39,7 +39,7 @@ class Test_Cache_File extends Test_Cache { //set up temporary storage OC_Filesystem::clearMounts(); - OC_Filesystem::mount('OC_Filestorage_Temporary',array(),'/'); + OC_Filesystem::mount('\OC\Files\Storage\Temporary',array(),'/'); OC_User::clearBackends(); OC_User::useBackend(new OC_User_Dummy()); diff --git a/tests/lib/filestorage.php b/tests/lib/filestorage.php index 3f7bb7b62d..bfb40bcd67 100644 --- a/tests/lib/filestorage.php +++ b/tests/lib/filestorage.php @@ -22,7 +22,7 @@ abstract class Test_FileStorage extends UnitTestCase { /** - * @var OC_Filestorage instance + * @var \OC\Files\Storage\Storage instance */ protected $instance; diff --git a/tests/lib/filestorage/commontest.php b/tests/lib/filestorage/commontest.php index 89e83589e5..0e52e91678 100644 --- a/tests/lib/filestorage/commontest.php +++ b/tests/lib/filestorage/commontest.php @@ -30,7 +30,7 @@ class Test_Filestorage_CommonTest extends Test_FileStorage { if(!file_exists($this->tmpDir)) { mkdir($this->tmpDir); } - $this->instance=new OC_Filestorage_CommonTest(array('datadir'=>$this->tmpDir)); + $this->instance=new \OC\Files\Storage\CommonTest(array('datadir'=>$this->tmpDir)); } public function tearDown() { diff --git a/tests/lib/filestorage/local.php b/tests/lib/filestorage/local.php index f68fb69b97..7bb27830ba 100644 --- a/tests/lib/filestorage/local.php +++ b/tests/lib/filestorage/local.php @@ -27,7 +27,7 @@ class Test_Filestorage_Local extends Test_FileStorage { private $tmpDir; public function setUp() { $this->tmpDir=OC_Helper::tmpFolder(); - $this->instance=new OC_Filestorage_Local(array('datadir'=>$this->tmpDir)); + $this->instance=new \OC\Files\Storage\Local(array('datadir'=>$this->tmpDir)); } public function tearDown() { diff --git a/tests/lib/filesystem.php b/tests/lib/filesystem.php index 1cfa35e305..5f5d393ed5 100644 --- a/tests/lib/filesystem.php +++ b/tests/lib/filesystem.php @@ -46,13 +46,13 @@ class Test_Filesystem extends UnitTestCase{ } public function testMount() { - OC_Filesystem::mount('OC_Filestorage_Local',self::getStorageData(),'/'); + OC_Filesystem::mount('\OC\Files\Storage\Local',self::getStorageData(),'/'); $this->assertEqual('/',OC_Filesystem::getMountPoint('/')); $this->assertEqual('/',OC_Filesystem::getMountPoint('/some/folder')); $this->assertEqual('',OC_Filesystem::getInternalPath('/')); $this->assertEqual('some/folder',OC_Filesystem::getInternalPath('/some/folder')); - OC_Filesystem::mount('OC_Filestorage_Local',self::getStorageData(),'/some'); + OC_Filesystem::mount('\OC\Files\Storage\Local',self::getStorageData(),'/some'); $this->assertEqual('/',OC_Filesystem::getMountPoint('/')); $this->assertEqual('/some/',OC_Filesystem::getMountPoint('/some/folder')); $this->assertEqual('/some/',OC_Filesystem::getMountPoint('/some/')); From e8d3a4768541980fcc391117e5a173875b2f75e0 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 12 Sep 2012 22:50:10 +0200 Subject: [PATCH 002/272] add getId to storage backends --- lib/files/storage/common.php | 1 + lib/files/storage/commontest.php | 3 +++ lib/files/storage/local.php | 3 +++ lib/files/storage/storage.php | 1 + 4 files changed, 8 insertions(+) diff --git a/lib/files/storage/common.php b/lib/files/storage/common.php index 0a60ff757e..1f8b620480 100644 --- a/lib/files/storage/common.php +++ b/lib/files/storage/common.php @@ -23,6 +23,7 @@ namespace OC\Files\Storage; abstract class Common extends \OC\Files\Storage\Storage { public function __construct($parameters) {} +// abstract public function getId(); // abstract public function mkdir($path); // abstract public function rmdir($path); // abstract public function opendir($path); diff --git a/lib/files/storage/commontest.php b/lib/files/storage/commontest.php index 773bfffcb4..2b42cfe115 100644 --- a/lib/files/storage/commontest.php +++ b/lib/files/storage/commontest.php @@ -38,6 +38,9 @@ class CommonTest extends \OC\Files\Storage\Common{ $this->storage=new \OC\Files\Storage\Local($params); } + public function getId(){ + return 'test::'.$this->storage->getId(); + } public function mkdir($path) { return $this->storage->mkdir($path); } diff --git a/lib/files/storage/local.php b/lib/files/storage/local.php index e47441499a..78e4877a00 100644 --- a/lib/files/storage/local.php +++ b/lib/files/storage/local.php @@ -19,6 +19,9 @@ class Local extends \OC\Files\Storage\Common{ $this->datadir.='/'; } } + public function getId(){ + return 'local::'.$this->datadir; + } public function mkdir($path) { return @mkdir($this->datadir.$path); } diff --git a/lib/files/storage/storage.php b/lib/files/storage/storage.php index a2f6cb7ec3..bd4cc4743b 100644 --- a/lib/files/storage/storage.php +++ b/lib/files/storage/storage.php @@ -13,6 +13,7 @@ namespace OC\Files\Storage; */ abstract class Storage{ abstract public function __construct($parameters); + abstract public function getId(); abstract public function mkdir($path); abstract public function rmdir($path); abstract public function opendir($path); From 68f65b657c398d44508d20b56ccf9ddbee8e3521 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 12 Sep 2012 22:53:10 +0200 Subject: [PATCH 003/272] add OC_Filesystem::resolvePath to get the storage backend and internal path for a file in one go --- lib/filesystem.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/lib/filesystem.php b/lib/filesystem.php index 9b022d8fef..ef8b3fef5b 100644 --- a/lib/filesystem.php +++ b/lib/filesystem.php @@ -194,6 +194,24 @@ class OC_Filesystem{ } } + /** + * resolve a path to a storage and internal path + * @param string $path + * @return array consisting of the storage and the internal path + */ + static public function resolvePath($path){ + $mountpoint=self::getMountPoint($path); + if($mountpoint) { + if(!isset(OC_Filesystem::$storages[$mountpoint])) { + $mount=OC_Filesystem::$mounts[$mountpoint]; + OC_Filesystem::$storages[$mountpoint]=OC_Filesystem::createStorage($mount['class'],$mount['arguments']); + } + $storage = OC_Filesystem::$storages[$mountpoint]; + $internalPath=substr($path,strlen($mountpoint)); + return array($storage, $internalPath); + } + } + static public function init($root) { if(self::$defaultInstance) { return false; From c94fe38d3900acff474c909aaa730d7ab51f914d Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 12 Sep 2012 23:25:57 +0200 Subject: [PATCH 004/272] add getPermissions to storage backends to get all permission flags in one go --- lib/files/storage/common.php | 19 +++++++++++++++++++ lib/files/storage/storage.php | 1 + 2 files changed, 20 insertions(+) diff --git a/lib/files/storage/common.php b/lib/files/storage/common.php index 1f8b620480..b2b4ea8eee 100644 --- a/lib/files/storage/common.php +++ b/lib/files/storage/common.php @@ -54,6 +54,25 @@ abstract class Common extends \OC\Files\Storage\Storage { public function isSharable($path) { return $this->isReadable($path); } + public function getPermissions($path){ + $permissions = 0; + if($this->isCreatable($path)){ + $permissions |= OCP\Share::PERMISSION_CREATE; + } + if($this->isReadable($path)){ + $permissions |= OCP\Share::PERMISSION_READ; + } + if($this->isUpdatable($path)){ + $permissions |= OCP\Share::PERMISSION_UPDATE; + } + if($this->isDeletable($path)){ + $permissions |= OCP\Share::PERMISSION_DELETE; + } + if($this->isSharable($path)){ + $permissions |= OCP\Share::PERMISSION_SHARE; + } + return $permissions; + } // abstract public function file_exists($path); public function filectime($path) { $stat = $this->stat($path); diff --git a/lib/files/storage/storage.php b/lib/files/storage/storage.php index bd4cc4743b..f149c61830 100644 --- a/lib/files/storage/storage.php +++ b/lib/files/storage/storage.php @@ -27,6 +27,7 @@ abstract class Storage{ abstract public function isUpdatable($path); abstract public function isDeletable($path); abstract public function isSharable($path); + abstract public function getPermissions($path); abstract public function file_exists($path); abstract public function filectime($path); abstract public function filemtime($path); From 954596c251cb3132878d8d5eafcc37c47b3f520e Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sun, 16 Sep 2012 16:52:32 +0200 Subject: [PATCH 005/272] rework filecache to work directly on storage backends wip --- lib/files/cache/cache.php | 189 ++++++++++++++++++++++++++++++++++++ lib/files/cache/scanner.php | 78 +++++++++++++++ lib/files/file.php | 61 ++++++++++++ 3 files changed, 328 insertions(+) create mode 100644 lib/files/cache/cache.php create mode 100644 lib/files/cache/scanner.php create mode 100644 lib/files/file.php diff --git a/lib/files/cache/cache.php b/lib/files/cache/cache.php new file mode 100644 index 0000000000..8fc575948d --- /dev/null +++ b/lib/files/cache/cache.php @@ -0,0 +1,189 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Files\Cache; + +class Cache { + /** + * @var array partial data for the cache + */ + private static $partial = array(); + + /** + * get the stored metadata of a file or folder + * + * @param \OC\Files\File or int $file + * @return array + */ + static public function get($file) { + if ($file instanceof \OC\Files\File) { + $where = 'WHERE `storage` = ? AND `path_hash` = ?'; + $params = array($file->getStorageId(), $file->getInternalPath()); + } else { //file id + $where = 'WHERE `fileid` = ?'; + $params = array($file); + } + $query = \OC_DB::prepare( + 'SELECT `id`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime` + FROM `*PREFIX*filecache` ' . $where); + $result=$query->execute($params); + return $result->fetchRow(); + } + + /** + * store meta data for a file or folder + * + * @param \OC\Files\File $file + * @param array $data + * + * @return int file id + */ + static public function put(\OC\Files\File $file, array $data) { + if ($id = self::getId($file) > -1) { + self::update($id, $data); + return $id; + } else { + $key = $file->getStorageId() . '::' . $file->getInternalPath(); + if (isset(self::$partial[$key])) { //add any saved partial data + $data = array_merge($data, self::$partial[$key]); + unset(self::$partial[$key]); + } + + $requiredFields = array('size', 'mtime', 'mimetype'); + foreach ($requiredFields as $field) { + if (!isset($data[$field])) { //data not complete save as partial and return + self::$partial[$key] = $data; + return -1; + } + } + + $data['path'] = $file->getInternalPath(); + $data['parent'] = self::getParentId($file); + $data['name'] = basename($file->getInternalPath()); + + list($queryParts, $params) = self::buildParts($data); + $queryParts[] = '`storage`'; + $params[] = $file->getStorageId(); + $valuesPlaceholder = array_fill(0, count($queryParts), '?'); + + $query = \OC_DB::prepare('INSERT INTO `*PREFIX*filecache`(' . implode(', ', $queryParts) . ' VALUES(' . implode(', ', $valuesPlaceholder) . ')'); + $query->execute($params); + + return \OC_DB::insertid('*PREFIX*filecache'); + } + } + + /** + * update the metadata in the cache + * + * @param int $id + * @param array $data + */ + static public function update($id, array $data) { + list($queryParts, $params) = self::buildParts($data); + $params[] = $id; + + $query = \OC_DB::prepare('UPDATE `*PREFIX*filecache` SET ' . implode(' = ?, ', $queryParts) . '=? WHERE fileid = ?'); + $query->execute($params); + } + + /** + * extract query parts and params array from data array + * + * @param array $data + * @return array + */ + private static function buildParts(array $data) { + $fields = array('path', 'parent', 'name', 'mimetype', 'size', 'mtime'); + + $params = array(); + $queryParts = array(); + foreach ($data as $name => $value) { + if (array_search($name, $fields) !== false) { + $params[] = $value; + $queryParts[] = '`' . $name . '`'; + if ($name === 'path') { + $params[] = md5($value); + $queryParts[] = '`path_hash`'; + } elseif ($name === 'mimetype') { + $params[] = substr($value, 0, strpos($value, '/')); + $queryParts[] = '`mimepart`'; + } + } + } + return array($queryParts, $params); + } + + /** + * get the file id for a file + * + * @param \OC\Files\File $file + * @return int + */ + static public function getId(\OC\Files\File $file) { + $storageId = $file->getStorageId(); + $pathHash = md5($file->getInternalPath()); + + $query = \OC_DB::prepare('SELECT id FROM `*PREFIX*filecache` WHERE `storage` = ? AND `path_hash` = ?'); + $result = $query->execute(array($storageId, $pathHash)); + + if ($row = $result->fetchRow()) { + return $row['id']; + } else { + return -1; + } + } + + /** + * get the id of the parent folder of a file + * + * @param \OC\Files\File $file + * return int + */ + static public function getParentId(\OC\Files\File $file) { + $path = $file->getInternalPath(); + if ($path === '/' or $path === '') { + return -1; + } else { + return self::getId(new \OC\Files\File($file->getStorage(), dirname($path))); + } + } + + /** + * check if a file is available in the cache + * + * @param \OC\Files\File $file + * @return bool + */ + static public function inCache(\OC\Files\File $file) { + return self::getId($file) != -1; + } + + /** + * remove a file or folder from the cache + * + * @param \OC\Files\File $file + */ + public function remove(\OC\Files\File $file) { + $storageId = $file->getStorageId(); + $pathHash = md5($file->getInternalPath()); + $query = \OC_DB::prepare('DELETE FROM `*PREFIX*filecache` WHERE `storage` = ? AND `path_hash` = ?'); + $query->execute(array($storageId, $pathHash)); + } + + /** + * remove all entries for files that are stored on $storage form the cache + * + * @param \OC\Files\Storage\Storage $storage + */ + public function removeStorage(\OC\Files\Storage\Storage $storage) { + $storageId = $storage->getId(); + $query = \OC_DB::prepare('DELETE FROM `*PREFIX*filecache` WHERE storage=?'); + $query->execute(array($storageId)); + } +} diff --git a/lib/files/cache/scanner.php b/lib/files/cache/scanner.php new file mode 100644 index 0000000000..8f2c5bda5c --- /dev/null +++ b/lib/files/cache/scanner.php @@ -0,0 +1,78 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Files\Cache; + +class Scanner { + const SCAN_RECURSIVE = true; + const SCAN_SHALLOW = false; + + /** + * get all the metadata of a file or folder + * * + * @param \OC\Files\File $file + * @return array with metadata of the file + */ + public static function getData(\OC\Files\File $file) { + $data = array(); + $storage = $file->getStorage(); + $path = $file->getInternalPath(); + if (!$storage->isReadable($path)) return null; //cant read, nothing we can do + clearstatcache(); + $data['mimetype'] = $storage->getMimeType($path); + $data['mtime'] = $storage->filemtime($path); + if ($data['mimetype'] == 'httpd/unix-directory') { + $data['size'] = -1; //unknown + $data['permissions'] = $storage->getPermissions($path . '/'); + } else { + $data['size'] = $storage->filesize($path); + $data['permissions'] = $storage->getPermissions($path); + } + return $data; + } + + /** + * scan a single file and store it in the cache + * + * @param \OC\Files\File $file + * @return array with metadata of the scanned file + */ + public static function scanFile(\OC\Files\File $file) { + $data = self::getData($file); + Cache::put($file, $data); + return $data; + } + + /** + * scan all the files in a folder and store them in the cache + * + * @param \OC\Files\File $folder + * @param SCAN_RECURSIVE/SCAN_SHALLOW $recursive + * @return int the size of the scanned folder or -1 if the size is unknown at this stage + */ + public static function scan(\OC\Files\File $folder, $recursive) { + $size = 0; + $storage = $folder->getStorage(); + $path = $folder->getInternalPath(); + if ($dh = $storage->opendir($path)) { + while ($file = readdir($dh)) { + if ($file !== '.' and $file !== '..') { + $child = new \OC\Files\File($storage, $path . '/' . $file); + $data = self::scanFile($child); + if ($recursive === self::SCAN_RECURSIVE and $data['mimetype'] === 'httpd/unix-directory') { + $data['size'] = self::scan($child, self::SCAN_RECURSIVE); + } + if ($data['size'] >= 0 and $size >= 0) { + $size += $data['size']; + } + } + } + } + return $size; + } +} diff --git a/lib/files/file.php b/lib/files/file.php new file mode 100644 index 0000000000..b6432e6345 --- /dev/null +++ b/lib/files/file.php @@ -0,0 +1,61 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Files; + +/** + * representation of the location a file or folder is stored + */ + +class File{ + /** + * @var Storage\Storage $storage + */ + private $storage; + /** + * @var string internalPath + */ + private $internalPath; + + public function __construct(Storage\Storage $storage, $internalPath){ + $this->storage = $storage; + $this->internalPath = $internalPath; + } + + public static function resolve($fullPath){ + $storage = null; + $internalPath = ''; + list($storage, $internalPath) = \OC_Filesystem::resolvePath($fullPath); + return new File($storage, $internalPath); + } + + /** + * get the internal path of the file inside the filestorage + * @return string + */ + public function getInternalPath(){ + return $this->internalPath; + } + + /** + * get the storage the file is stored in + * @return \OC\Files\Storage\Storage + */ + public function getStorage(){ + return $this->storage; + } + + /** + * get the id of the storage the file is stored in + * @return string + */ + public function getStorageId(){ + return $this->storage->getId(); + } + +} From 153cd802a9bbc0f5a5f67fe428af7ca78185011f Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sat, 22 Sep 2012 14:40:04 +0200 Subject: [PATCH 006/272] add partial file data to the result of Cache::get --- lib/files/cache/cache.php | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/files/cache/cache.php b/lib/files/cache/cache.php index 8fc575948d..9d90ab041f 100644 --- a/lib/files/cache/cache.php +++ b/lib/files/cache/cache.php @@ -31,7 +31,13 @@ class Cache { $query = \OC_DB::prepare( 'SELECT `id`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime` FROM `*PREFIX*filecache` ' . $where); - $result=$query->execute($params); + $result = $query->execute($params); + + //merge partial data + $key = $file->getStorageId() . '::' . $file->getInternalPath(); + if (isset(self::$partial[$key])) { + $result=array_merge($result, self::$partial[$key]); + } return $result->fetchRow(); } From 96b798a59f236f4251ced87ecac9da55e82d1685 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sat, 22 Sep 2012 14:51:15 +0200 Subject: [PATCH 007/272] move storage backend test cases to their own namespace --- apps/files_external/tests/amazons3.php | 8 +++--- apps/files_external/tests/ftp.php | 8 +++--- apps/files_external/tests/google.php | 6 +++-- apps/files_external/tests/smb.php | 8 +++--- apps/files_external/tests/swift.php | 6 +++-- apps/files_external/tests/webdav.php | 6 +++-- .../storage}/commontest.php | 8 +++--- .../{filestorage => files/storage}/local.php | 10 ++++---- .../storage/storage.php} | 25 ++++++++++--------- 9 files changed, 50 insertions(+), 35 deletions(-) rename tests/lib/{filestorage => files/storage}/commontest.php (91%) rename tests/lib/{filestorage => files/storage}/local.php (87%) rename tests/lib/{filestorage.php => files/storage/storage.php} (93%) diff --git a/apps/files_external/tests/amazons3.php b/apps/files_external/tests/amazons3.php index ad1c453abb..cf372aecbd 100644 --- a/apps/files_external/tests/amazons3.php +++ b/apps/files_external/tests/amazons3.php @@ -20,12 +20,14 @@ * License along with this library. If not, see . */ +namespace Test\Files\Storage; + $config = include('apps/files_external/tests/config.php'); if (!is_array($config) or !isset($config['amazons3']) or !$config['amazons3']['run']) { - abstract class Test_Filestorage_AmazonS3 extends Test_FileStorage{} + abstract class AmazonS3 extends Storage{} return; } else { - class Test_Filestorage_AmazonS3 extends Test_FileStorage { + class AmazonS3 extends Storage { private $config; private $id; @@ -38,7 +40,7 @@ if (!is_array($config) or !isset($config['amazons3']) or !$config['amazons3']['r } public function tearDown() { - $s3 = new AmazonS3(array('key' => $this->config['amazons3']['key'], 'secret' => $this->config['amazons3']['secret'])); + $s3 = new \AmazonS3(array('key' => $this->config['amazons3']['key'], 'secret' => $this->config['amazons3']['secret'])); if ($s3->delete_all_objects($this->id)) { $s3->delete_bucket($this->id); } diff --git a/apps/files_external/tests/ftp.php b/apps/files_external/tests/ftp.php index 105f7b485b..8a0821e25a 100644 --- a/apps/files_external/tests/ftp.php +++ b/apps/files_external/tests/ftp.php @@ -6,12 +6,14 @@ * See the COPYING-README file. */ +namespace Test\Files\Storage; + $config=include('apps/files_external/tests/config.php'); if(!is_array($config) or !isset($config['ftp']) or !$config['ftp']['run']) { - abstract class Test_Filestorage_FTP extends Test_FileStorage{} + abstract class FTP extends Storage{} return; }else{ - class Test_Filestorage_FTP extends Test_FileStorage { + class FTP extends Storage { private $config; public function setUp() { @@ -22,7 +24,7 @@ if(!is_array($config) or !isset($config['ftp']) or !$config['ftp']['run']) { } public function tearDown() { - OCP\Files::rmdirr($this->instance->constructUrl('')); + \OCP\Files::rmdirr($this->instance->constructUrl('')); } } } diff --git a/apps/files_external/tests/google.php b/apps/files_external/tests/google.php index e96c2263e1..fff6461c91 100644 --- a/apps/files_external/tests/google.php +++ b/apps/files_external/tests/google.php @@ -20,12 +20,14 @@ * License along with this library. If not, see . */ +namespace Test\Files\Storage; + $config=include('apps/files_external/tests/config.php'); if(!is_array($config) or !isset($config['google']) or !$config['google']['run']) { - abstract class Test_Filestorage_Google extends Test_FileStorage{} + abstract class Google extends Storage{} return; }else{ - class Test_Filestorage_Google extends Test_FileStorage { + class Google extends Storage { private $config; diff --git a/apps/files_external/tests/smb.php b/apps/files_external/tests/smb.php index a0c5d83473..87b110a241 100644 --- a/apps/files_external/tests/smb.php +++ b/apps/files_external/tests/smb.php @@ -8,11 +8,13 @@ $config=include('apps/files_external/tests/config.php'); +namespace Test\Files\Storage; + if(!is_array($config) or !isset($config['smb']) or !$config['smb']['run']) { - abstract class Test_Filestorage_SMB extends Test_FileStorage{} + abstract class SMB extends Storage{} return; }else{ - class Test_Filestorage_SMB extends Test_FileStorage { + class SMB extends Storage { private $config; public function setUp() { @@ -23,7 +25,7 @@ if(!is_array($config) or !isset($config['smb']) or !$config['smb']['run']) { } public function tearDown() { - OCP\Files::rmdirr($this->instance->constructUrl('')); + \OCP\Files::rmdirr($this->instance->constructUrl('')); } } } diff --git a/apps/files_external/tests/swift.php b/apps/files_external/tests/swift.php index 3e1485b0f3..98a97f99b5 100644 --- a/apps/files_external/tests/swift.php +++ b/apps/files_external/tests/swift.php @@ -6,12 +6,14 @@ * See the COPYING-README file. */ +namespace Test\Files\Storage; + $config=include('apps/files_external/tests/config.php'); if(!is_array($config) or !isset($config['swift']) or !$config['swift']['run']) { - abstract class Test_Filestorage_SWIFT extends Test_FileStorage{} + abstract class SWIFT extends Storage{} return; }else{ - class Test_Filestorage_SWIFT extends Test_FileStorage { + class SWIFT extends Storage { private $config; public function setUp() { diff --git a/apps/files_external/tests/webdav.php b/apps/files_external/tests/webdav.php index 6b1121c9d0..cddd0d513e 100644 --- a/apps/files_external/tests/webdav.php +++ b/apps/files_external/tests/webdav.php @@ -6,12 +6,14 @@ * See the COPYING-README file. */ +namespace Test\Files\Storage; + $config=include('apps/files_external/tests/config.php'); if(!is_array($config) or !isset($config['webdav']) or !$config['webdav']['run']) { - abstract class Test_Filestorage_DAV extends Test_FileStorage{} + abstract class DAV extends Storage{} return; }else{ - class Test_Filestorage_DAV extends Test_FileStorage { + class DAV extends Storage { private $config; public function setUp() { diff --git a/tests/lib/filestorage/commontest.php b/tests/lib/files/storage/commontest.php similarity index 91% rename from tests/lib/filestorage/commontest.php rename to tests/lib/files/storage/commontest.php index 0e52e91678..2e3808e2d9 100644 --- a/tests/lib/filestorage/commontest.php +++ b/tests/lib/files/storage/commontest.php @@ -20,7 +20,9 @@ * */ -class Test_Filestorage_CommonTest extends Test_FileStorage { +namespace Test\Files\Storage; + +class CommonTest extends Storage { /** * @var string tmpDir */ @@ -34,8 +36,8 @@ class Test_Filestorage_CommonTest extends Test_FileStorage { } public function tearDown() { - OC_Helper::rmdirr($this->tmpDir); + \OC_Helper::rmdirr($this->tmpDir); } } -?> \ No newline at end of file +?> diff --git a/tests/lib/filestorage/local.php b/tests/lib/files/storage/local.php similarity index 87% rename from tests/lib/filestorage/local.php rename to tests/lib/files/storage/local.php index 7bb27830ba..6672368e67 100644 --- a/tests/lib/filestorage/local.php +++ b/tests/lib/files/storage/local.php @@ -20,19 +20,19 @@ * */ -class Test_Filestorage_Local extends Test_FileStorage { +namespace Test\Files\Storage; + +class Local extends Storage { /** * @var string tmpDir */ private $tmpDir; public function setUp() { - $this->tmpDir=OC_Helper::tmpFolder(); + $this->tmpDir=\OC_Helper::tmpFolder(); $this->instance=new \OC\Files\Storage\Local(array('datadir'=>$this->tmpDir)); } public function tearDown() { - OC_Helper::rmdirr($this->tmpDir); + \OC_Helper::rmdirr($this->tmpDir); } } - -?> \ No newline at end of file diff --git a/tests/lib/filestorage.php b/tests/lib/files/storage/storage.php similarity index 93% rename from tests/lib/filestorage.php rename to tests/lib/files/storage/storage.php index bfb40bcd67..9c8581b259 100644 --- a/tests/lib/filestorage.php +++ b/tests/lib/files/storage/storage.php @@ -20,7 +20,9 @@ * */ -abstract class Test_FileStorage extends UnitTestCase { +namespace Test\Files\Storage; + +abstract class Storage extends \UnitTestCase { /** * @var \OC\Files\Storage\Storage instance */ @@ -83,7 +85,7 @@ abstract class Test_FileStorage extends UnitTestCase { * test the various uses of file_get_contents and file_put_contents */ public function testGetPutContents() { - $sourceFile=OC::$SERVERROOT.'/tests/data/lorem.txt'; + $sourceFile=\OC::$SERVERROOT.'/tests/data/lorem.txt'; $sourceText=file_get_contents($sourceFile); //fill a file with string data @@ -103,21 +105,21 @@ abstract class Test_FileStorage extends UnitTestCase { $this->assertEqual('httpd/unix-directory',$this->instance->getMimeType('/')); $this->assertEqual(false,$this->instance->getMimeType('/non/existing/file')); - $textFile=OC::$SERVERROOT.'/tests/data/lorem.txt'; + $textFile=\OC::$SERVERROOT.'/tests/data/lorem.txt'; $this->instance->file_put_contents('/lorem.txt',file_get_contents($textFile,'r')); $this->assertEqual('text/plain',$this->instance->getMimeType('/lorem.txt')); - $pngFile=OC::$SERVERROOT.'/tests/data/logo-wide.png'; + $pngFile=\OC::$SERVERROOT.'/tests/data/logo-wide.png'; $this->instance->file_put_contents('/logo-wide.png',file_get_contents($pngFile,'r')); $this->assertEqual('image/png',$this->instance->getMimeType('/logo-wide.png')); - $svgFile=OC::$SERVERROOT.'/tests/data/logo-wide.svg'; + $svgFile=\OC::$SERVERROOT.'/tests/data/logo-wide.svg'; $this->instance->file_put_contents('/logo-wide.svg',file_get_contents($svgFile,'r')); $this->assertEqual('image/svg+xml',$this->instance->getMimeType('/logo-wide.svg')); } public function testCopyAndMove() { - $textFile=OC::$SERVERROOT.'/tests/data/lorem.txt'; + $textFile=\OC::$SERVERROOT.'/tests/data/lorem.txt'; $this->instance->file_put_contents('/source.txt',file_get_contents($textFile)); $this->instance->copy('/source.txt','/target.txt'); $this->assertTrue($this->instance->file_exists('/target.txt')); @@ -130,7 +132,7 @@ abstract class Test_FileStorage extends UnitTestCase { } public function testLocal() { - $textFile=OC::$SERVERROOT.'/tests/data/lorem.txt'; + $textFile=\OC::$SERVERROOT.'/tests/data/lorem.txt'; $this->instance->file_put_contents('/lorem.txt',file_get_contents($textFile)); $localFile=$this->instance->getLocalFile('/lorem.txt'); $this->assertTrue(file_exists($localFile)); @@ -151,7 +153,7 @@ abstract class Test_FileStorage extends UnitTestCase { } public function testStat() { - $textFile=OC::$SERVERROOT.'/tests/data/lorem.txt'; + $textFile=\OC::$SERVERROOT.'/tests/data/lorem.txt'; $ctimeStart=time(); $this->instance->file_put_contents('/lorem.txt',file_get_contents($textFile)); $this->assertTrue($this->instance->isReadable('/lorem.txt')); @@ -198,7 +200,6 @@ abstract class Test_FileStorage extends UnitTestCase { fclose($fh); clearstatcache(); $mtimeEnd=time(); - $originalCTime=$cTime; $mTime=$this->instance->filemtime('/lorem.txt'); $this->assertTrue(($mtimeStart-1)<=$mTime); $this->assertTrue($mTime<=($mtimeEnd+1)); @@ -208,11 +209,11 @@ abstract class Test_FileStorage extends UnitTestCase { } public function testSearch() { - $textFile=OC::$SERVERROOT.'/tests/data/lorem.txt'; + $textFile=\OC::$SERVERROOT.'/tests/data/lorem.txt'; $this->instance->file_put_contents('/lorem.txt',file_get_contents($textFile,'r')); - $pngFile=OC::$SERVERROOT.'/tests/data/logo-wide.png'; + $pngFile=\OC::$SERVERROOT.'/tests/data/logo-wide.png'; $this->instance->file_put_contents('/logo-wide.png',file_get_contents($pngFile,'r')); - $svgFile=OC::$SERVERROOT.'/tests/data/logo-wide.svg'; + $svgFile=\OC::$SERVERROOT.'/tests/data/logo-wide.svg'; $this->instance->file_put_contents('/logo-wide.svg',file_get_contents($svgFile,'r')); $result=$this->instance->search('logo'); $this->assertEqual(2,count($result)); From b206d16b104f0474cfd41944b857a3981948b94d Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sat, 22 Sep 2012 14:51:34 +0200 Subject: [PATCH 008/272] add support for loading namespaced test cases --- lib/base.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/base.php b/lib/base.php index 7a09946b9d..38568e2749 100644 --- a/lib/base.php +++ b/lib/base.php @@ -95,6 +95,9 @@ class OC{ } elseif(strpos($className, 'Test_')===0) { $path = 'tests/lib/'.strtolower(str_replace('_', '/', substr($className, 5)) . '.php'); + } + elseif(strpos($className, 'Test\\')===0) { + $path = 'tests/lib/'.strtolower(str_replace('\\', '/', substr($className, 5)) . '.php'); }else{ return false; } From 46896be0d482f5dcec85ec32b232dd8a205f3e41 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sat, 22 Sep 2012 15:03:17 +0200 Subject: [PATCH 009/272] fix namespace error in temporary storage backend --- lib/files/storage/temporary.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/files/storage/temporary.php b/lib/files/storage/temporary.php index 0e69c9112a..ffc55e2750 100644 --- a/lib/files/storage/temporary.php +++ b/lib/files/storage/temporary.php @@ -11,7 +11,7 @@ namespace OC\Files\Storage; /** * local storage backnd in temporary folder for testing purpores */ -class Temporary extends OC\Files\Storage\Local{ +class Temporary extends Local{ public function __construct($arguments) { $this->datadir=\OC_Helper::tmpFolder(); } From 73eedd8fc8326616a356c8b8c40826d9dba92f31 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sat, 22 Sep 2012 15:43:10 +0200 Subject: [PATCH 010/272] some simple test cases for the new filecache --- tests/lib/files/cache/cache.php | 88 +++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 tests/lib/files/cache/cache.php diff --git a/tests/lib/files/cache/cache.php b/tests/lib/files/cache/cache.php new file mode 100644 index 0000000000..168489a721 --- /dev/null +++ b/tests/lib/files/cache/cache.php @@ -0,0 +1,88 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace Test\Files\Cache; + +use \OC\Files\Cache\Cache as FileCache; + +class Cache extends \UnitTestCase { + /** + * @var \OC\Files\Storage\Temporary $storage; + */ + private $storage; + + private function createPath($path) { + return new \OC\Files\File($this->storage, $path); + } + + public function testSimple() { + $file1 = $this->createPath('foo'); + $file2 = $this->createPath('foo/bar'); + $data1 = array('size' => 100, 'mtime' => 50, 'mimetype' => 'foo/folder'); + $data2 = array('size' => 1000, 'mtime' => 20, 'mimetype' => 'foo/file'); + + $this->assertFalse(FileCache::inCache($file1)); + $this->assertEqual(FileCache::get($file1), null); + + $id1 = FileCache::put($file1, $data1); + $this->assertTrue(FileCache::inCache($file1)); + $cacheData1 = FileCache::get($file1); + foreach ($data1 as $key => $value) { + $this->assertEqual($value, $cacheData1[$key]); + } + $this->assertEqual($cacheData1['fileid'], $id1); + $this->assertEqual($id1, FileCache::getId($file1)); + + $this->assertFalse(FileCache::inCache($file2)); + $id2 = FileCache::put($file2, $data2); + $this->assertTrue(FileCache::inCache($file2)); + $cacheData2 = FileCache::get($file2); + foreach ($data2 as $key => $value) { + $this->assertEqual($value, $cacheData2[$key]); + } + $this->assertEqual($cacheData1['fileid'], $cacheData2['parent']); + $this->assertEqual($cacheData2['fileid'], $id2); + $this->assertEqual($id2, FileCache::getId($file2)); + $this->assertEqual($id1, FileCache::getParentId($file2)); + + $newSize = 1050; + $newId2 = FileCache::put($file2, array('size' => $newSize)); + $cacheData2 = FileCache::get($file2); + $this->assertEqual($newId2, $id2); + $this->assertEqual($cacheData2['size'], $newSize); + $this->assertEqual($cacheData1, FileCache::get($file1)); + + FileCache::remove($file2); + $this->assertFalse(FileCache::inCache($file2)); + $this->assertEqual(FileCache::get($file2), null); + $this->assertTrue(FileCache::inCache($file1)); + + $this->assertEqual($cacheData1, FileCache::get($id1)); + } + + public function testPartial() { + $file1 = $this->createPath('foo'); + + FileCache::put($file1, array('size' => 10)); + $this->assertEqual(array('size' => 10), FileCache::get($file1)); + + FileCache::put($file1, array('mtime' => 15)); + $this->assertEqual(array('size' => 10, 'mtime' => 15), FileCache::get($file1)); + + FileCache::put($file1, array('size' => 12)); + $this->assertEqual(array('size' => 12, 'mtime' => 15), FileCache::get($file1)); + } + + public function tearDown() { + FileCache::removeStorage($this->storage); + } + + public function setUp() { + $this->storage = new \OC\Files\Storage\Temporary(array()); + } +} From 97b0eabc850f64949282b471288c93d7148c72ff Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sat, 22 Sep 2012 15:43:48 +0200 Subject: [PATCH 011/272] fix several problems in the new filecache in order to complete the tests --- lib/files/cache/cache.php | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/lib/files/cache/cache.php b/lib/files/cache/cache.php index 9d90ab041f..5d4cb9dd2b 100644 --- a/lib/files/cache/cache.php +++ b/lib/files/cache/cache.php @@ -23,22 +23,25 @@ class Cache { static public function get($file) { if ($file instanceof \OC\Files\File) { $where = 'WHERE `storage` = ? AND `path_hash` = ?'; - $params = array($file->getStorageId(), $file->getInternalPath()); + $params = array($file->getStorageId(), md5($file->getInternalPath())); } else { //file id $where = 'WHERE `fileid` = ?'; $params = array($file); } $query = \OC_DB::prepare( - 'SELECT `id`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime` + 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime` FROM `*PREFIX*filecache` ' . $where); $result = $query->execute($params); + $data = $result->fetchRow(); //merge partial data - $key = $file->getStorageId() . '::' . $file->getInternalPath(); - if (isset(self::$partial[$key])) { - $result=array_merge($result, self::$partial[$key]); + if (!$data and $file instanceof \OC\Files\File) { + $key = $file->getStorageId() . '::' . $file->getInternalPath(); + if (isset(self::$partial[$key])) { + $data = self::$partial[$key]; + } } - return $result->fetchRow(); + return $data; } /** @@ -50,13 +53,13 @@ class Cache { * @return int file id */ static public function put(\OC\Files\File $file, array $data) { - if ($id = self::getId($file) > -1) { + if (($id = self::getId($file)) > -1) { self::update($id, $data); return $id; } else { $key = $file->getStorageId() . '::' . $file->getInternalPath(); if (isset(self::$partial[$key])) { //add any saved partial data - $data = array_merge($data, self::$partial[$key]); + $data = array_merge(self::$partial[$key], $data); unset(self::$partial[$key]); } @@ -77,7 +80,7 @@ class Cache { $params[] = $file->getStorageId(); $valuesPlaceholder = array_fill(0, count($queryParts), '?'); - $query = \OC_DB::prepare('INSERT INTO `*PREFIX*filecache`(' . implode(', ', $queryParts) . ' VALUES(' . implode(', ', $valuesPlaceholder) . ')'); + $query = \OC_DB::prepare('INSERT INTO `*PREFIX*filecache`(' . implode(', ', $queryParts) . ') VALUES(' . implode(', ', $valuesPlaceholder) . ')'); $query->execute($params); return \OC_DB::insertid('*PREFIX*filecache'); @@ -135,11 +138,11 @@ class Cache { $storageId = $file->getStorageId(); $pathHash = md5($file->getInternalPath()); - $query = \OC_DB::prepare('SELECT id FROM `*PREFIX*filecache` WHERE `storage` = ? AND `path_hash` = ?'); + $query = \OC_DB::prepare('SELECT `fileid` FROM `*PREFIX*filecache` WHERE `storage` = ? AND `path_hash` = ?'); $result = $query->execute(array($storageId, $pathHash)); if ($row = $result->fetchRow()) { - return $row['id']; + return $row['fileid']; } else { return -1; } @@ -175,7 +178,7 @@ class Cache { * * @param \OC\Files\File $file */ - public function remove(\OC\Files\File $file) { + static public function remove(\OC\Files\File $file) { $storageId = $file->getStorageId(); $pathHash = md5($file->getInternalPath()); $query = \OC_DB::prepare('DELETE FROM `*PREFIX*filecache` WHERE `storage` = ? AND `path_hash` = ?'); @@ -187,7 +190,7 @@ class Cache { * * @param \OC\Files\Storage\Storage $storage */ - public function removeStorage(\OC\Files\Storage\Storage $storage) { + static public function removeStorage(\OC\Files\Storage\Storage $storage) { $storageId = $storage->getId(); $query = \OC_DB::prepare('DELETE FROM `*PREFIX*filecache` WHERE storage=?'); $query->execute(array($storageId)); From 6fafd5d4e9d23cd98738a3b00ce0053d6b6e3dff Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sat, 22 Sep 2012 15:48:39 +0200 Subject: [PATCH 012/272] this should be done elsewhere if needed --- lib/files/cache/cache.php | 2 +- lib/files/cache/scanner.php | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/files/cache/cache.php b/lib/files/cache/cache.php index 5d4cb9dd2b..0c1d6788ec 100644 --- a/lib/files/cache/cache.php +++ b/lib/files/cache/cache.php @@ -152,7 +152,7 @@ class Cache { * get the id of the parent folder of a file * * @param \OC\Files\File $file - * return int + * @return int */ static public function getParentId(\OC\Files\File $file) { $path = $file->getInternalPath(); diff --git a/lib/files/cache/scanner.php b/lib/files/cache/scanner.php index 8f2c5bda5c..e8e9c71c4e 100644 --- a/lib/files/cache/scanner.php +++ b/lib/files/cache/scanner.php @@ -23,7 +23,6 @@ class Scanner { $storage = $file->getStorage(); $path = $file->getInternalPath(); if (!$storage->isReadable($path)) return null; //cant read, nothing we can do - clearstatcache(); $data['mimetype'] = $storage->getMimeType($path); $data['mtime'] = $storage->filemtime($path); if ($data['mimetype'] == 'httpd/unix-directory') { From f360d7c736b08f48f1abcea1e0b93dc890178c94 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sun, 23 Sep 2012 01:51:00 +0200 Subject: [PATCH 013/272] add getId to shared storage backend --- apps/files_sharing/lib/sharedstorage.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/apps/files_sharing/lib/sharedstorage.php b/apps/files_sharing/lib/sharedstorage.php index f1539610b0..876e719956 100644 --- a/apps/files_sharing/lib/sharedstorage.php +++ b/apps/files_sharing/lib/sharedstorage.php @@ -91,7 +91,7 @@ class Shared extends \OC\Files\Storage\Common { * @param string Shared target file path * @return Returns CRUDS permissions granted or false if not found */ - private function getPermissions($target) { + public function getPermissions($target) { $file = $this->getFile($target); if (isset($file['permissions'])) { return $file['permissions']; @@ -449,4 +449,8 @@ class Shared extends \OC\Files\Storage\Common { //TODO return false; } + + public function getId(){ + return 'shared::' . $this->sharedFolder; + } } From dcf995fff3ecc2780d3187744397461d1a14c041 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sun, 23 Sep 2012 15:25:03 +0200 Subject: [PATCH 014/272] add Cache::getFolderContent --- lib/files/cache/cache.php | 19 +++++++++++++++++++ tests/lib/files/cache/cache.php | 23 +++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/lib/files/cache/cache.php b/lib/files/cache/cache.php index 0c1d6788ec..f8808ad0b4 100644 --- a/lib/files/cache/cache.php +++ b/lib/files/cache/cache.php @@ -44,6 +44,25 @@ class Cache { return $data; } + /** + * get the metadata of all files stored in $folder + * + * @param \OC\Files\File $folder + * @return array + */ + static public function getFolderContents($folder) { + $fileId = self::getId($folder); + if ($fileId > -1) { + $query = \OC_DB::prepare( + 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime` + FROM `*PREFIX*filecache` WHERE parent = ?'); + $result = $query->execute(array($fileId)); + return $result->fetchAll(); + } else { + return array(); + } + } + /** * store meta data for a file or folder * diff --git a/tests/lib/files/cache/cache.php b/tests/lib/files/cache/cache.php index 168489a721..9f35cd889a 100644 --- a/tests/lib/files/cache/cache.php +++ b/tests/lib/files/cache/cache.php @@ -78,6 +78,29 @@ class Cache extends \UnitTestCase { $this->assertEqual(array('size' => 12, 'mtime' => 15), FileCache::get($file1)); } + public function testFolder() { + $file1 = $this->createPath('folder'); + $file2 = $this->createPath('folder/bar'); + $file3 = $this->createPath('folder/foo'); + $data1 = array('size' => 100, 'mtime' => 50, 'mimetype' => 'foo/folder'); + $fileData = array(); + $fileData['bar'] = array('size' => 1000, 'mtime' => 20, 'mimetype' => 'foo/file'); + $fileData['foo'] = array('size' => 20, 'mtime' => 25, 'mimetype' => 'foo/file'); + + FileCache::put($file1, $data1); + FileCache::put($file2, $fileData['bar']); + FileCache::put($file3, $fileData['foo']); + + $content = FileCache::getFolderContents($file1); + $this->assertEqual(count($content), 2); + foreach ($content as $cachedData) { + $data = $fileData[$cachedData['name']]; + foreach ($data as $name => $value) { + $this->assertEqual($value, $cachedData[$name]); + } + } + } + public function tearDown() { FileCache::removeStorage($this->storage); } From 522d7df860fb47d3cc56e315850dda2797569ac5 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 26 Sep 2012 17:52:02 +0200 Subject: [PATCH 015/272] convert Cache to a non-static class that handles the cache for a single storage backend --- lib/files/cache/cache.php | 121 ++++++++++++++++++-------------- lib/files/cache/scanner.php | 1 + tests/lib/files/cache/cache.php | 82 +++++++++++----------- 3 files changed, 110 insertions(+), 94 deletions(-) diff --git a/lib/files/cache/cache.php b/lib/files/cache/cache.php index f8808ad0b4..167cc5d13f 100644 --- a/lib/files/cache/cache.php +++ b/lib/files/cache/cache.php @@ -9,21 +9,36 @@ namespace OC\Files\Cache; class Cache { + /** + * @var \OC\Files\Storage\Storage + */ + private $storage; + /** * @var array partial data for the cache */ - private static $partial = array(); + private $partial = array(); + + private $storageId; + + /** + * @param \OC\Files\Storage\Storage $storage + */ + public function __construct(\OC\Files\Storage\Storage $storage) { + $this->storage = $storage; + $this->storageId = $storage->getId(); + } /** * get the stored metadata of a file or folder * - * @param \OC\Files\File or int $file + * @param string/int $file * @return array */ - static public function get($file) { - if ($file instanceof \OC\Files\File) { + public function get($file) { + if (is_string($file)) { $where = 'WHERE `storage` = ? AND `path_hash` = ?'; - $params = array($file->getStorageId(), md5($file->getInternalPath())); + $params = array($this->storageId, md5($file)); } else { //file id $where = 'WHERE `fileid` = ?'; $params = array($file); @@ -35,23 +50,28 @@ class Cache { $data = $result->fetchRow(); //merge partial data - if (!$data and $file instanceof \OC\Files\File) { - $key = $file->getStorageId() . '::' . $file->getInternalPath(); - if (isset(self::$partial[$key])) { - $data = self::$partial[$key]; + if (!$data and is_string($file)) { + if (isset($this->partial[$file])) { + $data = $this->partial[$file]; } + } else { + //fix types + $data['fileid'] = (int)$data['fileid']; + $data['size'] = (int)$data['size']; + $data['mtime'] = (int)$data['mtime']; } + return $data; } /** * get the metadata of all files stored in $folder * - * @param \OC\Files\File $folder + * @param string $folder * @return array */ - static public function getFolderContents($folder) { - $fileId = self::getId($folder); + public function getFolderContents($folder) { + $fileId = $this->getId($folder); if ($fileId > -1) { $query = \OC_DB::prepare( 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime` @@ -66,43 +86,42 @@ class Cache { /** * store meta data for a file or folder * - * @param \OC\Files\File $file + * @param string $file * @param array $data * * @return int file id */ - static public function put(\OC\Files\File $file, array $data) { - if (($id = self::getId($file)) > -1) { - self::update($id, $data); + public function put($file, array $data) { + if (($id = $this->getId($file)) > -1) { + $this->update($id, $data); return $id; } else { - $key = $file->getStorageId() . '::' . $file->getInternalPath(); - if (isset(self::$partial[$key])) { //add any saved partial data - $data = array_merge(self::$partial[$key], $data); - unset(self::$partial[$key]); + if (isset($this->partial[$file])) { //add any saved partial data + $data = array_merge($this->partial[$file], $data); + unset($this->partial[$file]); } $requiredFields = array('size', 'mtime', 'mimetype'); foreach ($requiredFields as $field) { if (!isset($data[$field])) { //data not complete save as partial and return - self::$partial[$key] = $data; + $this->partial[$file] = $data; return -1; } } - $data['path'] = $file->getInternalPath(); - $data['parent'] = self::getParentId($file); - $data['name'] = basename($file->getInternalPath()); + $data['path'] = $file; + $data['parent'] = $this->getParentId($file); + $data['name'] = basename($file); - list($queryParts, $params) = self::buildParts($data); + list($queryParts, $params) = $this->buildParts($data); $queryParts[] = '`storage`'; - $params[] = $file->getStorageId(); + $params[] = $this->storageId; $valuesPlaceholder = array_fill(0, count($queryParts), '?'); $query = \OC_DB::prepare('INSERT INTO `*PREFIX*filecache`(' . implode(', ', $queryParts) . ') VALUES(' . implode(', ', $valuesPlaceholder) . ')'); $query->execute($params); - return \OC_DB::insertid('*PREFIX*filecache'); + return (int) \OC_DB::insertid('*PREFIX*filecache'); } } @@ -112,8 +131,8 @@ class Cache { * @param int $id * @param array $data */ - static public function update($id, array $data) { - list($queryParts, $params) = self::buildParts($data); + public function update($id, array $data) { + list($queryParts, $params) = $this->buildParts($data); $params[] = $id; $query = \OC_DB::prepare('UPDATE `*PREFIX*filecache` SET ' . implode(' = ?, ', $queryParts) . '=? WHERE fileid = ?'); @@ -126,7 +145,7 @@ class Cache { * @param array $data * @return array */ - private static function buildParts(array $data) { + static function buildParts(array $data) { $fields = array('path', 'parent', 'name', 'mimetype', 'size', 'mtime'); $params = array(); @@ -150,15 +169,14 @@ class Cache { /** * get the file id for a file * - * @param \OC\Files\File $file + * @param string $file * @return int */ - static public function getId(\OC\Files\File $file) { - $storageId = $file->getStorageId(); - $pathHash = md5($file->getInternalPath()); + public function getId($file) { + $pathHash = md5($file); $query = \OC_DB::prepare('SELECT `fileid` FROM `*PREFIX*filecache` WHERE `storage` = ? AND `path_hash` = ?'); - $result = $query->execute(array($storageId, $pathHash)); + $result = $query->execute(array($this->storageId, $pathHash)); if ($row = $result->fetchRow()) { return $row['fileid']; @@ -170,48 +188,43 @@ class Cache { /** * get the id of the parent folder of a file * - * @param \OC\Files\File $file + * @param string $file * @return int */ - static public function getParentId(\OC\Files\File $file) { - $path = $file->getInternalPath(); - if ($path === '/' or $path === '') { + public function getParentId($file) { + if ($file === '/' or $file === '') { return -1; } else { - return self::getId(new \OC\Files\File($file->getStorage(), dirname($path))); + return $this->getId(dirname($file)); } } /** * check if a file is available in the cache * - * @param \OC\Files\File $file + * @param string $file * @return bool */ - static public function inCache(\OC\Files\File $file) { - return self::getId($file) != -1; + public function inCache($file) { + return $this->getId($file) != -1; } /** * remove a file or folder from the cache * - * @param \OC\Files\File $file + * @param string $file */ - static public function remove(\OC\Files\File $file) { - $storageId = $file->getStorageId(); - $pathHash = md5($file->getInternalPath()); + public function remove($file) { + $pathHash = md5($file); $query = \OC_DB::prepare('DELETE FROM `*PREFIX*filecache` WHERE `storage` = ? AND `path_hash` = ?'); - $query->execute(array($storageId, $pathHash)); + $query->execute(array($this->storageId, $pathHash)); } /** - * remove all entries for files that are stored on $storage form the cache - * - * @param \OC\Files\Storage\Storage $storage + * remove all entries for files that are stored on the storage from the cache */ - static public function removeStorage(\OC\Files\Storage\Storage $storage) { - $storageId = $storage->getId(); + public function clear() { $query = \OC_DB::prepare('DELETE FROM `*PREFIX*filecache` WHERE storage=?'); - $query->execute(array($storageId)); + $query->execute(array($this->storageId)); } } diff --git a/lib/files/cache/scanner.php b/lib/files/cache/scanner.php index e8e9c71c4e..17fc0fb2e8 100644 --- a/lib/files/cache/scanner.php +++ b/lib/files/cache/scanner.php @@ -15,6 +15,7 @@ class Scanner { /** * get all the metadata of a file or folder * * + * * @param \OC\Files\File $file * @return array with metadata of the file */ diff --git a/tests/lib/files/cache/cache.php b/tests/lib/files/cache/cache.php index 9f35cd889a..24dd38fa73 100644 --- a/tests/lib/files/cache/cache.php +++ b/tests/lib/files/cache/cache.php @@ -16,82 +16,83 @@ class Cache extends \UnitTestCase { */ private $storage; - private function createPath($path) { - return new \OC\Files\File($this->storage, $path); - } + /** + * @var \OC\Files\Cache\Cache $cache + */ + private $cache; public function testSimple() { - $file1 = $this->createPath('foo'); - $file2 = $this->createPath('foo/bar'); + $file1 = 'foo'; + $file2 = 'foo/bar'; $data1 = array('size' => 100, 'mtime' => 50, 'mimetype' => 'foo/folder'); $data2 = array('size' => 1000, 'mtime' => 20, 'mimetype' => 'foo/file'); - $this->assertFalse(FileCache::inCache($file1)); - $this->assertEqual(FileCache::get($file1), null); + $this->assertFalse($this->cache->inCache($file1)); + $this->assertEqual($this->cache->get($file1), null); - $id1 = FileCache::put($file1, $data1); - $this->assertTrue(FileCache::inCache($file1)); - $cacheData1 = FileCache::get($file1); + $id1 = $this->cache->put($file1, $data1); + $this->assertTrue($this->cache->inCache($file1)); + $cacheData1 = $this->cache->get($file1); foreach ($data1 as $key => $value) { $this->assertEqual($value, $cacheData1[$key]); } $this->assertEqual($cacheData1['fileid'], $id1); - $this->assertEqual($id1, FileCache::getId($file1)); + $this->assertEqual($id1, $this->cache->getId($file1)); - $this->assertFalse(FileCache::inCache($file2)); - $id2 = FileCache::put($file2, $data2); - $this->assertTrue(FileCache::inCache($file2)); - $cacheData2 = FileCache::get($file2); + $this->assertFalse($this->cache->inCache($file2)); + $id2 = $this->cache->put($file2, $data2); + $this->assertTrue($this->cache->inCache($file2)); + $cacheData2 = $this->cache->get($file2); foreach ($data2 as $key => $value) { $this->assertEqual($value, $cacheData2[$key]); } $this->assertEqual($cacheData1['fileid'], $cacheData2['parent']); $this->assertEqual($cacheData2['fileid'], $id2); - $this->assertEqual($id2, FileCache::getId($file2)); - $this->assertEqual($id1, FileCache::getParentId($file2)); + $this->assertEqual($id2, $this->cache->getId($file2)); + $this->assertEqual($id1, $this->cache->getParentId($file2)); $newSize = 1050; - $newId2 = FileCache::put($file2, array('size' => $newSize)); - $cacheData2 = FileCache::get($file2); + $newId2 = $this->cache->put($file2, array('size' => $newSize)); + $cacheData2 = $this->cache->get($file2); $this->assertEqual($newId2, $id2); $this->assertEqual($cacheData2['size'], $newSize); - $this->assertEqual($cacheData1, FileCache::get($file1)); + $this->assertEqual($cacheData1, $this->cache->get($file1)); - FileCache::remove($file2); - $this->assertFalse(FileCache::inCache($file2)); - $this->assertEqual(FileCache::get($file2), null); - $this->assertTrue(FileCache::inCache($file1)); + $this->cache->remove($file2); + $this->assertFalse($this->cache->inCache($file2)); + $this->assertEqual($this->cache->get($file2), null); + $this->assertTrue($this->cache->inCache($file1)); - $this->assertEqual($cacheData1, FileCache::get($id1)); + $this->assertEqual($cacheData1, $this->cache->get($id1)); } public function testPartial() { - $file1 = $this->createPath('foo'); + $file1 = 'foo'; - FileCache::put($file1, array('size' => 10)); - $this->assertEqual(array('size' => 10), FileCache::get($file1)); + $this->cache->put($file1, array('size' => 10)); + $this->assertEqual(array('size' => 10), $this->cache->get($file1)); - FileCache::put($file1, array('mtime' => 15)); - $this->assertEqual(array('size' => 10, 'mtime' => 15), FileCache::get($file1)); + $this->cache->put($file1, array('mtime' => 15)); + $this->assertEqual(array('size' => 10, 'mtime' => 15), $this->cache->get($file1)); - FileCache::put($file1, array('size' => 12)); - $this->assertEqual(array('size' => 12, 'mtime' => 15), FileCache::get($file1)); + $this->cache->put($file1, array('size' => 12)); + $this->assertEqual(array('size' => 12, 'mtime' => 15), $this->cache->get($file1)); } public function testFolder() { - $file1 = $this->createPath('folder'); - $file2 = $this->createPath('folder/bar'); - $file3 = $this->createPath('folder/foo'); + $file1 = 'folder'; + $file2 = 'folder/bar'; + $file3 = 'folder/foo'; $data1 = array('size' => 100, 'mtime' => 50, 'mimetype' => 'foo/folder'); $fileData = array(); $fileData['bar'] = array('size' => 1000, 'mtime' => 20, 'mimetype' => 'foo/file'); $fileData['foo'] = array('size' => 20, 'mtime' => 25, 'mimetype' => 'foo/file'); - FileCache::put($file1, $data1); - FileCache::put($file2, $fileData['bar']); - FileCache::put($file3, $fileData['foo']); + $this->cache->put($file1, $data1); + $this->cache->put($file2, $fileData['bar']); + $this->cache->put($file3, $fileData['foo']); - $content = FileCache::getFolderContents($file1); + $content = $this->cache->getFolderContents($file1); $this->assertEqual(count($content), 2); foreach ($content as $cachedData) { $data = $fileData[$cachedData['name']]; @@ -102,10 +103,11 @@ class Cache extends \UnitTestCase { } public function tearDown() { - FileCache::removeStorage($this->storage); + $this->cache->clear(); } public function setUp() { $this->storage = new \OC\Files\Storage\Temporary(array()); + $this->cache = new \OC\Files\Cache\Cache($this->storage); } } From 92555eff711ff391f3af5ef6fb6f6414bfe012cf Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 2 Oct 2012 23:34:45 +0200 Subject: [PATCH 016/272] add encrypted column to the new filecache --- lib/files/cache/cache.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/files/cache/cache.php b/lib/files/cache/cache.php index 167cc5d13f..9d8275e5e0 100644 --- a/lib/files/cache/cache.php +++ b/lib/files/cache/cache.php @@ -44,7 +44,7 @@ class Cache { $params = array($file); } $query = \OC_DB::prepare( - 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime` + 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted` FROM `*PREFIX*filecache` ' . $where); $result = $query->execute($params); $data = $result->fetchRow(); @@ -59,6 +59,7 @@ class Cache { $data['fileid'] = (int)$data['fileid']; $data['size'] = (int)$data['size']; $data['mtime'] = (int)$data['mtime']; + $data['encrypted'] = (bool)$data['encrypted']; } return $data; @@ -74,7 +75,7 @@ class Cache { $fileId = $this->getId($folder); if ($fileId > -1) { $query = \OC_DB::prepare( - 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime` + 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted` FROM `*PREFIX*filecache` WHERE parent = ?'); $result = $query->execute(array($fileId)); return $result->fetchAll(); @@ -112,6 +113,7 @@ class Cache { $data['path'] = $file; $data['parent'] = $this->getParentId($file); $data['name'] = basename($file); + $data['encrypted'] = isset($data['encrypted']) ? ((int)$data['encrypted']) : 0; list($queryParts, $params) = $this->buildParts($data); $queryParts[] = '`storage`'; @@ -121,7 +123,7 @@ class Cache { $query = \OC_DB::prepare('INSERT INTO `*PREFIX*filecache`(' . implode(', ', $queryParts) . ') VALUES(' . implode(', ', $valuesPlaceholder) . ')'); $query->execute($params); - return (int) \OC_DB::insertid('*PREFIX*filecache'); + return (int)\OC_DB::insertid('*PREFIX*filecache'); } } @@ -146,7 +148,7 @@ class Cache { * @return array */ static function buildParts(array $data) { - $fields = array('path', 'parent', 'name', 'mimetype', 'size', 'mtime'); + $fields = array('path', 'parent', 'name', 'mimetype', 'size', 'mtime', 'encrypted'); $params = array(); $queryParts = array(); From 1ed89760be5e1c5cf5b3fa7d40d845750b2840ab Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 2 Oct 2012 23:35:51 +0200 Subject: [PATCH 017/272] dont need this anymore --- tests/lib/files/cache/cache.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/lib/files/cache/cache.php b/tests/lib/files/cache/cache.php index 24dd38fa73..48cdc1c8c8 100644 --- a/tests/lib/files/cache/cache.php +++ b/tests/lib/files/cache/cache.php @@ -8,8 +8,6 @@ namespace Test\Files\Cache; -use \OC\Files\Cache\Cache as FileCache; - class Cache extends \UnitTestCase { /** * @var \OC\Files\Storage\Temporary $storage; From 6134e554f2306c5309a793d4d3149a0f9b05ea5f Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 2 Oct 2012 23:46:35 +0200 Subject: [PATCH 018/272] new filecache table --- db_structure.xml | 114 +++++++++++++++++------------------------------ lib/util.php | 2 +- 2 files changed, 41 insertions(+), 75 deletions(-) diff --git a/db_structure.xml b/db_structure.xml index 99a30cb613..8bbc6e5cc6 100644 --- a/db_structure.xml +++ b/db_structure.xml @@ -53,19 +53,27 @@ - *dbprefix*fscache + *dbprefix*filecache - id - 1 + fileid integer 0 true + 1 4 + + storage + text + + true + 64 + + path text @@ -85,9 +93,9 @@ parent integer - 0 + true - 8 + 4 @@ -95,39 +103,7 @@ text true - 300 - - - - user - text - - true - 64 - - - - size - integer - 0 - true - 8 - - - - ctime - integer - 0 - true - 8 - - - - mtime - integer - 0 - true - 8 + 250 @@ -135,7 +111,7 @@ text true - 96 + 64 @@ -146,32 +122,37 @@ 32 + + size + integer + + true + 4 + + + + mtime + integer + + true + 4 + + encrypted integer 0 true - 1 - - - - versioned - integer - 0 - true - 1 - - - - writable - integer - 0 - true - 1 + 4 - fscache_path_hash_index + storage_path_hash + true + + storage + ascending + path_hash ascending @@ -179,23 +160,8 @@ - parent_index - - parent - ascending - - - - - name_index - - name - ascending - - - - - parent_name_index + parent_name_hash + true parent ascending diff --git a/lib/util.php b/lib/util.php index 31dd376b9c..264d61319d 100755 --- a/lib/util.php +++ b/lib/util.php @@ -81,7 +81,7 @@ class OC_Util { */ public static function getVersion() { // hint: We only can count up. So the internal version number of ownCloud 4.5 will be 4.9.0. This is not visible to the user - return array(4,86,11); + return array(4,86,12); } /** From 636c75ce061492751d0fb001e0bf7219df379f09 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 3 Oct 2012 11:23:33 +0200 Subject: [PATCH 019/272] better parent path bahaviour for the filecache --- lib/files/cache/cache.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/files/cache/cache.php b/lib/files/cache/cache.php index 9d8275e5e0..79673771e5 100644 --- a/lib/files/cache/cache.php +++ b/lib/files/cache/cache.php @@ -194,10 +194,14 @@ class Cache { * @return int */ public function getParentId($file) { - if ($file === '/' or $file === '') { + if ($file === '') { return -1; } else { - return $this->getId(dirname($file)); + $parent = dirname($file); + if ($parent === '.') { + $parent = ''; + } + return $this->getId($parent); } } From 96d7cd59978cc416bc0d9c5ac487af23692ef1d8 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 3 Oct 2012 11:24:10 +0200 Subject: [PATCH 020/272] correct namespace usage in common filestorage backend --- lib/files/storage/common.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/files/storage/common.php b/lib/files/storage/common.php index b2b4ea8eee..3886a04182 100644 --- a/lib/files/storage/common.php +++ b/lib/files/storage/common.php @@ -57,19 +57,19 @@ abstract class Common extends \OC\Files\Storage\Storage { public function getPermissions($path){ $permissions = 0; if($this->isCreatable($path)){ - $permissions |= OCP\Share::PERMISSION_CREATE; + $permissions |= \OCP\Share::PERMISSION_CREATE; } if($this->isReadable($path)){ - $permissions |= OCP\Share::PERMISSION_READ; + $permissions |= \OCP\Share::PERMISSION_READ; } if($this->isUpdatable($path)){ - $permissions |= OCP\Share::PERMISSION_UPDATE; + $permissions |= \OCP\Share::PERMISSION_UPDATE; } if($this->isDeletable($path)){ - $permissions |= OCP\Share::PERMISSION_DELETE; + $permissions |= \OCP\Share::PERMISSION_DELETE; } if($this->isSharable($path)){ - $permissions |= OCP\Share::PERMISSION_SHARE; + $permissions |= \OCP\Share::PERMISSION_SHARE; } return $permissions; } From e415e90c6d8a2e8bb129be7c63a7077c56ab3da8 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 3 Oct 2012 11:24:49 +0200 Subject: [PATCH 021/272] make filestorage scanner non-static and add a simple test case --- lib/files/cache/scanner.php | 64 ++++++++++++++++++++----------- tests/lib/files/cache/cache.php | 1 + tests/lib/files/cache/scanner.php | 47 +++++++++++++++++++++++ 3 files changed, 90 insertions(+), 22 deletions(-) create mode 100644 tests/lib/files/cache/scanner.php diff --git a/lib/files/cache/scanner.php b/lib/files/cache/scanner.php index 17fc0fb2e8..721d0d825e 100644 --- a/lib/files/cache/scanner.php +++ b/lib/files/cache/scanner.php @@ -9,29 +9,42 @@ namespace OC\Files\Cache; class Scanner { + /** + * @var \OC\Files\Storage\Storage $storage + */ + private $storage; + + /** + * @var \OC\Files\Cache\Cache $cache + */ + private $cache; + const SCAN_RECURSIVE = true; const SCAN_SHALLOW = false; + public function __construct(\OC\Files\Storage\Storage $storage) { + $this->storage = $storage; + $this->cache = new Cache($storage); + } + /** * get all the metadata of a file or folder * * * - * @param \OC\Files\File $file + * @param string $path * @return array with metadata of the file */ - public static function getData(\OC\Files\File $file) { + public function getData($path) { $data = array(); - $storage = $file->getStorage(); - $path = $file->getInternalPath(); - if (!$storage->isReadable($path)) return null; //cant read, nothing we can do - $data['mimetype'] = $storage->getMimeType($path); - $data['mtime'] = $storage->filemtime($path); + if (!$this->storage->isReadable($path)) return null; //cant read, nothing we can do + $data['mimetype'] = $this->storage->getMimeType($path); + $data['mtime'] = $this->storage->filemtime($path); if ($data['mimetype'] == 'httpd/unix-directory') { $data['size'] = -1; //unknown - $data['permissions'] = $storage->getPermissions($path . '/'); + $data['permissions'] = $this->storage->getPermissions($path . '/'); } else { - $data['size'] = $storage->filesize($path); - $data['permissions'] = $storage->getPermissions($path); + $data['size'] = $this->storage->filesize($path); + $data['permissions'] = $this->storage->getPermissions($path); } return $data; } @@ -39,33 +52,40 @@ class Scanner { /** * scan a single file and store it in the cache * - * @param \OC\Files\File $file + * @param string $file * @return array with metadata of the scanned file */ - public static function scanFile(\OC\Files\File $file) { - $data = self::getData($file); - Cache::put($file, $data); + public function scanFile($file) { + $data = $this->getData($file); + if ($file !== '') { + $parent = dirname($file); + if ($parent === '.') { + $parent = ''; + } + if (!$this->cache->inCache($parent)) { + $this->scanFile($parent); + } + } + $this->cache->put($file, $data); return $data; } /** * scan all the files in a folder and store them in the cache * - * @param \OC\Files\File $folder + * @param string $path * @param SCAN_RECURSIVE/SCAN_SHALLOW $recursive * @return int the size of the scanned folder or -1 if the size is unknown at this stage */ - public static function scan(\OC\Files\File $folder, $recursive) { + public function scan($path, $recursive) { $size = 0; - $storage = $folder->getStorage(); - $path = $folder->getInternalPath(); - if ($dh = $storage->opendir($path)) { + if ($dh = $this->storage->opendir($path)) { while ($file = readdir($dh)) { if ($file !== '.' and $file !== '..') { - $child = new \OC\Files\File($storage, $path . '/' . $file); - $data = self::scanFile($child); + $child = $path . '/' . $file; + $data = $this->scanFile($child); if ($recursive === self::SCAN_RECURSIVE and $data['mimetype'] === 'httpd/unix-directory') { - $data['size'] = self::scan($child, self::SCAN_RECURSIVE); + $data['size'] = $this->scan($child, self::SCAN_RECURSIVE); } if ($data['size'] >= 0 and $size >= 0) { $size += $data['size']; diff --git a/tests/lib/files/cache/cache.php b/tests/lib/files/cache/cache.php index 48cdc1c8c8..8cedadbf19 100644 --- a/tests/lib/files/cache/cache.php +++ b/tests/lib/files/cache/cache.php @@ -34,6 +34,7 @@ class Cache extends \UnitTestCase { foreach ($data1 as $key => $value) { $this->assertEqual($value, $cacheData1[$key]); } + $this->assertEqual($cacheData1['mimepart'], 'foo'); $this->assertEqual($cacheData1['fileid'], $id1); $this->assertEqual($id1, $this->cache->getId($file1)); diff --git a/tests/lib/files/cache/scanner.php b/tests/lib/files/cache/scanner.php new file mode 100644 index 0000000000..3d1c1546ab --- /dev/null +++ b/tests/lib/files/cache/scanner.php @@ -0,0 +1,47 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace Test\Files\Cache; + +class Scanner extends \UnitTestCase { + /** + * @var \OC\Files\Storage\Storage $storage + */ + private $storage; + + /** + * @var \OC\Files\Cache\Scanner $scanner + */ + private $scanner; + + /** + * @var \OC\Files\Cache\Cache $cache + */ + private $cache; + + function testFile() { + $data = "dummy file data\n"; + $this->storage->file_put_contents('foo.txt', $data); + $this->scanner->scanFile('foo.txt'); + + $this->assertEqual($this->cache->inCache('foo.txt'), true); + $cachedData = $this->cache->get('foo.txt'); + $this->assertEqual($cachedData['size'], strlen($data)); + $this->assertEqual($cachedData['mimetype'], 'text/plain'); + } + + function setUp() { + $this->storage = new \OC\Files\Storage\Temporary(array()); + $this->scanner = new \OC\Files\Cache\Scanner($this->storage); + $this->cache = new \OC\Files\Cache\Cache($this->storage); + } + + function tearDown() { +// $this->cache->clear(); + } +} From 3c8e5ea3581cbbfc5acd3c229080bc63548d2827 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 3 Oct 2012 11:31:13 +0200 Subject: [PATCH 022/272] this index cant be unique due to collisions of the root of different storages --- db_structure.xml | 1 - lib/util.php | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/db_structure.xml b/db_structure.xml index 8bbc6e5cc6..e0b9dc11e9 100644 --- a/db_structure.xml +++ b/db_structure.xml @@ -161,7 +161,6 @@ parent_name_hash - true parent ascending diff --git a/lib/util.php b/lib/util.php index 264d61319d..be9d1f4fb5 100755 --- a/lib/util.php +++ b/lib/util.php @@ -81,7 +81,7 @@ class OC_Util { */ public static function getVersion() { // hint: We only can count up. So the internal version number of ownCloud 4.5 will be 4.9.0. This is not visible to the user - return array(4,86,12); + return array(4,86,13); } /** From 85be00be6595a3af168f6df963ffdeac8c3c6d5e Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 3 Oct 2012 11:40:09 +0200 Subject: [PATCH 023/272] add some more test cases for the scanner --- lib/files/cache/scanner.php | 6 ++++-- tests/lib/files/cache/scanner.php | 23 ++++++++++++++++++++++- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/lib/files/cache/scanner.php b/lib/files/cache/scanner.php index 721d0d825e..7d449204e8 100644 --- a/lib/files/cache/scanner.php +++ b/lib/files/cache/scanner.php @@ -77,12 +77,14 @@ class Scanner { * @param SCAN_RECURSIVE/SCAN_SHALLOW $recursive * @return int the size of the scanned folder or -1 if the size is unknown at this stage */ - public function scan($path, $recursive) { + public function scan($path, $recursive = self::SCAN_RECURSIVE) { + $this->scanFile($path); + $size = 0; if ($dh = $this->storage->opendir($path)) { while ($file = readdir($dh)) { if ($file !== '.' and $file !== '..') { - $child = $path . '/' . $file; + $child = ($path !== '') ? $path . '/' . $file : $file; $data = $this->scanFile($child); if ($recursive === self::SCAN_RECURSIVE and $data['mimetype'] === 'httpd/unix-directory') { $data['size'] = $this->scan($child, self::SCAN_RECURSIVE); diff --git a/tests/lib/files/cache/scanner.php b/tests/lib/files/cache/scanner.php index 3d1c1546ab..7ee28dfc3f 100644 --- a/tests/lib/files/cache/scanner.php +++ b/tests/lib/files/cache/scanner.php @@ -33,6 +33,27 @@ class Scanner extends \UnitTestCase { $cachedData = $this->cache->get('foo.txt'); $this->assertEqual($cachedData['size'], strlen($data)); $this->assertEqual($cachedData['mimetype'], 'text/plain'); + $this->assertNotEqual($cachedData['parent'], -1); //parent folders should be scanned automatically + + $data = file_get_contents(\OC::$SERVERROOT . '/core/img/logo.png'); + $this->storage->file_put_contents('foo.png', $data); + $this->scanner->scanFile('foo.png'); + + $this->assertEqual($this->cache->inCache('foo.png'), true); + $cachedData = $this->cache->get('foo.png'); + $this->assertEqual($cachedData['size'], strlen($data)); + $this->assertEqual($cachedData['mimetype'], 'image/png'); + } + + function testFolder() { + $textData = "dummy file data\n"; + $imgData = file_get_contents(\OC::$SERVERROOT . '/core/img/logo.png'); + $this->storage->file_put_contents('foo.txt', $textData); + $this->storage->file_put_contents('foo.png', $imgData); + + $this->scanner->scan(''); + $this->assertEqual($this->cache->inCache('foo.txt'), true); + $this->assertEqual($this->cache->inCache('foo.png'), true); } function setUp() { @@ -42,6 +63,6 @@ class Scanner extends \UnitTestCase { } function tearDown() { -// $this->cache->clear(); + $this->cache->clear(); } } From b9b9fd9dbaae47b3a15aed9694c18b95404550b0 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 3 Oct 2012 13:07:19 +0200 Subject: [PATCH 024/272] more tests and fixes for the filesystem scanner --- lib/files/cache/scanner.php | 15 +++++++++--- tests/lib/files/cache/scanner.php | 39 ++++++++++++++++++++++++++++++- 2 files changed, 50 insertions(+), 4 deletions(-) diff --git a/lib/files/cache/scanner.php b/lib/files/cache/scanner.php index 7d449204e8..e8f54c34be 100644 --- a/lib/files/cache/scanner.php +++ b/lib/files/cache/scanner.php @@ -86,15 +86,24 @@ class Scanner { if ($file !== '.' and $file !== '..') { $child = ($path !== '') ? $path . '/' . $file : $file; $data = $this->scanFile($child); - if ($recursive === self::SCAN_RECURSIVE and $data['mimetype'] === 'httpd/unix-directory') { - $data['size'] = $this->scan($child, self::SCAN_RECURSIVE); + if ($data['mimetype'] === 'httpd/unix-directory') { + if ($recursive === self::SCAN_RECURSIVE) { + $data['size'] = $this->scan($child, self::SCAN_RECURSIVE); + } else { + $data['size'] = -1; + } } - if ($data['size'] >= 0 and $size >= 0) { + if ($data['size'] === -1) { + $size = -1; + } elseif ($size !== -1) { $size += $data['size']; } } } } + if ($size !== -1) { + $this->cache->put($path, array('size' => $size)); + } return $size; } } diff --git a/tests/lib/files/cache/scanner.php b/tests/lib/files/cache/scanner.php index 7ee28dfc3f..41286e69d3 100644 --- a/tests/lib/files/cache/scanner.php +++ b/tests/lib/files/cache/scanner.php @@ -45,15 +45,52 @@ class Scanner extends \UnitTestCase { $this->assertEqual($cachedData['mimetype'], 'image/png'); } - function testFolder() { + private function fillTestFolders() { $textData = "dummy file data\n"; $imgData = file_get_contents(\OC::$SERVERROOT . '/core/img/logo.png'); + $this->storage->mkdir('folder'); $this->storage->file_put_contents('foo.txt', $textData); $this->storage->file_put_contents('foo.png', $imgData); + $this->storage->file_put_contents('folder/bar.txt', $textData); + } + + function testFolder() { + $this->fillTestFolders(); $this->scanner->scan(''); + $this->assertEqual($this->cache->inCache(''), true); $this->assertEqual($this->cache->inCache('foo.txt'), true); $this->assertEqual($this->cache->inCache('foo.png'), true); + $this->assertEqual($this->cache->inCache('folder'), true); + $this->assertEqual($this->cache->inCache('folder/bar.txt'), true); + + $cachedDataText = $this->cache->get('foo.txt'); + $cachedDataText2 = $this->cache->get('foo.txt'); + $cachedDataImage = $this->cache->get('foo.png'); + $cachedDataFolder = $this->cache->get(''); + $cachedDataFolder2 = $this->cache->get('folder'); + + $this->assertEqual($cachedDataImage['parent'], $cachedDataText['parent']); + $this->assertEqual($cachedDataFolder['fileid'], $cachedDataImage['parent']); + $this->assertEqual($cachedDataFolder['size'], $cachedDataImage['size'] + $cachedDataText['size'] + $cachedDataText2['size']); + $this->assertEqual($cachedDataFolder2['size'], $cachedDataText2['size']); + } + + function testShallow() { + $this->fillTestFolders(); + + $this->scanner->scan('', \OC\Files\Cache\Scanner::SCAN_SHALLOW); + $this->assertEqual($this->cache->inCache(''), true); + $this->assertEqual($this->cache->inCache('foo.txt'), true); + $this->assertEqual($this->cache->inCache('foo.png'), true); + $this->assertEqual($this->cache->inCache('folder'), true); + $this->assertEqual($this->cache->inCache('folder/bar.txt'), false); + + $cachedDataFolder = $this->cache->get(''); + $cachedDataFolder2 = $this->cache->get('folder'); + + $this->assertEqual($cachedDataFolder['size'], -1); + $this->assertEqual($cachedDataFolder2['size'], -1); } function setUp() { From c815fd5a5c8cba52b24327584e689498cb03fb3e Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 8 Oct 2012 14:31:13 +0200 Subject: [PATCH 025/272] fix files_external test cases --- apps/files_external/tests/dropbox.php | 8 +++++--- apps/files_external/tests/smb.php | 3 ++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/apps/files_external/tests/dropbox.php b/apps/files_external/tests/dropbox.php index 64eb2556c9..e77e0767c9 100644 --- a/apps/files_external/tests/dropbox.php +++ b/apps/files_external/tests/dropbox.php @@ -6,19 +6,21 @@ * See the COPYING-README file. */ +namespace Test\Files\Storage; + $config=include('files_external/tests/config.php'); if(!is_array($config) or !isset($config['dropbox']) or !$config['dropbox']['run']) { - abstract class Test_Filestorage_Dropbox extends Test_FileStorage{} + abstract class Dropbox extends Storage{} return; }else{ - class Test_Filestorage_Dropbox extends Test_FileStorage { + class Dropbox extends Storage { private $config; public function setUp() { $id=uniqid(); $this->config=include('files_external/tests/config.php'); $this->config['dropbox']['root'].='/'.$id;//make sure we have an new empty folder to work in - $this->instance=new OC_Filestorage_Dropbox($this->config['dropbox']); + $this->instance=new \OC\Files\Storage\Dropbox($this->config['dropbox']); } public function tearDown() { diff --git a/apps/files_external/tests/smb.php b/apps/files_external/tests/smb.php index 87b110a241..24728dc237 100644 --- a/apps/files_external/tests/smb.php +++ b/apps/files_external/tests/smb.php @@ -6,9 +6,10 @@ * See the COPYING-README file. */ +namespace Test\Files\Storage; + $config=include('apps/files_external/tests/config.php'); -namespace Test\Files\Storage; if(!is_array($config) or !isset($config['smb']) or !$config['smb']['run']) { abstract class SMB extends Storage{} From 13515effc99bfad7e776d00476e897fa396a8c6c Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 8 Oct 2012 14:58:21 +0200 Subject: [PATCH 026/272] add Cache::getStatus --- lib/files/cache/cache.php | 29 +++++++++++++++++++++++++++++ tests/lib/files/cache/cache.php | 10 ++++++++++ 2 files changed, 39 insertions(+) diff --git a/lib/files/cache/cache.php b/lib/files/cache/cache.php index 79673771e5..5ef49246ea 100644 --- a/lib/files/cache/cache.php +++ b/lib/files/cache/cache.php @@ -9,6 +9,11 @@ namespace OC\Files\Cache; class Cache { + const NOT_FOUND = 0; + const PARTIAL = 1; //only partial data available, file not cached in the database + const SHALLOW = 2; //folder in cache, but not all child files are completely scanned + const COMPLETE = 3; + /** * @var \OC\Files\Storage\Storage */ @@ -233,4 +238,28 @@ class Cache { $query = \OC_DB::prepare('DELETE FROM `*PREFIX*filecache` WHERE storage=?'); $query->execute(array($this->storageId)); } + + /** + * @param string $file + * + * @return int, Cache::NOT_FOUND, Cache::PARTIAL, Cache::SHALLOW or Cache::COMPLETE + */ + public function getStatus($file) { + $pathHash = md5($file); + $query = \OC_DB::prepare('SELECT * FROM `*PREFIX*filecache` WHERE `storage` = ? AND `path_hash` = ?'); + $result = $query->execute(array($this->storageId, $pathHash)); + if ($row = $result->fetchRow()) { + if ((int)$row['size'] === -1) { + return self::SHALLOW; + } else { + return self::COMPLETE; + } + } else { + if (isset($this->partial[$file])) { + return self::PARTIAL; + } else { + return self::NOT_FOUND; + } + } + } } diff --git a/tests/lib/files/cache/cache.php b/tests/lib/files/cache/cache.php index 8cedadbf19..177cf1c045 100644 --- a/tests/lib/files/cache/cache.php +++ b/tests/lib/files/cache/cache.php @@ -101,6 +101,16 @@ class Cache extends \UnitTestCase { } } + function testStatus() { + $this->assertEquals(\OC\Files\Cache\Cache::NOT_FOUND, $this->cache->getStatus('foo')); + $this->cache->put('foo', array('size' => -1)); + $this->assertEquals(\OC\Files\Cache\Cache::PARTIAL, $this->cache->getStatus('foo')); + $this->cache->put('foo', array('size' => -1, 'mtime' => 20, 'mimetype' => 'foo/file')); + $this->assertEquals(\OC\Files\Cache\Cache::SHALLOW, $this->cache->getStatus('foo')); + $this->cache->put('foo', array('size' => 10)); + $this->assertEquals(\OC\Files\Cache\Cache::COMPLETE, $this->cache->getStatus('foo')); + } + public function tearDown() { $this->cache->clear(); } From d717a5e55ca54c6ddbaf2f35f0f19b707dcfe1c8 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 10 Oct 2012 11:51:14 +0200 Subject: [PATCH 027/272] remove old filecache --- lib/filecache.php | 511 --------------------------------------- lib/filecache/cached.php | 81 ------- lib/filecache/update.php | 217 ----------------- lib/filesystem.php | 12 +- lib/util.php | 7 - 5 files changed, 2 insertions(+), 826 deletions(-) delete mode 100644 lib/filecache.php delete mode 100644 lib/filecache/cached.php delete mode 100644 lib/filecache/update.php diff --git a/lib/filecache.php b/lib/filecache.php deleted file mode 100644 index 8fcb6fd940..0000000000 --- a/lib/filecache.php +++ /dev/null @@ -1,511 +0,0 @@ -. -* -*/ - -/** - * provide caching for filesystem info in the database - * - * not used by OC_Filesystem for reading filesystem info, - * instread apps should use OC_FileCache::get where possible - * - * It will try to keep the data up to date but changes from outside ownCloud can invalidate the cache - */ -class OC_FileCache{ - /** - * get the filesystem info from the cache - * @param string path - * @param string root (optional) - * @return array - * - * returns an associative array with the following keys: - * - size - * - mtime - * - ctime - * - mimetype - * - encrypted - * - versioned - */ - public static function get($path,$root=false) { - if(OC_FileCache_Update::hasUpdated($path,$root)) { - if($root===false) {//filesystem hooks are only valid for the default root - OC_Hook::emit('OC_Filesystem','post_write',array('path'=>$path)); - }else{ - OC_FileCache_Update::update($path,$root); - } - } - return OC_FileCache_Cached::get($path,$root); - } - - /** - * put filesystem info in the cache - * @param string $path - * @param array data - * @param string root (optional) - * - * $data is an assiciative array in the same format as returned by get - */ - public static function put($path,$data,$root=false) { - if($root===false) { - $root=OC_Filesystem::getRoot(); - } - $fullpath=$root.$path; - $parent=self::getParentId($fullpath); - $id=self::getId($fullpath,''); - if(isset(OC_FileCache_Cached::$savedData[$fullpath])) { - $data=array_merge(OC_FileCache_Cached::$savedData[$fullpath],$data); - unset(OC_FileCache_Cached::$savedData[$fullpath]); - } - if($id!=-1) { - self::update($id,$data); - return; - } - - // add parent directory to the file cache if it does not exist yet. - if ($parent == -1 && $fullpath != $root) { - $parentDir = dirname($path); - self::scanFile($parentDir); - $parent = self::getParentId($fullpath); - } - - if(!isset($data['size']) or !isset($data['mtime'])) {//save incomplete data for the next time we write it - OC_FileCache_Cached::$savedData[$fullpath]=$data; - return; - } - if(!isset($data['encrypted'])) { - $data['encrypted']=false; - } - if(!isset($data['versioned'])) { - $data['versioned']=false; - } - $mimePart=dirname($data['mimetype']); - $data['size']=(int)$data['size']; - $data['ctime']=(int)$data['mtime']; - $data['writable']=(int)$data['writable']; - $data['encrypted']=(int)$data['encrypted']; - $data['versioned']=(int)$data['versioned']; - $user=OC_User::getUser(); - $query=OC_DB::prepare('INSERT INTO `*PREFIX*fscache`(`parent`, `name`, `path`, `path_hash`, `size`, `mtime`, `ctime`, `mimetype`, `mimepart`,`user`,`writable`,`encrypted`,`versioned`) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?)'); - $result=$query->execute(array($parent,basename($fullpath),$fullpath,md5($fullpath),$data['size'],$data['mtime'],$data['ctime'],$data['mimetype'],$mimePart,$user,$data['writable'],$data['encrypted'],$data['versioned'])); - if(OC_DB::isError($result)) { - OC_Log::write('files','error while writing file('.$fullpath.') to cache',OC_Log::ERROR); - } - - if($cache=OC_Cache::getUserCache(true)) { - $cache->remove('fileid/'.$fullpath);//ensure we don't have -1 cached - } - } - - /** - * update filesystem info of a file - * @param int $id - * @param array $data - */ - private static function update($id,$data) { - $arguments=array(); - $queryParts=array(); - foreach(array('size','mtime','ctime','mimetype','encrypted','versioned','writable') as $attribute) { - if(isset($data[$attribute])) { - //Convert to int it args are false - if($data[$attribute] === false) { - $arguments[] = 0; - }else{ - $arguments[] = $data[$attribute]; - } - $queryParts[]='`'.$attribute.'`=?'; - } - } - if(isset($data['mimetype'])) { - $arguments[]=dirname($data['mimetype']); - $queryParts[]='`mimepart`=?'; - } - $arguments[]=$id; - - $sql = 'UPDATE `*PREFIX*fscache` SET '.implode(' , ',$queryParts).' WHERE `id`=?'; - $query=OC_DB::prepare($sql); - $result=$query->execute($arguments); - if(OC_DB::isError($result)) { - OC_Log::write('files','error while updating file('.$id.') in cache',OC_Log::ERROR); - } - } - - /** - * register a file move in the cache - * @param string oldPath - * @param string newPath - * @param string root (optional) - */ - public static function move($oldPath,$newPath,$root=false) { - if($root===false) { - $root=OC_Filesystem::getRoot(); - } - // If replacing an existing file, delete the file - if (self::inCache($newPath, $root)) { - self::delete($newPath, $root); - } - $oldPath=$root.$oldPath; - $newPath=$root.$newPath; - $newParent=self::getParentId($newPath); - $query=OC_DB::prepare('UPDATE `*PREFIX*fscache` SET `parent`=? ,`name`=?, `path`=?, `path_hash`=? WHERE `path_hash`=?'); - $query->execute(array($newParent,basename($newPath),$newPath,md5($newPath),md5($oldPath))); - - if(($cache=OC_Cache::getUserCache(true)) && $cache->hasKey('fileid/'.$oldPath)) { - $cache->set('fileid/'.$newPath,$cache->get('fileid/'.$oldPath)); - $cache->remove('fileid/'.$oldPath); - } - - $query=OC_DB::prepare('SELECT `path` FROM `*PREFIX*fscache` WHERE `path` LIKE ?'); - $oldLength=strlen($oldPath); - $updateQuery=OC_DB::prepare('UPDATE `*PREFIX*fscache` SET `path`=?, `path_hash`=? WHERE `path_hash`=?'); - while($row= $query->execute(array($oldPath.'/%'))->fetchRow()) { - $old=$row['path']; - $new=$newPath.substr($old,$oldLength); - $updateQuery->execute(array($new,md5($new),md5($old))); - - if(($cache=OC_Cache::getUserCache(true)) && $cache->hasKey('fileid/'.$old)) { - $cache->set('fileid/'.$new,$cache->get('fileid/'.$old)); - $cache->remove('fileid/'.$old); - } - } - } - - /** - * delete info from the cache - * @param string path - * @param string root (optional) - */ - public static function delete($path,$root=false) { - if($root===false) { - $root=OC_Filesystem::getRoot(); - } - $query=OC_DB::prepare('DELETE FROM `*PREFIX*fscache` WHERE `path_hash`=?'); - $query->execute(array(md5($root.$path))); - - //delete everything inside the folder - $query=OC_DB::prepare('DELETE FROM `*PREFIX*fscache` WHERE `path` LIKE ?'); - $query->execute(array($root.$path.'/%')); - - OC_Cache::remove('fileid/'.$root.$path); - } - - /** - * return array of filenames matching the querty - * @param string $query - * @param boolean $returnData - * @param string root (optional) - * @return array of filepaths - */ - public static function search($search,$returnData=false,$root=false) { - if($root===false) { - $root=OC_Filesystem::getRoot(); - } - $rootLen=strlen($root); - if(!$returnData) { - $query=OC_DB::prepare('SELECT `path` FROM `*PREFIX*fscache` WHERE `name` LIKE ? AND `user`=?'); - }else{ - $query=OC_DB::prepare('SELECT * FROM `*PREFIX*fscache` WHERE `name` LIKE ? AND `user`=?'); - } - $result=$query->execute(array("%$search%",OC_User::getUser())); - $names=array(); - while($row=$result->fetchRow()) { - if(!$returnData) { - $names[]=substr($row['path'],$rootLen); - }else{ - $row['path']=substr($row['path'],$rootLen); - $names[]=$row; - } - } - return $names; - } - - /** - * get all files and folders in a folder - * @param string path - * @param string root (optional) - * @return array - * - * returns an array of assiciative arrays with the following keys: - * - name - * - size - * - mtime - * - ctime - * - mimetype - * - encrypted - * - versioned - */ - public static function getFolderContent($path,$root=false,$mimetype_filter='') { - if(OC_FileCache_Update::hasUpdated($path,$root,true)) { - OC_FileCache_Update::updateFolder($path,$root); - } - return OC_FileCache_Cached::getFolderContent($path,$root,$mimetype_filter); - } - - /** - * check if a file or folder is in the cache - * @param string $path - * @param string root (optional) - * @return bool - */ - public static function inCache($path,$root=false) { - return self::getId($path,$root)!=-1; - } - - /** - * get the file id as used in the cache - * @param string path - * @param string root (optional) - * @return int - */ - public static function getId($path,$root=false) { - if($root===false) { - $root=OC_Filesystem::getRoot(); - } - - $fullPath=$root.$path; - if(($cache=OC_Cache::getUserCache(true)) && $cache->hasKey('fileid/'.$fullPath)) { - return $cache->get('fileid/'.$fullPath); - } - - $query=OC_DB::prepare('SELECT `id` FROM `*PREFIX*fscache` WHERE `path_hash`=?'); - $result=$query->execute(array(md5($fullPath))); - if(OC_DB::isError($result)) { - OC_Log::write('files','error while getting file id of '.$path,OC_Log::ERROR); - return -1; - } - - $result=$result->fetchRow(); - if(is_array($result)) { - $id=$result['id']; - }else{ - $id=-1; - } - if($cache=OC_Cache::getUserCache(true)) { - $cache->set('fileid/'.$fullPath,$id); - } - - return $id; - } - - /** - * get the file path from the id, relative to the home folder of the user - * @param int id - * @param string user (optional) - * @return string - */ - public static function getPath($id,$user='') { - if(!$user) { - $user=OC_User::getUser(); - } - $query=OC_DB::prepare('SELECT `path` FROM `*PREFIX*fscache` WHERE `id`=? AND `user`=?'); - $result=$query->execute(array($id,$user)); - $row=$result->fetchRow(); - $path=$row['path']; - $root='/'.$user.'/files'; - if(substr($path,0,strlen($root))!=$root) { - return false; - } - return substr($path,strlen($root)); - } - - /** - * get the file id of the parent folder, taking into account '/' has no parent - * @param string $path - * @return int - */ - private static function getParentId($path) { - if($path=='/') { - return -1; - }else{ - return self::getId(dirname($path),''); - } - } - - /** - * adjust the size of the parent folders - * @param string $path - * @param int $sizeDiff - * @param string root (optinal) - */ - public static function increaseSize($path,$sizeDiff, $root=false) { - if($sizeDiff==0) return; - $id=self::getId($path,$root); - while($id!=-1) {//walk up the filetree increasing the size of all parent folders - $query=OC_DB::prepare('UPDATE `*PREFIX*fscache` SET `size`=`size`+? WHERE `id`=?'); - $query->execute(array($sizeDiff,$id)); - $id=self::getParentId($path); - $path=dirname($path); - } - } - - /** - * recursively scan the filesystem and fill the cache - * @param string $path - * @param OC_EventSource $enventSource (optional) - * @param int count (optional) - * @param string root (optional) - */ - public static function scan($path,$eventSource=false,&$count=0,$root=false) { - if($eventSource) { - $eventSource->send('scanning',array('file'=>$path,'count'=>$count)); - } - $lastSend=$count; - // NOTE: Ugly hack to prevent shared files from going into the cache (the source already exists somewhere in the cache) - if (substr($path, 0, 7) == '/Shared') { - return; - } - if($root===false) { - $view=OC_Filesystem::getView(); - }else{ - $view=new OC_FilesystemView($root); - } - self::scanFile($path,$root); - $dh=$view->opendir($path.'/'); - $totalSize=0; - if($dh) { - while (($filename = readdir($dh)) !== false) { - if($filename != '.' and $filename != '..') { - $file=$path.'/'.$filename; - if($view->is_dir($file.'/')) { - self::scan($file,$eventSource,$count,$root); - }else{ - $totalSize+=self::scanFile($file,$root); - $count++; - if($count>$lastSend+25 and $eventSource) { - $lastSend=$count; - $eventSource->send('scanning',array('file'=>$path,'count'=>$count)); - } - } - } - } - } - - OC_FileCache_Update::cleanFolder($path,$root); - self::increaseSize($path,$totalSize,$root); - } - - /** - * scan a single file - * @param string path - * @param string root (optional) - * @return int size of the scanned file - */ - public static function scanFile($path,$root=false) { - // NOTE: Ugly hack to prevent shared files from going into the cache (the source already exists somewhere in the cache) - if (substr($path, 0, 7) == '/Shared') { - return; - } - if($root===false) { - $view=OC_Filesystem::getView(); - }else{ - $view=new OC_FilesystemView($root); - } - if(!$view->is_readable($path)) return; //cant read, nothing we can do - clearstatcache(); - $mimetype=$view->getMimeType($path); - $stat=$view->stat($path); - if($mimetype=='httpd/unix-directory') { - $stat['size'] = 0; - $writable=$view->is_writable($path.'/'); - }else{ - $writable=$view->is_writable($path); - } - $stat['mimetype']=$mimetype; - $stat['writable']=$writable; - if($path=='/') { - $path=''; - } - self::put($path,$stat,$root); - return $stat['size']; - } - - /** - * find files by mimetype - * @param string $part1 - * @param string $part2 (optional) - * @param string root (optional) - * @return array of file paths - * - * $part1 and $part2 together form the complete mimetype. - * e.g. searchByMime('text','plain') - * - * seccond mimetype part can be ommited - * e.g. searchByMime('audio') - */ - public static function searchByMime($part1,$part2=null,$root=false) { - if($root===false) { - $root=OC_Filesystem::getRoot(); - } - $rootLen=strlen($root); - $root .= '%'; - $user=OC_User::getUser(); - if(!$part2) { - $query=OC_DB::prepare('SELECT `path` FROM `*PREFIX*fscache` WHERE `mimepart`=? AND `user`=? AND `path` LIKE ?'); - $result=$query->execute(array($part1,$user, $root)); - }else{ - $query=OC_DB::prepare('SELECT `path` FROM `*PREFIX*fscache` WHERE `mimetype`=? AND `user`=? AND `path` LIKE ? '); - $result=$query->execute(array($part1.'/'.$part2,$user, $root)); - } - $names=array(); - while($row=$result->fetchRow()) { - $names[]=substr($row['path'],$rootLen); - } - return $names; - } - - /** - * clean old pre-path_hash entries - */ - public static function clean() { - $query=OC_DB::prepare('DELETE FROM `*PREFIX*fscache` WHERE LENGTH(`path_hash`)<30'); - $query->execute(); - } - - /** - * clear filecache entries - * @param string user (optonal) - */ - public static function clear($user='') { - if($user) { - $query=OC_DB::prepare('DELETE FROM `*PREFIX*fscache` WHERE `user`=?'); - $query->execute(array($user)); - }else{ - $query=OC_DB::prepare('DELETE FROM `*PREFIX*fscache`'); - $query->execute(); - } - } - - /** - * trigger an update for the cache by setting the mtimes to 0 - * @param string $user (optional) - */ - public static function triggerUpdate($user=''){ - if($user) { - $query=OC_DB::prepare('UPDATE `*PREFIX*fscache` SET `mtime`=0 WHERE `user`=? AND `mimetype`="httpd/unix-directory"'); - $query->execute(array($user)); - }else{ - $query=OC_DB::prepare('UPDATE `*PREFIX*fscache` SET `mtime`=0 AND `mimetype`="httpd/unix-directory"'); - $query->execute(); - } - } -} - -//watch for changes and try to keep the cache up to date -OC_Hook::connect('OC_Filesystem','post_write','OC_FileCache_Update','fileSystemWatcherWrite'); -OC_Hook::connect('OC_Filesystem','post_delete','OC_FileCache_Update','fileSystemWatcherDelete'); -OC_Hook::connect('OC_Filesystem','post_rename','OC_FileCache_Update','fileSystemWatcherRename'); diff --git a/lib/filecache/cached.php b/lib/filecache/cached.php deleted file mode 100644 index 9b1eb4f780..0000000000 --- a/lib/filecache/cached.php +++ /dev/null @@ -1,81 +0,0 @@ - - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. - */ - - -/** - * get data from the filecache without checking for updates - */ -class OC_FileCache_Cached{ - public static $savedData=array(); - - public static function get($path,$root=false) { - if($root===false) { - $root=OC_Filesystem::getRoot(); - } - $path=$root.$path; - $stmt=OC_DB::prepare('SELECT `path`,`ctime`,`mtime`,`mimetype`,`size`,`encrypted`,`versioned`,`writable` FROM `*PREFIX*fscache` WHERE `path_hash`=?'); - if ( ! OC_DB::isError($stmt) ) { - $result=$stmt->execute(array(md5($path))); - if ( ! OC_DB::isError($result) ) { - $result = $result->fetchRow(); - } else { - OC:Log::write('OC_FileCache_Cached', 'could not execute get: '. OC_DB::getErrorMessage($result), OC_Log::ERROR); - $result = false; - } - } else { - OC_Log::write('OC_FileCache_Cached', 'could not prepare get: '. OC_DB::getErrorMessage($stmt), OC_Log::ERROR); - $result = false; - } - if(is_array($result)) { - if(isset(self::$savedData[$path])) { - $result=array_merge($result, self::$savedData[$path]); - } - return $result; - }else{ - if(isset(self::$savedData[$path])) { - return self::$savedData[$path]; - }else{ - return array(); - } - } - } - - /** - * get all files and folders in a folder - * @param string path - * @param string root (optional) - * @return array - * - * returns an array of assiciative arrays with the following keys: - * - path - * - name - * - size - * - mtime - * - ctime - * - mimetype - * - encrypted - * - versioned - */ - public static function getFolderContent($path,$root=false,$mimetype_filter='') { - if($root===false) { - $root=OC_Filesystem::getRoot(); - } - $parent=OC_FileCache::getId($path, $root); - if($parent==-1) { - return array(); - } - $query=OC_DB::prepare('SELECT `id`,`path`,`name`,`ctime`,`mtime`,`mimetype`,`size`,`encrypted`,`versioned`,`writable` FROM `*PREFIX*fscache` WHERE `parent`=? AND (`mimetype` LIKE ? OR `mimetype` = ?)'); - $result=$query->execute(array($parent, $mimetype_filter.'%', 'httpd/unix-directory'))->fetchAll(); - if(is_array($result)) { - return $result; - }else{ - OC_Log::write('files', 'getFolderContent(): file not found in cache ('.$path.')', OC_Log::DEBUG); - return false; - } - } -} \ No newline at end of file diff --git a/lib/filecache/update.php b/lib/filecache/update.php deleted file mode 100644 index 1b81f70d77..0000000000 --- a/lib/filecache/update.php +++ /dev/null @@ -1,217 +0,0 @@ - - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. - */ - - -/** - * handles updating the filecache according to outside changes - */ -class OC_FileCache_Update{ - /** - * check if a file or folder is updated outside owncloud - * @param string path - * @param string root (optional) - * @param boolean folder - * @return bool - */ - public static function hasUpdated($path,$root=false,$folder=false) { - if($root===false) { - $view=OC_Filesystem::getView(); - }else{ - $view=new OC_FilesystemView($root); - } - if(!$view->file_exists($path)) { - return false; - } - $cachedData=OC_FileCache_Cached::get($path, $root); - if(isset($cachedData['mtime'])) { - $cachedMTime=$cachedData['mtime']; - if($folder) { - return $view->hasUpdated($path.'/', $cachedMTime); - }else{ - return $view->hasUpdated($path, $cachedMTime); - } - }else{//file not in cache, so it has to be updated - if(($path=='/' or $path=='') and $root===false) {//dont auto update the home folder, it will be scanned - return false; - } - return true; - } - } - - /** - * delete non existing files from the cache - */ - public static function cleanFolder($path,$root=false) { - if($root===false) { - $view=OC_Filesystem::getView(); - }else{ - $view=new OC_FilesystemView($root); - } - - $cachedContent=OC_FileCache_Cached::getFolderContent($path,$root); - foreach($cachedContent as $fileData) { - $path=$fileData['path']; - $file=$view->getRelativePath($path); - if(!$view->file_exists($file)) { - if($root===false) {//filesystem hooks are only valid for the default root - OC_Hook::emit('OC_Filesystem', 'post_delete', array('path'=>$file)); - }else{ - self::delete($file, $root); - } - } - } - } - - /** - * update the cache according to changes in the folder - * @param string path - * @param string root (optional) - */ - public static function updateFolder($path,$root=false) { - if($root===false) { - $view=OC_Filesystem::getView(); - }else{ - $view=new OC_FilesystemView($root); - } - $dh=$view->opendir($path.'/'); - if($dh) {//check for changed/new files - while (($filename = readdir($dh)) !== false) { - if($filename != '.' and $filename != '..' and $filename != '') { - $file=$path.'/'.$filename; - $isDir=$view->is_dir($file); - if(self::hasUpdated($file, $root, $isDir)) { - if($isDir){ - self::updateFolder($file, $root); - }elseif($root===false) {//filesystem hooks are only valid for the default root - OC_Hook::emit('OC_Filesystem', 'post_write', array('path'=>$file)); - }else{ - self::update($file, $root); - } - } - } - } - } - - self::cleanFolder($path, $root); - - //update the folder last, so we can calculate the size correctly - if($root===false) {//filesystem hooks are only valid for the default root - OC_Hook::emit('OC_Filesystem', 'post_write', array('path'=>$path)); - }else{ - self::update($path, $root); - } - } - - /** - * called when changes are made to files - * @param array $params - * @param string root (optional) - */ - public static function fileSystemWatcherWrite($params) { - $path=$params['path']; - self::update($path); - } - - /** - * called when files are deleted - * @param array $params - * @param string root (optional) - */ - public static function fileSystemWatcherDelete($params) { - $path=$params['path']; - self::delete($path); - } - - /** - * called when files are deleted - * @param array $params - * @param string root (optional) - */ - public static function fileSystemWatcherRename($params) { - $oldPath=$params['oldpath']; - $newPath=$params['newpath']; - self::rename($oldPath, $newPath); - } - - /** - * update the filecache according to changes to the filesystem - * @param string path - * @param string root (optional) - */ - public static function update($path,$root=false) { - if($root===false) { - $view=OC_Filesystem::getView(); - }else{ - $view=new OC_FilesystemView($root); - } - - $mimetype=$view->getMimeType($path); - - $size=0; - $cached=OC_FileCache_Cached::get($path,$root); - $cachedSize=isset($cached['size'])?$cached['size']:0; - - if($view->is_dir($path.'/')) { - if(OC_FileCache::inCache($path, $root)) { - $cachedContent=OC_FileCache_Cached::getFolderContent($path, $root); - foreach($cachedContent as $file) { - $size+=$file['size']; - } - $mtime=$view->filemtime($path.'/'); - $ctime=$view->filectime($path.'/'); - $writable=$view->is_writable($path.'/'); - OC_FileCache::put($path, array('size'=>$size,'mtime'=>$mtime,'ctime'=>$ctime,'mimetype'=>$mimetype,'writable'=>$writable)); - }else{ - $count=0; - OC_FileCache::scan($path, null, $count, $root); - return; //increaseSize is already called inside scan - } - }else{ - $size=OC_FileCache::scanFile($path, $root); - } - OC_FileCache::increaseSize(dirname($path), $size-$cachedSize, $root); - } - - /** - * update the filesystem after a delete has been detected - * @param string path - * @param string root (optional) - */ - public static function delete($path,$root=false) { - $cached=OC_FileCache_Cached::get($path, $root); - if(!isset($cached['size'])) { - return; - } - $size=$cached['size']; - OC_FileCache::increaseSize(dirname($path), -$size, $root); - OC_FileCache::delete($path, $root); - } - - /** - * update the filesystem after a rename has been detected - * @param string oldPath - * @param string newPath - * @param string root (optional) - */ - public static function rename($oldPath,$newPath,$root=false) { - if(!OC_FileCache::inCache($oldPath, $root)) { - return; - } - if($root===false) { - $view=OC_Filesystem::getView(); - }else{ - $view=new OC_FilesystemView($root); - } - - $cached=OC_FileCache_Cached::get($oldPath, $root); - $oldSize=$cached['size']; - OC_FileCache::increaseSize(dirname($oldPath), -$oldSize, $root); - OC_FileCache::increaseSize(dirname($newPath), $oldSize, $root); - OC_FileCache::move($oldPath, $newPath); - } -} diff --git a/lib/filesystem.php b/lib/filesystem.php index 0d24b7203b..b73b52d420 100644 --- a/lib/filesystem.php +++ b/lib/filesystem.php @@ -254,13 +254,6 @@ class OC_Filesystem{ } } } - - $mtime=filemtime(OC::$SERVERROOT.'/config/mount.php'); - $previousMTime=OC_Appconfig::getValue('files','mountconfigmtime',0); - if($mtime>$previousMTime) {//mount config has changed, filecache needs to be updated - OC_FileCache::triggerUpdate(); - OC_Appconfig::setValue('files','mountconfigmtime',$mtime); - } } self::$loaded=true; @@ -399,7 +392,7 @@ class OC_Filesystem{ } /** - * checks if a file is blacklsited for storage in the filesystem + * checks if a file is blacklisted for storage in the filesystem * Listens to write and rename hooks * @param array $data from hook */ @@ -419,7 +412,7 @@ class OC_Filesystem{ } /** - * following functions are equivilent to their php buildin equivilents for arguments/return values. + * following functions are equivalent to their php builtin equivalents for arguments/return values. */ static public function mkdir($path) { return self::$defaultInstance->mkdir($path); @@ -592,4 +585,3 @@ OC_Hook::connect('OC_Filesystem','post_delete','OC_Filesystem','removeETagHook') OC_Hook::connect('OC_Filesystem','post_rename','OC_Filesystem','removeETagHook'); OC_Util::setupFS(); -require_once 'filecache.php'; diff --git a/lib/util.php b/lib/util.php index b21580cb01..557f731522 100755 --- a/lib/util.php +++ b/lib/util.php @@ -58,13 +58,6 @@ class OC_Util { OC_Filesystem::mount($options['class'], $options['options'], $mountPoint); } } - - $mtime=filemtime($user_root.'/mount.php'); - $previousMTime=OC_Preferences::getValue($user,'files','mountconfigmtime',0); - if($mtime>$previousMTime) {//mount config has changed, filecache needs to be updated - OC_FileCache::triggerUpdate($user); - OC_Preferences::setValue($user,'files','mountconfigmtime',$mtime); - } } OC_Hook::emit('OC_Filesystem', 'setup', array('user' => $user, 'user_dir' => $user_dir)); } From 9df60d27bd9dad6a37a0d763b808a5b1c5ade5c5 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 10 Oct 2012 11:54:44 +0200 Subject: [PATCH 028/272] move some code around --- lib/files/filesystem.php | 629 +++++++++++++++++++++ lib/{filesystemview.php => files/view.php} | 387 +++++++------ lib/filesystem.php | 562 ------------------ 3 files changed, 835 insertions(+), 743 deletions(-) create mode 100644 lib/files/filesystem.php rename lib/{filesystemview.php => files/view.php} (62%) diff --git a/lib/files/filesystem.php b/lib/files/filesystem.php new file mode 100644 index 0000000000..d0a96b59a0 --- /dev/null +++ b/lib/files/filesystem.php @@ -0,0 +1,629 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +/** + * Class for abstraction of filesystem functions + * This class won't call any filesystem functions for itself but but will pass them to the correct OC_Filestorage object + * this class should also handle all the file permission related stuff + * + * Hooks provided: + * read(path) + * write(path, &run) + * post_write(path) + * create(path, &run) (when a file is created, both create and write will be emited in that order) + * post_create(path) + * delete(path, &run) + * post_delete(path) + * rename(oldpath,newpath, &run) + * post_rename(oldpath,newpath) + * copy(oldpath,newpath, &run) (if the newpath doesn't exists yes, copy, create and write will be emited in that order) + * post_rename(oldpath,newpath) + * + * the &run parameter can be set to false to prevent the operation from occuring + */ + +namespace OC\Files; + +class Filesystem { + static private $storages = array(); + static private $mounts = array(); + public static $loaded = false; + /** + * @var \OC\Files\Storage\Storage $defaultInstance + */ + static private $defaultInstance; + + + /** + * classname which used for hooks handling + * used as signalclass in OC_Hooks::emit() + */ + const CLASSNAME = 'OC_Filesystem'; + + /** + * signalname emited before file renaming + * + * @param oldpath + * @param newpath + */ + const signal_rename = 'rename'; + + /** + * signal emited after file renaming + * + * @param oldpath + * @param newpath + */ + const signal_post_rename = 'post_rename'; + + /** + * signal emited before file/dir creation + * + * @param path + * @param run changing this flag to false in hook handler will cancel event + */ + const signal_create = 'create'; + + /** + * signal emited after file/dir creation + * + * @param path + * @param run changing this flag to false in hook handler will cancel event + */ + const signal_post_create = 'post_create'; + + /** + * signal emits before file/dir copy + * + * @param oldpath + * @param newpath + * @param run changing this flag to false in hook handler will cancel event + */ + const signal_copy = 'copy'; + + /** + * signal emits after file/dir copy + * + * @param oldpath + * @param newpath + */ + const signal_post_copy = 'post_copy'; + + /** + * signal emits before file/dir save + * + * @param path + * @param run changing this flag to false in hook handler will cancel event + */ + const signal_write = 'write'; + + /** + * signal emits after file/dir save + * + * @param path + */ + const signal_post_write = 'post_write'; + + /** + * signal emits when reading file/dir + * + * @param path + */ + const signal_read = 'read'; + + /** + * signal emits when removing file/dir + * + * @param path + */ + const signal_delete = 'delete'; + + /** + * parameters definitions for signals + */ + const signal_param_path = 'path'; + const signal_param_oldpath = 'oldpath'; + const signal_param_newpath = 'newpath'; + + /** + * run - changing this flag to false in hook handler will cancel event + */ + const signal_param_run = 'run'; + + /** + * get the mountpoint of the storage object for a path + ( note: because a storage is not always mounted inside the fakeroot, the returned mountpoint is relative to the absolute root of the filesystem and doesn't take the chroot into account + * + * @param string path + * @return string + */ + static public function getMountPoint($path) { + OC_Hook::emit(self::CLASSNAME, 'get_mountpoint', array('path' => $path)); + if (!$path) { + $path = '/'; + } + if ($path[0] !== '/') { + $path = '/' . $path; + } + $path = str_replace('//', '/', $path); + $foundMountPoint = ''; + $mountPoints = array_keys(OC_Filesystem::$mounts); + foreach ($mountPoints as $mountpoint) { + if ($mountpoint == $path) { + return $mountpoint; + } + if (strpos($path, $mountpoint) === 0 and strlen($mountpoint) > strlen($foundMountPoint)) { + $foundMountPoint = $mountpoint; + } + } + return $foundMountPoint; + } + + /** + * get the part of the path relative to the mountpoint of the storage it's stored in + * + * @param string path + * @return bool + */ + static public function getInternalPath($path) { + $mountPoint = self::getMountPoint($path); + $internalPath = substr($path, strlen($mountPoint)); + return $internalPath; + } + + /** + * get the storage object for a path + * + * @param string path + * @return \OC\Files\Storage\Storage + */ + static public function getStorage($path) { + $mountpoint = self::getMountPoint($path); + if ($mountpoint) { + if (!isset(OC_Filesystem::$storages[$mountpoint])) { + $mount = OC_Filesystem::$mounts[$mountpoint]; + OC_Filesystem::$storages[$mountpoint] = OC_Filesystem::createStorage($mount['class'], $mount['arguments']); + } + return OC_Filesystem::$storages[$mountpoint]; + } + } + + /** + * resolve a path to a storage and internal path + * + * @param string $path + * @return array consisting of the storage and the internal path + */ + static public function resolvePath($path) { + $mountpoint = self::getMountPoint($path); + if ($mountpoint) { + if (!isset(OC_Filesystem::$storages[$mountpoint])) { + $mount = OC_Filesystem::$mounts[$mountpoint]; + OC_Filesystem::$storages[$mountpoint] = OC_Filesystem::createStorage($mount['class'], $mount['arguments']); + } + $storage = OC_Filesystem::$storages[$mountpoint]; + $internalPath = substr($path, strlen($mountpoint)); + return array($storage, $internalPath); + } + } + + static public function init($root) { + if (self::$defaultInstance) { + return false; + } + self::$defaultInstance = new OC_FilesystemView($root); + +//load custom mount config + if (is_file(OC::$SERVERROOT . '/config/mount.php')) { + $mountConfig = include(OC::$SERVERROOT . '/config/mount.php'); + if (isset($mountConfig['global'])) { + foreach ($mountConfig['global'] as $mountPoint => $options) { + self::mount($options['class'], $options['options'], $mountPoint); + } + } + + if (isset($mountConfig['group'])) { + foreach ($mountConfig['group'] as $group => $mounts) { + if (OC_Group::inGroup(OC_User::getUser(), $group)) { + foreach ($mounts as $mountPoint => $options) { + $mountPoint = self::setUserVars($mountPoint); + foreach ($options as &$option) { + $option = self::setUserVars($option); + } + self::mount($options['class'], $options['options'], $mountPoint); + } + } + } + } + + if (isset($mountConfig['user'])) { + foreach ($mountConfig['user'] as $user => $mounts) { + if ($user === 'all' or strtolower($user) === strtolower(OC_User::getUser())) { + foreach ($mounts as $mountPoint => $options) { + $mountPoint = self::setUserVars($mountPoint); + foreach ($options as &$option) { + $option = self::setUserVars($option); + } + self::mount($options['class'], $options['options'], $mountPoint); + } + } + } + } + } + + self::$loaded = true; + } + + /** + * fill in the correct values for $user, and $password placeholders + * + * @param string intput + * @return string + */ + private static function setUserVars($input) { + return str_replace('$user', OC_User::getUser(), $input); + } + + /** + * get the default filesystem view + * + * @return OC_FilesystemView + */ + static public function getView() { + return self::$defaultInstance; + } + + /** + * tear down the filesystem, removing all storage providers + */ + static public function tearDown() { + self::$storages = array(); + } + + /** + * create a new storage of a specific type + * + * @param string type + * @param array arguments + * @return \OC\Files\Storage\Storage + */ + static private function createStorage($class, $arguments) { + if (class_exists($class)) { + try { + return new $class($arguments); + } catch (Exception $exception) { + OC_Log::write('core', $exception->getMessage(), OC_Log::ERROR); + return false; + } + } else { + OC_Log::write('core', 'storage backend ' . $class . ' not found', OC_Log::ERROR); + return false; + } + } + + /** + * change the root to a fake root + * + * @param string fakeRoot + * @return bool + */ + static public function chroot($fakeRoot) { + return self::$defaultInstance->chroot($fakeRoot); + } + + /** + * @brief get the relative path of the root data directory for the current user + * @return string + * + * Returns path like /admin/files + */ + static public function getRoot() { + return self::$defaultInstance->getRoot(); + } + + /** + * clear all mounts and storage backends + */ + public static function clearMounts() { + self::$mounts = array(); + self::$storages = array(); + } + + /** + * mount an \OC\Files\Storage\Storage in our virtual filesystem + * + * @param \OC\Files\Storage\Storage storage + * @param string mountpoint + */ + static public function mount($class, $arguments, $mountpoint) { + if ($mountpoint[0] != '/') { + $mountpoint = '/' . $mountpoint; + } + if (substr($mountpoint, -1) !== '/') { + $mountpoint = $mountpoint . '/'; + } + self::$mounts[$mountpoint] = array('class' => $class, 'arguments' => $arguments); + } + + /** + * return the path to a local version of the file + * we need this because we can't know if a file is stored local or not from outside the filestorage and for some purposes a local file is needed + * + * @param string path + * @return string + */ + static public function getLocalFile($path) { + return self::$defaultInstance->getLocalFile($path); + } + + /** + * @param string path + * @return string + */ + static public function getLocalFolder($path) { + return self::$defaultInstance->getLocalFolder($path); + } + + /** + * return path to file which reflects one visible in browser + * + * @param string path + * @return string + */ + static public function getLocalPath($path) { + $datadir = OC_User::getHome(OC_User::getUser()) . '/files'; + $newpath = $path; + if (strncmp($newpath, $datadir, strlen($datadir)) == 0) { + $newpath = substr($path, strlen($datadir)); + } + return $newpath; + } + + /** + * check if the requested path is valid + * + * @param string path + * @return bool + */ + static public function isValidPath($path) { + if (!$path || $path[0] !== '/') { + $path = '/' . $path; + } + if (strstr($path, '/../') || strrchr($path, '/') === '/..') { + return false; + } + return true; + } + + /** + * checks if a file is blacklisted for storage in the filesystem + * Listens to write and rename hooks + * + * @param array $data from hook + */ + static public function isBlacklisted($data) { + $blacklist = array('.htaccess'); + if (isset($data['path'])) { + $path = $data['path']; + } else if (isset($data['newpath'])) { + $path = $data['newpath']; + } + if (isset($path)) { + $filename = strtolower(basename($path)); + if (in_array($filename, $blacklist)) { + $data['run'] = false; + } + } + } + + /** + * following functions are equivalent to their php builtin equivalents for arguments/return values. + */ + static public function mkdir($path) { + return self::$defaultInstance->mkdir($path); + } + + static public function rmdir($path) { + return self::$defaultInstance->rmdir($path); + } + + static public function opendir($path) { + return self::$defaultInstance->opendir($path); + } + + static public function readdir($path) { + return self::$defaultInstance->readdir($path); + } + + static public function is_dir($path) { + return self::$defaultInstance->is_dir($path); + } + + static public function is_file($path) { + return self::$defaultInstance->is_file($path); + } + + static public function stat($path) { + return self::$defaultInstance->stat($path); + } + + static public function filetype($path) { + return self::$defaultInstance->filetype($path); + } + + static public function filesize($path) { + return self::$defaultInstance->filesize($path); + } + + static public function readfile($path) { + return self::$defaultInstance->readfile($path); + } + + /** + * @deprecated Replaced by isReadable() as part of CRUDS + */ + static public function is_readable($path) { + return self::$defaultInstance->is_readable($path); + } + + /** + * @deprecated Replaced by isCreatable(), isUpdatable(), isDeletable() as part of CRUDS + */ + static public function is_writable($path) { + return self::$defaultInstance->is_writable($path); + } + + static public function isCreatable($path) { + return self::$defaultInstance->isCreatable($path); + } + + static public function isReadable($path) { + return self::$defaultInstance->isReadable($path); + } + + static public function isUpdatable($path) { + return self::$defaultInstance->isUpdatable($path); + } + + static public function isDeletable($path) { + return self::$defaultInstance->isDeletable($path); + } + + static public function isSharable($path) { + return self::$defaultInstance->isSharable($path); + } + + static public function file_exists($path) { + return self::$defaultInstance->file_exists($path); + } + + static public function filectime($path) { + return self::$defaultInstance->filectime($path); + } + + static public function filemtime($path) { + return self::$defaultInstance->filemtime($path); + } + + static public function touch($path, $mtime = null) { + return self::$defaultInstance->touch($path, $mtime); + } + + static public function file_get_contents($path) { + return self::$defaultInstance->file_get_contents($path); + } + + static public function file_put_contents($path, $data) { + return self::$defaultInstance->file_put_contents($path, $data); + } + + static public function unlink($path) { + return self::$defaultInstance->unlink($path); + } + + static public function rename($path1, $path2) { + return self::$defaultInstance->rename($path1, $path2); + } + + static public function copy($path1, $path2) { + return self::$defaultInstance->copy($path1, $path2); + } + + static public function fopen($path, $mode) { + return self::$defaultInstance->fopen($path, $mode); + } + + static public function toTmpFile($path) { + return self::$defaultInstance->toTmpFile($path); + } + + static public function fromTmpFile($tmpFile, $path) { + return self::$defaultInstance->fromTmpFile($tmpFile, $path); + } + + static public function getMimeType($path) { + return self::$defaultInstance->getMimeType($path); + } + + static public function hash($type, $path, $raw = false) { + return self::$defaultInstance->hash($type, $path, $raw); + } + + static public function free_space($path = '/') { + return self::$defaultInstance->free_space($path); + } + + static public function search($query) { + return OC_FileCache::search($query); + } + + /** + * check if a file or folder has been updated since $time + * + * @param int $time + * @return bool + */ + static public function hasUpdated($path, $time) { + return self::$defaultInstance->hasUpdated($path, $time); + } + + static public function removeETagHook($params, $root = false) { + if (isset($params['path'])) { + $path = $params['path']; + } else { + $path = $params['oldpath']; + } + + if ($root) { // reduce path to the required part of it (no 'username/files') + $fakeRootView = new OC_FilesystemView($root); + $count = 1; + $path = str_replace(OC_App::getStorage("files")->getAbsolutePath($path), "", $fakeRootView->getAbsolutePath($path), $count); + } + + $path = self::normalizePath($path); + OC_Connector_Sabre_Node::removeETagPropertyForPath($path); + } + + /** + * normalize a path + * + * @param string path + * @param bool $stripTrailingSlash + * @return string + */ + public static function normalizePath($path, $stripTrailingSlash = true) { + if ($path == '') { + return '/'; + } +//no windows style slashes + $path = str_replace('\\', '/', $path); +//add leading slash + if ($path[0] !== '/') { + $path = '/' . $path; + } +//remove trainling slash + if ($stripTrailingSlash and strlen($path) > 1 and substr($path, -1, 1) === '/') { + $path = substr($path, 0, -1); + } +//remove duplicate slashes + while (strpos($path, '//') !== false) { + $path = str_replace('//', '/', $path); + } +//normalize unicode if possible + if (class_exists('Normalizer')) { + $path = Normalizer::normalize($path); + } + return $path; + } +} + +OC_Hook::connect('OC_Filesystem', 'post_write', 'OC_Filesystem', 'removeETagHook'); +OC_Hook::connect('OC_Filesystem', 'post_delete', 'OC_Filesystem', 'removeETagHook'); +OC_Hook::connect('OC_Filesystem', 'post_rename', 'OC_Filesystem', 'removeETagHook'); + +OC_Util::setupFS(); diff --git a/lib/filesystemview.php b/lib/files/view.php similarity index 62% rename from lib/filesystemview.php rename to lib/files/view.php index 071fc781f1..8c3f288f44 100644 --- a/lib/filesystemview.php +++ b/lib/files/view.php @@ -1,26 +1,11 @@ . + * Copyright (c) 2012 Robin Appelman + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. */ - /** * Class to provide access to ownCloud filesystem via a "view", and methods for * working with files within that view (e.g. read, write, delete, etc.). Each @@ -38,41 +23,45 @@ * \OC\Files\Storage\Storage object */ -class OC_FilesystemView { - private $fakeRoot=''; - private $internal_path_cache=array(); - private $storage_cache=array(); +namespace OC\Files; + +class View { + private $fakeRoot = ''; + private $internal_path_cache = array(); + private $storage_cache = array(); public function __construct($root) { - $this->fakeRoot=$root; + $this->fakeRoot = $root; } public function getAbsolutePath($path) { - if(!$path) { - $path='/'; + if (!$path) { + $path = '/'; } - if($path[0]!=='/') { - $path='/'.$path; + if ($path[0] !== '/') { + $path = '/' . $path; } - return $this->fakeRoot.$path; + return $this->fakeRoot . $path; } /** - * change the root to a fake toor - * @param string fakeRoot - * @return bool - */ + * change the root to a fake toor + * + * @param string fakeRoot + * @return bool + */ public function chroot($fakeRoot) { - if(!$fakeRoot=='') { - if($fakeRoot[0]!=='/') { - $fakeRoot='/'.$fakeRoot; + if (!$fakeRoot == '') { + if ($fakeRoot[0] !== '/') { + $fakeRoot = '/' . $fakeRoot; } } - $this->fakeRoot=$fakeRoot; + $this->fakeRoot = $fakeRoot; } /** * get the fake root + * * @return string */ public function getRoot() { @@ -80,10 +69,11 @@ class OC_FilesystemView { } /** - * get the part of the path relative to the mountpoint of the storage it's stored in - * @param string path - * @return bool - */ + * get the part of the path relative to the mountpoint of the storage it's stored in + * + * @param string path + * @return bool + */ public function getInternalPath($path) { if (!isset($this->internal_path_cache[$path])) { $this->internal_path_cache[$path] = OC_Filesystem::getInternalPath($this->getAbsolutePath($path)); @@ -93,30 +83,32 @@ class OC_FilesystemView { /** * get path relative to the root of the view + * * @param string path * @return string */ public function getRelativePath($path) { - if($this->fakeRoot=='') { + if ($this->fakeRoot == '') { return $path; } - if(strpos($path, $this->fakeRoot)!==0) { + if (strpos($path, $this->fakeRoot) !== 0) { return null; - }else{ - $path=substr($path, strlen($this->fakeRoot)); - if(strlen($path)===0) { + } else { + $path = substr($path, strlen($this->fakeRoot)); + if (strlen($path) === 0) { return '/'; - }else{ + } else { return $path; } } } /** - * get the storage object for a path - * @param string path - * @return \OC\Files\Storage\Storage - */ + * get the storage object for a path + * + * @param string path + * @return \OC\Files\Storage\Storage + */ public function getStorage($path) { if (!isset($this->storage_cache[$path])) { $this->storage_cache[$path] = OC_Filesystem::getStorage($this->getAbsolutePath($path)); @@ -125,35 +117,37 @@ class OC_FilesystemView { } /** - * get the mountpoint of the storage object for a path + * get the mountpoint of the storage object for a path ( note: because a storage is not always mounted inside the fakeroot, the returned mountpoint is relative to the absolute root of the filesystem and doesn't take the chroot into account - * - * @param string path - * @return string - */ + * + * @param string path + * @return string + */ public function getMountPoint($path) { return OC_Filesystem::getMountPoint($this->getAbsolutePath($path)); } /** - * return the path to a local version of the file - * we need this because we can't know if a file is stored local or not from outside the filestorage and for some purposes a local file is needed - * @param string path - * @return string - */ + * return the path to a local version of the file + * we need this because we can't know if a file is stored local or not from outside the filestorage and for some purposes a local file is needed + * + * @param string path + * @return string + */ public function getLocalFile($path) { - $parent=substr($path, 0, strrpos($path,'/')); - if(OC_Filesystem::isValidPath($parent) and $storage=$this->getStorage($path)) { + $parent = substr($path, 0, strrpos($path, '/')); + if (OC_Filesystem::isValidPath($parent) and $storage = $this->getStorage($path)) { return $storage->getLocalFile($this->getInternalPath($path)); } } + /** * @param string path * @return string */ public function getLocalFolder($path) { - $parent=substr($path, 0, strrpos($path,'/')); - if(OC_Filesystem::isValidPath($parent) and $storage=$this->getStorage($path)) { + $parent = substr($path, 0, strrpos($path, '/')); + if (OC_Filesystem::isValidPath($parent) and $storage = $this->getStorage($path)) { return $storage->getLocalFolder($this->getInternalPath($path)); } } @@ -166,105 +160,127 @@ class OC_FilesystemView { public function mkdir($path) { return $this->basicOperation('mkdir', $path, array('create', 'write')); } + public function rmdir($path) { return $this->basicOperation('rmdir', $path, array('delete')); } + public function opendir($path) { return $this->basicOperation('opendir', $path, array('read')); } + public function readdir($handle) { - $fsLocal= new \OC\Files\Storage\Local( array( 'datadir' => '/' ) ); - return $fsLocal->readdir( $handle ); + $fsLocal = new \OC\Files\Storage\Local(array('datadir' => '/')); + return $fsLocal->readdir($handle); } + public function is_dir($path) { - if($path=='/') { + if ($path == '/') { return true; } return $this->basicOperation('is_dir', $path); } + public function is_file($path) { - if($path=='/') { + if ($path == '/') { return false; } return $this->basicOperation('is_file', $path); } + public function stat($path) { return $this->basicOperation('stat', $path); } + public function filetype($path) { return $this->basicOperation('filetype', $path); } + public function filesize($path) { return $this->basicOperation('filesize', $path); } + public function readfile($path) { @ob_end_clean(); - $handle=$this->fopen($path, 'rb'); + $handle = $this->fopen($path, 'rb'); if ($handle) { - $chunkSize = 8192;// 8 MB chunks + $chunkSize = 8192; // 8 MB chunks while (!feof($handle)) { echo fread($handle, $chunkSize); flush(); } - $size=$this->filesize($path); + $size = $this->filesize($path); return $size; } return false; } + /** - * @deprecated Replaced by isReadable() as part of CRUDS - */ + * @deprecated Replaced by isReadable() as part of CRUDS + */ public function is_readable($path) { - return $this->basicOperation('isReadable',$path); + return $this->basicOperation('isReadable', $path); } + /** - * @deprecated Replaced by isCreatable(), isUpdatable(), isDeletable() as part of CRUDS - */ + * @deprecated Replaced by isCreatable(), isUpdatable(), isDeletable() as part of CRUDS + */ public function is_writable($path) { - return $this->basicOperation('isUpdatable',$path); + return $this->basicOperation('isUpdatable', $path); } + public function isCreatable($path) { return $this->basicOperation('isCreatable', $path); } + public function isReadable($path) { return $this->basicOperation('isReadable', $path); } + public function isUpdatable($path) { return $this->basicOperation('isUpdatable', $path); } + public function isDeletable($path) { return $this->basicOperation('isDeletable', $path); } + public function isSharable($path) { return $this->basicOperation('isSharable', $path); } + public function file_exists($path) { - if($path=='/') { + if ($path == '/') { return true; } return $this->basicOperation('file_exists', $path); } + public function filectime($path) { return $this->basicOperation('filectime', $path); } + public function filemtime($path) { return $this->basicOperation('filemtime', $path); } - public function touch($path, $mtime=null) { + + public function touch($path, $mtime = null) { return $this->basicOperation('touch', $path, array('write'), $mtime); } + public function file_get_contents($path) { return $this->basicOperation('file_get_contents', $path, array('read')); } + public function file_put_contents($path, $data) { - if(is_resource($data)) {//not having to deal with streams in file_put_contents makes life easier + if (is_resource($data)) { //not having to deal with streams in file_put_contents makes life easier $absolutePath = OC_Filesystem::normalizePath($this->getAbsolutePath($path)); if (OC_FileProxy::runPreProxies('file_put_contents', $absolutePath, $data) && OC_Filesystem::isValidPath($path)) { $path = $this->getRelativePath($absolutePath); $exists = $this->file_exists($path); $run = true; - if( $this->fakeRoot==OC_Filesystem::getRoot() ){ - if(!$exists) { + if ($this->fakeRoot == OC_Filesystem::getRoot()) { + if (!$exists) { OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_create, @@ -283,83 +299,86 @@ class OC_FilesystemView { ) ); } - if(!$run) { + if (!$run) { return false; } - $target=$this->fopen($path, 'w'); - if($target) { - $count=OC_Helper::streamCopy($data, $target); + $target = $this->fopen($path, 'w'); + if ($target) { + $count = OC_Helper::streamCopy($data, $target); fclose($target); fclose($data); - if( $this->fakeRoot==OC_Filesystem::getRoot() ){ - if(!$exists) { + if ($this->fakeRoot == OC_Filesystem::getRoot()) { + if (!$exists) { OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_create, - array( OC_Filesystem::signal_param_path => $path) + array(OC_Filesystem::signal_param_path => $path) ); } OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_write, - array( OC_Filesystem::signal_param_path => $path) + array(OC_Filesystem::signal_param_path => $path) ); } OC_FileProxy::runPostProxies('file_put_contents', $absolutePath, $count); return $count > 0; - }else{ + } else { return false; } } - }else{ + } else { return $this->basicOperation('file_put_contents', $path, array('create', 'write'), $data); } } + public function unlink($path) { return $this->basicOperation('unlink', $path, array('delete')); } - public function deleteAll( $directory, $empty = false ) { - return $this->basicOperation( 'deleteAll', $directory, array('delete'), $empty ); + + public function deleteAll($directory, $empty = false) { + return $this->basicOperation('deleteAll', $directory, array('delete'), $empty); } + public function rename($path1, $path2) { - $postFix1=(substr($path1,-1,1)==='/')?'/':''; - $postFix2=(substr($path2,-1,1)==='/')?'/':''; + $postFix1 = (substr($path1, -1, 1) === '/') ? '/' : ''; + $postFix2 = (substr($path2, -1, 1) === '/') ? '/' : ''; $absolutePath1 = OC_Filesystem::normalizePath($this->getAbsolutePath($path1)); $absolutePath2 = OC_Filesystem::normalizePath($this->getAbsolutePath($path2)); - if(OC_FileProxy::runPreProxies('rename', $absolutePath1, $absolutePath2) and OC_Filesystem::isValidPath($path2)) { + if (OC_FileProxy::runPreProxies('rename', $absolutePath1, $absolutePath2) and OC_Filesystem::isValidPath($path2)) { $path1 = $this->getRelativePath($absolutePath1); $path2 = $this->getRelativePath($absolutePath2); - if($path1 == null or $path2 == null) { + if ($path1 == null or $path2 == null) { return false; } - $run=true; - if( $this->fakeRoot==OC_Filesystem::getRoot() ){ + $run = true; + if ($this->fakeRoot == OC_Filesystem::getRoot()) { OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_rename, - array( - OC_Filesystem::signal_param_oldpath => $path1, - OC_Filesystem::signal_param_newpath => $path2, - OC_Filesystem::signal_param_run => &$run - ) + array( + OC_Filesystem::signal_param_oldpath => $path1, + OC_Filesystem::signal_param_newpath => $path2, + OC_Filesystem::signal_param_run => &$run + ) ); } - if($run) { - $mp1 = $this->getMountPoint($path1.$postFix1); - $mp2 = $this->getMountPoint($path2.$postFix2); - if($mp1 == $mp2) { - if($storage = $this->getStorage($path1)) { - $result = $storage->rename($this->getInternalPath($path1.$postFix1), $this->getInternalPath($path2.$postFix2)); + if ($run) { + $mp1 = $this->getMountPoint($path1 . $postFix1); + $mp2 = $this->getMountPoint($path2 . $postFix2); + if ($mp1 == $mp2) { + if ($storage = $this->getStorage($path1)) { + $result = $storage->rename($this->getInternalPath($path1 . $postFix1), $this->getInternalPath($path2 . $postFix2)); } } else { - $source = $this->fopen($path1.$postFix1, 'r'); - $target = $this->fopen($path2.$postFix2, 'w'); + $source = $this->fopen($path1 . $postFix1, 'r'); + $target = $this->fopen($path2 . $postFix2, 'w'); $count = OC_Helper::streamCopy($source, $target); $storage1 = $this->getStorage($path1); - $storage1->unlink($this->getInternalPath($path1.$postFix1)); - $result = $count>0; + $storage1->unlink($this->getInternalPath($path1 . $postFix1)); + $result = $count > 0; } - if( $this->fakeRoot==OC_Filesystem::getRoot() ){ + if ($this->fakeRoot == OC_Filesystem::getRoot()) { OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_rename, @@ -373,31 +392,32 @@ class OC_FilesystemView { } } } + public function copy($path1, $path2) { - $postFix1=(substr($path1,-1,1)==='/')?'/':''; - $postFix2=(substr($path2,-1,1)==='/')?'/':''; + $postFix1 = (substr($path1, -1, 1) === '/') ? '/' : ''; + $postFix2 = (substr($path2, -1, 1) === '/') ? '/' : ''; $absolutePath1 = OC_Filesystem::normalizePath($this->getAbsolutePath($path1)); $absolutePath2 = OC_Filesystem::normalizePath($this->getAbsolutePath($path2)); - if(OC_FileProxy::runPreProxies('copy', $absolutePath1, $absolutePath2) and OC_Filesystem::isValidPath($path2)) { + if (OC_FileProxy::runPreProxies('copy', $absolutePath1, $absolutePath2) and OC_Filesystem::isValidPath($path2)) { $path1 = $this->getRelativePath($absolutePath1); $path2 = $this->getRelativePath($absolutePath2); - if($path1 == null or $path2 == null) { + if ($path1 == null or $path2 == null) { return false; } - $run=true; - if( $this->fakeRoot==OC_Filesystem::getRoot() ){ + $run = true; + if ($this->fakeRoot == OC_Filesystem::getRoot()) { OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_copy, array( OC_Filesystem::signal_param_oldpath => $path1, - OC_Filesystem::signal_param_newpath=>$path2, + OC_Filesystem::signal_param_newpath => $path2, OC_Filesystem::signal_param_run => &$run ) ); - $exists=$this->file_exists($path2); - if($run and !$exists) { + $exists = $this->file_exists($path2); + if ($run and !$exists) { OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_create, @@ -407,7 +427,7 @@ class OC_FilesystemView { ) ); } - if($run) { + if ($run) { OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_write, @@ -418,28 +438,28 @@ class OC_FilesystemView { ); } } - if($run) { - $mp1=$this->getMountPoint($path1.$postFix1); - $mp2=$this->getMountPoint($path2.$postFix2); - if($mp1 == $mp2) { - if($storage = $this->getStorage($path1.$postFix1)) { - $result=$storage->copy($this->getInternalPath($path1.$postFix1), $this->getInternalPath($path2.$postFix2)); + if ($run) { + $mp1 = $this->getMountPoint($path1 . $postFix1); + $mp2 = $this->getMountPoint($path2 . $postFix2); + if ($mp1 == $mp2) { + if ($storage = $this->getStorage($path1 . $postFix1)) { + $result = $storage->copy($this->getInternalPath($path1 . $postFix1), $this->getInternalPath($path2 . $postFix2)); } } else { - $source = $this->fopen($path1.$postFix1, 'r'); - $target = $this->fopen($path2.$postFix2, 'w'); + $source = $this->fopen($path1 . $postFix1, 'r'); + $target = $this->fopen($path2 . $postFix2, 'w'); $result = OC_Helper::streamCopy($source, $target); } - if( $this->fakeRoot==OC_Filesystem::getRoot() ){ + if ($this->fakeRoot == OC_Filesystem::getRoot()) { OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_copy, array( OC_Filesystem::signal_param_oldpath => $path1, - OC_Filesystem::signal_param_newpath=>$path2 + OC_Filesystem::signal_param_newpath => $path2 ) ); - if(!$exists) { + if (!$exists) { OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_create, @@ -449,7 +469,7 @@ class OC_FilesystemView { OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_write, - array( OC_Filesystem::signal_param_path => $path2) + array(OC_Filesystem::signal_param_path => $path2) ); } else { // no real copy, file comes from somewhere else, e.g. version rollback -> just update the file cache and the webdav properties without all the other post_write actions OC_FileCache_Update::update($path2, $this->fakeRoot); @@ -459,12 +479,13 @@ class OC_FilesystemView { } } } + public function fopen($path, $mode) { - $hooks=array(); - switch($mode) { + $hooks = array(); + switch ($mode) { case 'r': case 'rb': - $hooks[]='read'; + $hooks[] = 'read'; break; case 'r+': case 'rb+': @@ -474,8 +495,8 @@ class OC_FilesystemView { case 'xb+': case 'a+': case 'ab+': - $hooks[]='read'; - $hooks[]='write'; + $hooks[] = 'read'; + $hooks[] = 'write'; break; case 'w': case 'wb': @@ -483,22 +504,23 @@ class OC_FilesystemView { case 'xb': case 'a': case 'ab': - $hooks[]='write'; + $hooks[] = 'write'; break; default: - OC_Log::write('core','invalid mode ('.$mode.') for '.$path,OC_Log::ERROR); + OC_Log::write('core', 'invalid mode (' . $mode . ') for ' . $path, OC_Log::ERROR); } return $this->basicOperation('fopen', $path, $hooks, $mode); } + public function toTmpFile($path) { - if(OC_Filesystem::isValidPath($path)) { + if (OC_Filesystem::isValidPath($path)) { $source = $this->fopen($path, 'r'); - if($source) { - $extension=''; - $extOffset=strpos($path, '.'); - if($extOffset !== false) { - $extension=substr($path, strrpos($path,'.')); + if ($source) { + $extension = ''; + $extOffset = strpos($path, '.'); + if ($extOffset !== false) { + $extension = substr($path, strrpos($path, '.')); } $tmpFile = OC_Helper::tmpFile($extension); file_put_contents($tmpFile, $source); @@ -506,13 +528,14 @@ class OC_FilesystemView { } } } + public function fromTmpFile($tmpFile, $path) { - if(OC_Filesystem::isValidPath($path)) { - if(!$tmpFile) { + if (OC_Filesystem::isValidPath($path)) { + if (!$tmpFile) { debug_print_backtrace(); } - $source=fopen($tmpFile, 'r'); - if($source) { + $source = fopen($tmpFile, 'r'); + if ($source) { $this->file_put_contents($path, $source); unlink($tmpFile); return true; @@ -526,8 +549,9 @@ class OC_FilesystemView { public function getMimeType($path) { return $this->basicOperation('getMimeType', $path); } + public function hash($type, $path, $raw = false) { - $postFix=(substr($path,-1,1)==='/')?'/':''; + $postFix = (substr($path, -1, 1) === '/') ? '/' : ''; $absolutePath = OC_Filesystem::normalizePath($this->getAbsolutePath($path)); if (OC_FileProxy::runPreProxies('hash', $absolutePath) && OC_Filesystem::isValidPath($path)) { $path = $this->getRelativePath($absolutePath); @@ -538,11 +562,11 @@ class OC_FilesystemView { OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_read, - array( OC_Filesystem::signal_param_path => $path) + array(OC_Filesystem::signal_param_path => $path) ); } - if ($storage = $this->getStorage($path.$postFix)) { - $result = $storage->hash($type, $this->getInternalPath($path.$postFix), $raw); + if ($storage = $this->getStorage($path . $postFix)) { + $result = $storage->hash($type, $this->getInternalPath($path . $postFix), $raw); $result = OC_FileProxy::runPostProxies('hash', $absolutePath, $result); return $result; } @@ -550,7 +574,7 @@ class OC_FilesystemView { return null; } - public function free_space($path='/') { + public function free_space($path = '/') { return $this->basicOperation('free_space', $path); } @@ -566,26 +590,26 @@ class OC_FilesystemView { * files), processes hooks and proxies, sanitises paths, and finally passes them on to * \OC\Files\Storage\Storage for delegation to a storage backend for execution */ - private function basicOperation($operation, $path, $hooks=array(), $extraParam=null) { - $postFix=(substr($path,-1,1)==='/')?'/':''; + private function basicOperation($operation, $path, $hooks = array(), $extraParam = null) { + $postFix = (substr($path, -1, 1) === '/') ? '/' : ''; $absolutePath = OC_Filesystem::normalizePath($this->getAbsolutePath($path)); - if(OC_FileProxy::runPreProxies($operation, $absolutePath, $extraParam) and OC_Filesystem::isValidPath($path)) { + if (OC_FileProxy::runPreProxies($operation, $absolutePath, $extraParam) and OC_Filesystem::isValidPath($path)) { $path = $this->getRelativePath($absolutePath); - if($path == null) { + if ($path == null) { return false; } - $internalPath = $this->getInternalPath($path.$postFix); - $run=$this->runHooks($hooks,$path); - if($run and $storage = $this->getStorage($path.$postFix)) { - if(!is_null($extraParam)) { + $internalPath = $this->getInternalPath($path . $postFix); + $run = $this->runHooks($hooks, $path); + if ($run and $storage = $this->getStorage($path . $postFix)) { + if (!is_null($extraParam)) { $result = $storage->$operation($internalPath, $extraParam); } else { $result = $storage->$operation($internalPath); } $result = OC_FileProxy::runPostProxies($operation, $this->getAbsolutePath($path), $result); - if(OC_Filesystem::$loaded and $this->fakeRoot==OC_Filesystem::getRoot()) { - if($operation!='fopen') {//no post hooks for fopen, the file stream is still open - $this->runHooks($hooks,$path,true); + if (OC_Filesystem::$loaded and $this->fakeRoot == OC_Filesystem::getRoot()) { + if ($operation != 'fopen') { //no post hooks for fopen, the file stream is still open + $this->runHooks($hooks, $path, true); } } return $result; @@ -594,24 +618,24 @@ class OC_FilesystemView { return null; } - private function runHooks($hooks,$path,$post=false) { - $prefix=($post)?'post_':''; - $run=true; - if(OC_Filesystem::$loaded and $this->fakeRoot==OC_Filesystem::getRoot()) { - foreach($hooks as $hook) { - if($hook!='read') { + private function runHooks($hooks, $path, $post = false) { + $prefix = ($post) ? 'post_' : ''; + $run = true; + if (OC_Filesystem::$loaded and $this->fakeRoot == OC_Filesystem::getRoot()) { + foreach ($hooks as $hook) { + if ($hook != 'read') { OC_Hook::emit( OC_Filesystem::CLASSNAME, - $prefix.$hook, + $prefix . $hook, array( OC_Filesystem::signal_param_run => &$run, OC_Filesystem::signal_param_path => $path ) ); - } elseif(!$post) { + } elseif (!$post) { OC_Hook::emit( OC_Filesystem::CLASSNAME, - $prefix.$hook, + $prefix . $hook, array( OC_Filesystem::signal_param_path => $path ) @@ -624,6 +648,7 @@ class OC_FilesystemView { /** * check if a file or folder has been updated since $time + * * @param int $time * @return bool */ diff --git a/lib/filesystem.php b/lib/filesystem.php index b73b52d420..b1200f95bf 100644 --- a/lib/filesystem.php +++ b/lib/filesystem.php @@ -22,566 +22,4 @@ */ -/** - * Class for abstraction of filesystem functions - * This class won't call any filesystem functions for itself but but will pass them to the correct OC_Filestorage object - * this class should also handle all the file permission related stuff - * - * Hooks provided: - * read(path) - * write(path, &run) - * post_write(path) - * create(path, &run) (when a file is created, both create and write will be emited in that order) - * post_create(path) - * delete(path, &run) - * post_delete(path) - * rename(oldpath,newpath, &run) - * post_rename(oldpath,newpath) - * copy(oldpath,newpath, &run) (if the newpath doesn't exists yes, copy, create and write will be emited in that order) - * post_rename(oldpath,newpath) - * - * the &run parameter can be set to false to prevent the operation from occuring - */ -class OC_Filesystem{ - static private $storages=array(); - static private $mounts=array(); - public static $loaded=false; - /** - * @var \OC\Files\Storage\Storage $defaultInstance - */ - static private $defaultInstance; - - - /** - * classname which used for hooks handling - * used as signalclass in OC_Hooks::emit() - */ - const CLASSNAME = 'OC_Filesystem'; - - /** - * signalname emited before file renaming - * @param oldpath - * @param newpath - */ - const signal_rename = 'rename'; - - /** - * signal emited after file renaming - * @param oldpath - * @param newpath - */ - const signal_post_rename = 'post_rename'; - - /** - * signal emited before file/dir creation - * @param path - * @param run changing this flag to false in hook handler will cancel event - */ - const signal_create = 'create'; - - /** - * signal emited after file/dir creation - * @param path - * @param run changing this flag to false in hook handler will cancel event - */ - const signal_post_create = 'post_create'; - - /** - * signal emits before file/dir copy - * @param oldpath - * @param newpath - * @param run changing this flag to false in hook handler will cancel event - */ - const signal_copy = 'copy'; - - /** - * signal emits after file/dir copy - * @param oldpath - * @param newpath - */ - const signal_post_copy = 'post_copy'; - - /** - * signal emits before file/dir save - * @param path - * @param run changing this flag to false in hook handler will cancel event - */ - const signal_write = 'write'; - - /** - * signal emits after file/dir save - * @param path - */ - const signal_post_write = 'post_write'; - - /** - * signal emits when reading file/dir - * @param path - */ - const signal_read = 'read'; - - /** - * signal emits when removing file/dir - * @param path - */ - const signal_delete = 'delete'; - - /** - * parameters definitions for signals - */ - const signal_param_path = 'path'; - const signal_param_oldpath = 'oldpath'; - const signal_param_newpath = 'newpath'; - - /** - * run - changing this flag to false in hook handler will cancel event - */ - const signal_param_run = 'run'; - - /** - * get the mountpoint of the storage object for a path - ( note: because a storage is not always mounted inside the fakeroot, the returned mountpoint is relative to the absolute root of the filesystem and doesn't take the chroot into account - * - * @param string path - * @return string - */ - static public function getMountPoint($path) { - OC_Hook::emit(self::CLASSNAME,'get_mountpoint',array('path'=>$path)); - if(!$path) { - $path='/'; - } - if($path[0]!=='/') { - $path='/'.$path; - } - $path=str_replace('//', '/',$path); - $foundMountPoint=''; - $mountPoints=array_keys(OC_Filesystem::$mounts); - foreach($mountPoints as $mountpoint) { - if($mountpoint==$path) { - return $mountpoint; - } - if(strpos($path,$mountpoint)===0 and strlen($mountpoint)>strlen($foundMountPoint)) { - $foundMountPoint=$mountpoint; - } - } - return $foundMountPoint; - } - - /** - * get the part of the path relative to the mountpoint of the storage it's stored in - * @param string path - * @return bool - */ - static public function getInternalPath($path) { - $mountPoint=self::getMountPoint($path); - $internalPath=substr($path,strlen($mountPoint)); - return $internalPath; - } - /** - * get the storage object for a path - * @param string path - * @return \OC\Files\Storage\Storage - */ - static public function getStorage($path) { - $mountpoint=self::getMountPoint($path); - if($mountpoint) { - if(!isset(OC_Filesystem::$storages[$mountpoint])) { - $mount=OC_Filesystem::$mounts[$mountpoint]; - OC_Filesystem::$storages[$mountpoint]=OC_Filesystem::createStorage($mount['class'],$mount['arguments']); - } - return OC_Filesystem::$storages[$mountpoint]; - } - } - - /** - * resolve a path to a storage and internal path - * @param string $path - * @return array consisting of the storage and the internal path - */ - static public function resolvePath($path){ - $mountpoint=self::getMountPoint($path); - if($mountpoint) { - if(!isset(OC_Filesystem::$storages[$mountpoint])) { - $mount=OC_Filesystem::$mounts[$mountpoint]; - OC_Filesystem::$storages[$mountpoint]=OC_Filesystem::createStorage($mount['class'],$mount['arguments']); - } - $storage = OC_Filesystem::$storages[$mountpoint]; - $internalPath=substr($path,strlen($mountpoint)); - return array($storage, $internalPath); - } - } - - static public function init($root) { - if(self::$defaultInstance) { - return false; - } - self::$defaultInstance=new OC_FilesystemView($root); - - //load custom mount config - if(is_file(OC::$SERVERROOT.'/config/mount.php')) { - $mountConfig=include(OC::$SERVERROOT.'/config/mount.php'); - if(isset($mountConfig['global'])) { - foreach($mountConfig['global'] as $mountPoint=>$options) { - self::mount($options['class'],$options['options'],$mountPoint); - } - } - - if(isset($mountConfig['group'])) { - foreach($mountConfig['group'] as $group=>$mounts) { - if(OC_Group::inGroup(OC_User::getUser(),$group)) { - foreach($mounts as $mountPoint=>$options) { - $mountPoint=self::setUserVars($mountPoint); - foreach($options as &$option) { - $option=self::setUserVars($option); - } - self::mount($options['class'],$options['options'],$mountPoint); - } - } - } - } - - if(isset($mountConfig['user'])) { - foreach($mountConfig['user'] as $user=>$mounts) { - if($user==='all' or strtolower($user)===strtolower(OC_User::getUser())) { - foreach($mounts as $mountPoint=>$options) { - $mountPoint=self::setUserVars($mountPoint); - foreach($options as &$option) { - $option=self::setUserVars($option); - } - self::mount($options['class'],$options['options'],$mountPoint); - } - } - } - } - } - - self::$loaded=true; - } - - /** - * fill in the correct values for $user, and $password placeholders - * @param string intput - * @return string - */ - private static function setUserVars($input) { - return str_replace('$user',OC_User::getUser(),$input); - } - - /** - * get the default filesystem view - * @return OC_FilesystemView - */ - static public function getView() { - return self::$defaultInstance; - } - - /** - * tear down the filesystem, removing all storage providers - */ - static public function tearDown() { - self::$storages=array(); - } - - /** - * create a new storage of a specific type - * @param string type - * @param array arguments - * @return \OC\Files\Storage\Storage - */ - static private function createStorage($class,$arguments) { - if(class_exists($class)) { - try { - return new $class($arguments); - } catch (Exception $exception) { - OC_Log::write('core', $exception->getMessage(), OC_Log::ERROR); - return false; - } - }else{ - OC_Log::write('core','storage backend '.$class.' not found',OC_Log::ERROR); - return false; - } - } - - /** - * change the root to a fake root - * @param string fakeRoot - * @return bool - */ - static public function chroot($fakeRoot) { - return self::$defaultInstance->chroot($fakeRoot); - } - - /** - * @brief get the relative path of the root data directory for the current user - * @return string - * - * Returns path like /admin/files - */ - static public function getRoot() { - return self::$defaultInstance->getRoot(); - } - - /** - * clear all mounts and storage backends - */ - public static function clearMounts() { - self::$mounts=array(); - self::$storages=array(); - } - - /** - * mount an \OC\Files\Storage\Storage in our virtual filesystem - * @param \OC\Files\Storage\Storage storage - * @param string mountpoint - */ - static public function mount($class,$arguments,$mountpoint) { - if($mountpoint[0]!='/') { - $mountpoint='/'.$mountpoint; - } - if(substr($mountpoint,-1)!=='/') { - $mountpoint=$mountpoint.'/'; - } - self::$mounts[$mountpoint]=array('class'=>$class,'arguments'=>$arguments); - } - - /** - * return the path to a local version of the file - * we need this because we can't know if a file is stored local or not from outside the filestorage and for some purposes a local file is needed - * @param string path - * @return string - */ - static public function getLocalFile($path) { - return self::$defaultInstance->getLocalFile($path); - } - /** - * @param string path - * @return string - */ - static public function getLocalFolder($path) { - return self::$defaultInstance->getLocalFolder($path); - } - - /** - * return path to file which reflects one visible in browser - * @param string path - * @return string - */ - static public function getLocalPath($path) { - $datadir = OC_User::getHome(OC_User::getUser()).'/files'; - $newpath = $path; - if (strncmp($newpath, $datadir, strlen($datadir)) == 0) { - $newpath = substr($path, strlen($datadir)); - } - return $newpath; - } - - /** - * check if the requested path is valid - * @param string path - * @return bool - */ - static public function isValidPath($path) { - if(!$path || $path[0]!=='/') { - $path='/'.$path; - } - if(strstr($path,'/../') || strrchr($path, '/') === '/..' ) { - return false; - } - return true; - } - - /** - * checks if a file is blacklisted for storage in the filesystem - * Listens to write and rename hooks - * @param array $data from hook - */ - static public function isBlacklisted($data) { - $blacklist = array('.htaccess'); - if (isset($data['path'])) { - $path = $data['path']; - } else if (isset($data['newpath'])) { - $path = $data['newpath']; - } - if (isset($path)) { - $filename = strtolower(basename($path)); - if (in_array($filename, $blacklist)) { - $data['run'] = false; - } - } - } - - /** - * following functions are equivalent to their php builtin equivalents for arguments/return values. - */ - static public function mkdir($path) { - return self::$defaultInstance->mkdir($path); - } - static public function rmdir($path) { - return self::$defaultInstance->rmdir($path); - } - static public function opendir($path) { - return self::$defaultInstance->opendir($path); - } - static public function readdir($path) { - return self::$defaultInstance->readdir($path); - } - static public function is_dir($path) { - return self::$defaultInstance->is_dir($path); - } - static public function is_file($path) { - return self::$defaultInstance->is_file($path); - } - static public function stat($path) { - return self::$defaultInstance->stat($path); - } - static public function filetype($path) { - return self::$defaultInstance->filetype($path); - } - static public function filesize($path) { - return self::$defaultInstance->filesize($path); - } - static public function readfile($path) { - return self::$defaultInstance->readfile($path); - } - /** - * @deprecated Replaced by isReadable() as part of CRUDS - */ - static public function is_readable($path) { - return self::$defaultInstance->is_readable($path); - } - /** - * @deprecated Replaced by isCreatable(), isUpdatable(), isDeletable() as part of CRUDS - */ - static public function is_writable($path) { - return self::$defaultInstance->is_writable($path); - } - static public function isCreatable($path) { - return self::$defaultInstance->isCreatable($path); - } - static public function isReadable($path) { - return self::$defaultInstance->isReadable($path); - } - static public function isUpdatable($path) { - return self::$defaultInstance->isUpdatable($path); - } - static public function isDeletable($path) { - return self::$defaultInstance->isDeletable($path); - } - static public function isSharable($path) { - return self::$defaultInstance->isSharable($path); - } - static public function file_exists($path) { - return self::$defaultInstance->file_exists($path); - } - static public function filectime($path) { - return self::$defaultInstance->filectime($path); - } - static public function filemtime($path) { - return self::$defaultInstance->filemtime($path); - } - static public function touch($path, $mtime=null) { - return self::$defaultInstance->touch($path, $mtime); - } - static public function file_get_contents($path) { - return self::$defaultInstance->file_get_contents($path); - } - static public function file_put_contents($path,$data) { - return self::$defaultInstance->file_put_contents($path,$data); - } - static public function unlink($path) { - return self::$defaultInstance->unlink($path); - } - static public function rename($path1,$path2) { - return self::$defaultInstance->rename($path1,$path2); - } - static public function copy($path1,$path2) { - return self::$defaultInstance->copy($path1,$path2); - } - static public function fopen($path,$mode) { - return self::$defaultInstance->fopen($path,$mode); - } - static public function toTmpFile($path) { - return self::$defaultInstance->toTmpFile($path); - } - static public function fromTmpFile($tmpFile,$path) { - return self::$defaultInstance->fromTmpFile($tmpFile,$path); - } - - static public function getMimeType($path) { - return self::$defaultInstance->getMimeType($path); - } - static public function hash($type,$path, $raw = false) { - return self::$defaultInstance->hash($type,$path, $raw); - } - - static public function free_space($path='/') { - return self::$defaultInstance->free_space($path); - } - - static public function search($query) { - return OC_FileCache::search($query); - } - - /** - * check if a file or folder has been updated since $time - * @param int $time - * @return bool - */ - static public function hasUpdated($path,$time) { - return self::$defaultInstance->hasUpdated($path,$time); - } - - static public function removeETagHook($params, $root = false) { - if (isset($params['path'])) { - $path=$params['path']; - } else { - $path=$params['oldpath']; - } - - if ($root) { // reduce path to the required part of it (no 'username/files') - $fakeRootView = new OC_FilesystemView($root); - $count = 1; - $path=str_replace(OC_App::getStorage("files")->getAbsolutePath($path), "", $fakeRootView->getAbsolutePath($path), $count); - } - - $path = self::normalizePath($path); - OC_Connector_Sabre_Node::removeETagPropertyForPath($path); - } - - /** - * normalize a path - * @param string path - * @param bool $stripTrailingSlash - * @return string - */ - public static function normalizePath($path,$stripTrailingSlash=true) { - if($path=='') { - return '/'; - } - //no windows style slashes - $path=str_replace('\\','/',$path); - //add leading slash - if($path[0]!=='/') { - $path='/'.$path; - } - //remove trainling slash - if($stripTrailingSlash and strlen($path)>1 and substr($path,-1,1)==='/') { - $path=substr($path,0,-1); - } - //remove duplicate slashes - while(strpos($path,'//')!==false) { - $path=str_replace('//','/',$path); - } - //normalize unicode if possible - if(class_exists('Normalizer')) { - $path=Normalizer::normalize($path); - } - return $path; - } -} -OC_Hook::connect('OC_Filesystem','post_write', 'OC_Filesystem','removeETagHook'); -OC_Hook::connect('OC_Filesystem','post_delete','OC_Filesystem','removeETagHook'); -OC_Hook::connect('OC_Filesystem','post_rename','OC_Filesystem','removeETagHook'); - -OC_Util::setupFS(); From 07c53841899262fdd34304302ffaaf2704800c38 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 10 Oct 2012 12:25:46 +0200 Subject: [PATCH 029/272] fix namespaces in filesystem and filesystemview --- lib/app.php | 6 +- lib/files/filesystem.php | 153 ++++++++++++----------- lib/files/view.php | 260 +++++++++++++++++++++------------------ 3 files changed, 225 insertions(+), 194 deletions(-) diff --git a/lib/app.php b/lib/app.php index 395230156f..4d7353e99d 100755 --- a/lib/app.php +++ b/lib/app.php @@ -714,16 +714,16 @@ class OC_App{ /** * @param string $appid - * @return OC_FilesystemView + * @return \OC\Files\View */ public static function getStorage($appid) { if(OC_App::isEnabled($appid)) {//sanity check if(OC_User::isLoggedIn()) { - $view = new OC_FilesystemView('/'.OC_User::getUser()); + $view = new \OC\Files\View('/'.OC_User::getUser()); if(!$view->file_exists($appid)) { $view->mkdir($appid); } - return new OC_FilesystemView('/'.OC_User::getUser().'/'.$appid); + return new \OC\Files\View('/'.OC_User::getUser().'/'.$appid); }else{ OC_Log::write('core', 'Can\'t get app storage, app, user not logged in', OC_Log::ERROR); return false; diff --git a/lib/files/filesystem.php b/lib/files/filesystem.php index d0a96b59a0..f23f3a79e9 100644 --- a/lib/files/filesystem.php +++ b/lib/files/filesystem.php @@ -15,16 +15,16 @@ * read(path) * write(path, &run) * post_write(path) - * create(path, &run) (when a file is created, both create and write will be emited in that order) + * create(path, &run) (when a file is created, both create and write will be emitted in that order) * post_create(path) * delete(path, &run) * post_delete(path) * rename(oldpath,newpath, &run) * post_rename(oldpath,newpath) - * copy(oldpath,newpath, &run) (if the newpath doesn't exists yes, copy, create and write will be emited in that order) + * copy(oldpath,newpath, &run) (if the newpath doesn't exists yes, copy, create and write will be emitted in that order) * post_rename(oldpath,newpath) * - * the &run parameter can be set to false to prevent the operation from occuring + * the &run parameter can be set to false to prevent the operation from occurring */ namespace OC\Files; @@ -34,7 +34,7 @@ class Filesystem { static private $mounts = array(); public static $loaded = false; /** - * @var \OC\Files\Storage\Storage $defaultInstance + * @var \OC\Files\View $defaultInstance */ static private $defaultInstance; @@ -46,80 +46,80 @@ class Filesystem { const CLASSNAME = 'OC_Filesystem'; /** - * signalname emited before file renaming + * signalname emitted before file renaming * - * @param oldpath - * @param newpath + * @param string $oldpath + * @param string $newpath */ const signal_rename = 'rename'; /** - * signal emited after file renaming + * signal emitted after file renaming * - * @param oldpath - * @param newpath + * @param string $oldpath + * @param string $newpath */ const signal_post_rename = 'post_rename'; /** - * signal emited before file/dir creation + * signal emitted before file/dir creation * - * @param path - * @param run changing this flag to false in hook handler will cancel event + * @param string $path + * @param bool $run changing this flag to false in hook handler will cancel event */ const signal_create = 'create'; /** - * signal emited after file/dir creation + * signal emitted after file/dir creation * - * @param path - * @param run changing this flag to false in hook handler will cancel event + * @param string $path + * @param bool $run changing this flag to false in hook handler will cancel event */ const signal_post_create = 'post_create'; /** * signal emits before file/dir copy * - * @param oldpath - * @param newpath - * @param run changing this flag to false in hook handler will cancel event + * @param string $oldpath + * @param string $newpath + * @param bool $run changing this flag to false in hook handler will cancel event */ const signal_copy = 'copy'; /** * signal emits after file/dir copy * - * @param oldpath - * @param newpath + * @param string $oldpath + * @param string $newpath */ const signal_post_copy = 'post_copy'; /** * signal emits before file/dir save * - * @param path - * @param run changing this flag to false in hook handler will cancel event + * @param string $path + * @param bool $run changing this flag to false in hook handler will cancel event */ const signal_write = 'write'; /** * signal emits after file/dir save * - * @param path + * @param string $path */ const signal_post_write = 'post_write'; /** * signal emits when reading file/dir * - * @param path + * @param string $path */ const signal_read = 'read'; /** * signal emits when removing file/dir * - * @param path + * @param string $path */ const signal_delete = 'delete'; @@ -139,11 +139,11 @@ class Filesystem { * get the mountpoint of the storage object for a path ( note: because a storage is not always mounted inside the fakeroot, the returned mountpoint is relative to the absolute root of the filesystem and doesn't take the chroot into account * - * @param string path + * @param string $path * @return string */ static public function getMountPoint($path) { - OC_Hook::emit(self::CLASSNAME, 'get_mountpoint', array('path' => $path)); + \OC_Hook::emit(self::CLASSNAME, 'get_mountpoint', array('path' => $path)); if (!$path) { $path = '/'; } @@ -152,7 +152,7 @@ class Filesystem { } $path = str_replace('//', '/', $path); $foundMountPoint = ''; - $mountPoints = array_keys(OC_Filesystem::$mounts); + $mountPoints = array_keys(self::$mounts); foreach ($mountPoints as $mountpoint) { if ($mountpoint == $path) { return $mountpoint; @@ -167,7 +167,7 @@ class Filesystem { /** * get the part of the path relative to the mountpoint of the storage it's stored in * - * @param string path + * @param string $path * @return bool */ static public function getInternalPath($path) { @@ -179,17 +179,19 @@ class Filesystem { /** * get the storage object for a path * - * @param string path + * @param string $path * @return \OC\Files\Storage\Storage */ static public function getStorage($path) { $mountpoint = self::getMountPoint($path); if ($mountpoint) { - if (!isset(OC_Filesystem::$storages[$mountpoint])) { - $mount = OC_Filesystem::$mounts[$mountpoint]; - OC_Filesystem::$storages[$mountpoint] = OC_Filesystem::createStorage($mount['class'], $mount['arguments']); + if (!isset(self::$storages[$mountpoint])) { + $mount = self::$mounts[$mountpoint]; + self::$storages[$mountpoint] = self::createStorage($mount['class'], $mount['arguments']); } - return OC_Filesystem::$storages[$mountpoint]; + return self::$storages[$mountpoint]; + }else{ + return null; } } @@ -202,13 +204,15 @@ class Filesystem { static public function resolvePath($path) { $mountpoint = self::getMountPoint($path); if ($mountpoint) { - if (!isset(OC_Filesystem::$storages[$mountpoint])) { - $mount = OC_Filesystem::$mounts[$mountpoint]; - OC_Filesystem::$storages[$mountpoint] = OC_Filesystem::createStorage($mount['class'], $mount['arguments']); + if (!isset(self::$storages[$mountpoint])) { + $mount = self::$mounts[$mountpoint]; + self::$storages[$mountpoint] = self::createStorage($mount['class'], $mount['arguments']); } - $storage = OC_Filesystem::$storages[$mountpoint]; + $storage = self::$storages[$mountpoint]; $internalPath = substr($path, strlen($mountpoint)); return array($storage, $internalPath); + }else{ + return array(null, null); } } @@ -216,11 +220,11 @@ class Filesystem { if (self::$defaultInstance) { return false; } - self::$defaultInstance = new OC_FilesystemView($root); + self::$defaultInstance = new View($root); -//load custom mount config - if (is_file(OC::$SERVERROOT . '/config/mount.php')) { - $mountConfig = include(OC::$SERVERROOT . '/config/mount.php'); + //load custom mount config + if (is_file(\OC::$SERVERROOT . '/config/mount.php')) { + $mountConfig = include 'config/mount.php'; if (isset($mountConfig['global'])) { foreach ($mountConfig['global'] as $mountPoint => $options) { self::mount($options['class'], $options['options'], $mountPoint); @@ -229,7 +233,7 @@ class Filesystem { if (isset($mountConfig['group'])) { foreach ($mountConfig['group'] as $group => $mounts) { - if (OC_Group::inGroup(OC_User::getUser(), $group)) { + if (\OC_Group::inGroup(\OC_User::getUser(), $group)) { foreach ($mounts as $mountPoint => $options) { $mountPoint = self::setUserVars($mountPoint); foreach ($options as &$option) { @@ -243,7 +247,7 @@ class Filesystem { if (isset($mountConfig['user'])) { foreach ($mountConfig['user'] as $user => $mounts) { - if ($user === 'all' or strtolower($user) === strtolower(OC_User::getUser())) { + if ($user === 'all' or strtolower($user) === strtolower(\OC_User::getUser())) { foreach ($mounts as $mountPoint => $options) { $mountPoint = self::setUserVars($mountPoint); foreach ($options as &$option) { @@ -257,22 +261,24 @@ class Filesystem { } self::$loaded = true; + + return true; } /** * fill in the correct values for $user, and $password placeholders * - * @param string intput + * @param string $input * @return string */ private static function setUserVars($input) { - return str_replace('$user', OC_User::getUser(), $input); + return str_replace('$user', \OC_User::getUser(), $input); } /** * get the default filesystem view * - * @return OC_FilesystemView + * @return View */ static public function getView() { return self::$defaultInstance; @@ -288,20 +294,20 @@ class Filesystem { /** * create a new storage of a specific type * - * @param string type - * @param array arguments + * @param string $type + * @param array $arguments * @return \OC\Files\Storage\Storage */ static private function createStorage($class, $arguments) { if (class_exists($class)) { try { return new $class($arguments); - } catch (Exception $exception) { - OC_Log::write('core', $exception->getMessage(), OC_Log::ERROR); + } catch (\Exception $exception) { + \OC_Log::write('core', $exception->getMessage(), \OC_Log::ERROR); return false; } } else { - OC_Log::write('core', 'storage backend ' . $class . ' not found', OC_Log::ERROR); + \OC_Log::write('core', 'storage backend ' . $class . ' not found', \OC_Log::ERROR); return false; } } @@ -309,7 +315,7 @@ class Filesystem { /** * change the root to a fake root * - * @param string fakeRoot + * @param string $fakeRoot * @return bool */ static public function chroot($fakeRoot) { @@ -337,8 +343,9 @@ class Filesystem { /** * mount an \OC\Files\Storage\Storage in our virtual filesystem * - * @param \OC\Files\Storage\Storage storage - * @param string mountpoint + * @param \OC\Files\Storage\Storage $storage + * @param array $arguments + * @param string $mountpoint */ static public function mount($class, $arguments, $mountpoint) { if ($mountpoint[0] != '/') { @@ -354,7 +361,7 @@ class Filesystem { * return the path to a local version of the file * we need this because we can't know if a file is stored local or not from outside the filestorage and for some purposes a local file is needed * - * @param string path + * @param string $path * @return string */ static public function getLocalFile($path) { @@ -362,7 +369,7 @@ class Filesystem { } /** - * @param string path + * @param string $path * @return string */ static public function getLocalFolder($path) { @@ -372,11 +379,11 @@ class Filesystem { /** * return path to file which reflects one visible in browser * - * @param string path + * @param string $path * @return string */ static public function getLocalPath($path) { - $datadir = OC_User::getHome(OC_User::getUser()) . '/files'; + $datadir = \OC_User::getHome(\OC_User::getUser()) . '/files'; $newpath = $path; if (strncmp($newpath, $datadir, strlen($datadir)) == 0) { $newpath = substr($path, strlen($datadir)); @@ -387,7 +394,7 @@ class Filesystem { /** * check if the requested path is valid * - * @param string path + * @param string $path * @return bool */ static public function isValidPath($path) { @@ -468,7 +475,7 @@ class Filesystem { * @deprecated Replaced by isReadable() as part of CRUDS */ static public function is_readable($path) { - return self::$defaultInstance->is_readable($path); + return self::$defaultInstance->isReadable($path); } /** @@ -559,7 +566,7 @@ class Filesystem { } static public function search($query) { - return OC_FileCache::search($query); + return Cache\Cache::search($query); } /** @@ -580,19 +587,19 @@ class Filesystem { } if ($root) { // reduce path to the required part of it (no 'username/files') - $fakeRootView = new OC_FilesystemView($root); + $fakeRootView = new View($root); $count = 1; - $path = str_replace(OC_App::getStorage("files")->getAbsolutePath($path), "", $fakeRootView->getAbsolutePath($path), $count); + $path = str_replace(\OC_App::getStorage("files")->getAbsolutePath($path), "", $fakeRootView->getAbsolutePath($path), $count); } $path = self::normalizePath($path); - OC_Connector_Sabre_Node::removeETagPropertyForPath($path); + \OC_Connector_Sabre_Node::removeETagPropertyForPath($path); } /** * normalize a path * - * @param string path + * @param string $path * @param bool $stripTrailingSlash * @return string */ @@ -606,7 +613,7 @@ class Filesystem { if ($path[0] !== '/') { $path = '/' . $path; } -//remove trainling slash +//remove trailing slash if ($stripTrailingSlash and strlen($path) > 1 and substr($path, -1, 1) === '/') { $path = substr($path, 0, -1); } @@ -616,14 +623,14 @@ class Filesystem { } //normalize unicode if possible if (class_exists('Normalizer')) { - $path = Normalizer::normalize($path); + $path = \Normalizer::normalize($path); } return $path; } } -OC_Hook::connect('OC_Filesystem', 'post_write', 'OC_Filesystem', 'removeETagHook'); -OC_Hook::connect('OC_Filesystem', 'post_delete', 'OC_Filesystem', 'removeETagHook'); -OC_Hook::connect('OC_Filesystem', 'post_rename', 'OC_Filesystem', 'removeETagHook'); +\OC_Hook::connect('OC_Filesystem', 'post_write', 'OC_Filesystem', 'removeETagHook'); +\OC_Hook::connect('OC_Filesystem', 'post_delete', 'OC_Filesystem', 'removeETagHook'); +\OC_Hook::connect('OC_Filesystem', 'post_rename', 'OC_Filesystem', 'removeETagHook'); -OC_Util::setupFS(); +\OC_Util::setupFS(); diff --git a/lib/files/view.php b/lib/files/view.php index 8c3f288f44..230455479c 100644 --- a/lib/files/view.php +++ b/lib/files/view.php @@ -45,9 +45,9 @@ class View { } /** - * change the root to a fake toor + * change the root to a fake root * - * @param string fakeRoot + * @param string $fakeRoot * @return bool */ public function chroot($fakeRoot) { @@ -71,12 +71,12 @@ class View { /** * get the part of the path relative to the mountpoint of the storage it's stored in * - * @param string path + * @param string $path * @return bool */ public function getInternalPath($path) { if (!isset($this->internal_path_cache[$path])) { - $this->internal_path_cache[$path] = OC_Filesystem::getInternalPath($this->getAbsolutePath($path)); + $this->internal_path_cache[$path] = Filesystem::getInternalPath($this->getAbsolutePath($path)); } return $this->internal_path_cache[$path]; } @@ -84,7 +84,7 @@ class View { /** * get path relative to the root of the view * - * @param string path + * @param string $path * @return string */ public function getRelativePath($path) { @@ -106,12 +106,12 @@ class View { /** * get the storage object for a path * - * @param string path + * @param string $path * @return \OC\Files\Storage\Storage */ public function getStorage($path) { if (!isset($this->storage_cache[$path])) { - $this->storage_cache[$path] = OC_Filesystem::getStorage($this->getAbsolutePath($path)); + $this->storage_cache[$path] = Filesystem::getStorage($this->getAbsolutePath($path)); } return $this->storage_cache[$path]; } @@ -120,35 +120,39 @@ class View { * get the mountpoint of the storage object for a path ( note: because a storage is not always mounted inside the fakeroot, the returned mountpoint is relative to the absolute root of the filesystem and doesn't take the chroot into account * - * @param string path + * @param string $path * @return string */ public function getMountPoint($path) { - return OC_Filesystem::getMountPoint($this->getAbsolutePath($path)); + return Filesystem::getMountPoint($this->getAbsolutePath($path)); } /** * return the path to a local version of the file * we need this because we can't know if a file is stored local or not from outside the filestorage and for some purposes a local file is needed * - * @param string path + * @param string $path * @return string */ public function getLocalFile($path) { $parent = substr($path, 0, strrpos($path, '/')); - if (OC_Filesystem::isValidPath($parent) and $storage = $this->getStorage($path)) { + if (Filesystem::isValidPath($parent) and $storage = $this->getStorage($path)) { return $storage->getLocalFile($this->getInternalPath($path)); + } else { + return null; } } /** - * @param string path + * @param string $path * @return string */ public function getLocalFolder($path) { $parent = substr($path, 0, strrpos($path, '/')); - if (OC_Filesystem::isValidPath($parent) and $storage = $this->getStorage($path)) { + if (Filesystem::isValidPath($parent) and $storage = $this->getStorage($path)) { return $storage->getLocalFolder($this->getInternalPath($path)); + } else { + return null; } } @@ -274,28 +278,28 @@ class View { public function file_put_contents($path, $data) { if (is_resource($data)) { //not having to deal with streams in file_put_contents makes life easier - $absolutePath = OC_Filesystem::normalizePath($this->getAbsolutePath($path)); - if (OC_FileProxy::runPreProxies('file_put_contents', $absolutePath, $data) && OC_Filesystem::isValidPath($path)) { + $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path)); + if (\OC_FileProxy::runPreProxies('file_put_contents', $absolutePath, $data) && Filesystem::isValidPath($path)) { $path = $this->getRelativePath($absolutePath); $exists = $this->file_exists($path); $run = true; - if ($this->fakeRoot == OC_Filesystem::getRoot()) { + if ($this->fakeRoot == Filesystem::getRoot()) { if (!$exists) { - OC_Hook::emit( - OC_Filesystem::CLASSNAME, - OC_Filesystem::signal_create, + \OC_Hook::emit( + Filesystem::CLASSNAME, + Filesystem::signal_create, array( - OC_Filesystem::signal_param_path => $path, - OC_Filesystem::signal_param_run => &$run + Filesystem::signal_param_path => $path, + Filesystem::signal_param_run => &$run ) ); } - OC_Hook::emit( - OC_Filesystem::CLASSNAME, - OC_Filesystem::signal_write, + \OC_Hook::emit( + Filesystem::CLASSNAME, + Filesystem::signal_write, array( - OC_Filesystem::signal_param_path => $path, - OC_Filesystem::signal_param_run => &$run + Filesystem::signal_param_path => $path, + Filesystem::signal_param_run => &$run ) ); } @@ -304,28 +308,30 @@ class View { } $target = $this->fopen($path, 'w'); if ($target) { - $count = OC_Helper::streamCopy($data, $target); + $count = \OC_Helper::streamCopy($data, $target); fclose($target); fclose($data); - if ($this->fakeRoot == OC_Filesystem::getRoot()) { + if ($this->fakeRoot == Filesystem::getRoot()) { if (!$exists) { - OC_Hook::emit( - OC_Filesystem::CLASSNAME, - OC_Filesystem::signal_post_create, - array(OC_Filesystem::signal_param_path => $path) + \OC_Hook::emit( + Filesystem::CLASSNAME, + Filesystem::signal_post_create, + array(Filesystem::signal_param_path => $path) ); } - OC_Hook::emit( - OC_Filesystem::CLASSNAME, - OC_Filesystem::signal_post_write, - array(OC_Filesystem::signal_param_path => $path) + \OC_Hook::emit( + Filesystem::CLASSNAME, + Filesystem::signal_post_write, + array(Filesystem::signal_param_path => $path) ); } - OC_FileProxy::runPostProxies('file_put_contents', $absolutePath, $count); + \OC_FileProxy::runPostProxies('file_put_contents', $absolutePath, $count); return $count > 0; } else { return false; } + } else { + return false; } } else { return $this->basicOperation('file_put_contents', $path, array('create', 'write'), $data); @@ -343,9 +349,9 @@ class View { public function rename($path1, $path2) { $postFix1 = (substr($path1, -1, 1) === '/') ? '/' : ''; $postFix2 = (substr($path2, -1, 1) === '/') ? '/' : ''; - $absolutePath1 = OC_Filesystem::normalizePath($this->getAbsolutePath($path1)); - $absolutePath2 = OC_Filesystem::normalizePath($this->getAbsolutePath($path2)); - if (OC_FileProxy::runPreProxies('rename', $absolutePath1, $absolutePath2) and OC_Filesystem::isValidPath($path2)) { + $absolutePath1 = Filesystem::normalizePath($this->getAbsolutePath($path1)); + $absolutePath2 = Filesystem::normalizePath($this->getAbsolutePath($path2)); + if (\OC_FileProxy::runPreProxies('rename', $absolutePath1, $absolutePath2) and Filesystem::isValidPath($path2)) { $path1 = $this->getRelativePath($absolutePath1); $path2 = $this->getRelativePath($absolutePath2); @@ -353,13 +359,13 @@ class View { return false; } $run = true; - if ($this->fakeRoot == OC_Filesystem::getRoot()) { - OC_Hook::emit( - OC_Filesystem::CLASSNAME, OC_Filesystem::signal_rename, + if ($this->fakeRoot == Filesystem::getRoot()) { + \OC_Hook::emit( + Filesystem::CLASSNAME, Filesystem::signal_rename, array( - OC_Filesystem::signal_param_oldpath => $path1, - OC_Filesystem::signal_param_newpath => $path2, - OC_Filesystem::signal_param_run => &$run + Filesystem::signal_param_oldpath => $path1, + Filesystem::signal_param_newpath => $path2, + Filesystem::signal_param_run => &$run ) ); } @@ -369,36 +375,42 @@ class View { if ($mp1 == $mp2) { if ($storage = $this->getStorage($path1)) { $result = $storage->rename($this->getInternalPath($path1 . $postFix1), $this->getInternalPath($path2 . $postFix2)); + } else { + $result = false; } } else { $source = $this->fopen($path1 . $postFix1, 'r'); $target = $this->fopen($path2 . $postFix2, 'w'); - $count = OC_Helper::streamCopy($source, $target); + $count = \OC_Helper::streamCopy($source, $target); $storage1 = $this->getStorage($path1); $storage1->unlink($this->getInternalPath($path1 . $postFix1)); $result = $count > 0; } - if ($this->fakeRoot == OC_Filesystem::getRoot()) { - OC_Hook::emit( - OC_Filesystem::CLASSNAME, - OC_Filesystem::signal_post_rename, + if ($this->fakeRoot == Filesystem::getRoot()) { + \OC_Hook::emit( + Filesystem::CLASSNAME, + Filesystem::signal_post_rename, array( - OC_Filesystem::signal_param_oldpath => $path1, - OC_Filesystem::signal_param_newpath => $path2 + Filesystem::signal_param_oldpath => $path1, + Filesystem::signal_param_newpath => $path2 ) ); } return $result; + } else { + return false; } + } else { + return false; } } public function copy($path1, $path2) { $postFix1 = (substr($path1, -1, 1) === '/') ? '/' : ''; $postFix2 = (substr($path2, -1, 1) === '/') ? '/' : ''; - $absolutePath1 = OC_Filesystem::normalizePath($this->getAbsolutePath($path1)); - $absolutePath2 = OC_Filesystem::normalizePath($this->getAbsolutePath($path2)); - if (OC_FileProxy::runPreProxies('copy', $absolutePath1, $absolutePath2) and OC_Filesystem::isValidPath($path2)) { + $absolutePath1 = Filesystem::normalizePath($this->getAbsolutePath($path1)); + $absolutePath2 = Filesystem::normalizePath($this->getAbsolutePath($path2)); + if (\OC_FileProxy::runPreProxies('copy', $absolutePath1, $absolutePath2) and Filesystem::isValidPath($path2)) { $path1 = $this->getRelativePath($absolutePath1); $path2 = $this->getRelativePath($absolutePath2); @@ -406,34 +418,34 @@ class View { return false; } $run = true; - if ($this->fakeRoot == OC_Filesystem::getRoot()) { - OC_Hook::emit( - OC_Filesystem::CLASSNAME, - OC_Filesystem::signal_copy, + $exists = $this->file_exists($path2); + if ($this->fakeRoot == Filesystem::getRoot()) { + \OC_Hook::emit( + Filesystem::CLASSNAME, + Filesystem::signal_copy, array( - OC_Filesystem::signal_param_oldpath => $path1, - OC_Filesystem::signal_param_newpath => $path2, - OC_Filesystem::signal_param_run => &$run + Filesystem::signal_param_oldpath => $path1, + Filesystem::signal_param_newpath => $path2, + Filesystem::signal_param_run => &$run ) ); - $exists = $this->file_exists($path2); if ($run and !$exists) { - OC_Hook::emit( - OC_Filesystem::CLASSNAME, - OC_Filesystem::signal_create, + \OC_Hook::emit( + Filesystem::CLASSNAME, + Filesystem::signal_create, array( - OC_Filesystem::signal_param_path => $path2, - OC_Filesystem::signal_param_run => &$run + Filesystem::signal_param_path => $path2, + Filesystem::signal_param_run => &$run ) ); } if ($run) { - OC_Hook::emit( - OC_Filesystem::CLASSNAME, - OC_Filesystem::signal_write, + \OC_Hook::emit( + Filesystem::CLASSNAME, + Filesystem::signal_write, array( - OC_Filesystem::signal_param_path => $path2, - OC_Filesystem::signal_param_run => &$run + Filesystem::signal_param_path => $path2, + Filesystem::signal_param_run => &$run ) ); } @@ -444,39 +456,45 @@ class View { if ($mp1 == $mp2) { if ($storage = $this->getStorage($path1 . $postFix1)) { $result = $storage->copy($this->getInternalPath($path1 . $postFix1), $this->getInternalPath($path2 . $postFix2)); + } else { + $result = false; } } else { $source = $this->fopen($path1 . $postFix1, 'r'); $target = $this->fopen($path2 . $postFix2, 'w'); - $result = OC_Helper::streamCopy($source, $target); + $result = \OC_Helper::streamCopy($source, $target); } - if ($this->fakeRoot == OC_Filesystem::getRoot()) { - OC_Hook::emit( - OC_Filesystem::CLASSNAME, - OC_Filesystem::signal_post_copy, + if ($this->fakeRoot == Filesystem::getRoot()) { + \OC_Hook::emit( + Filesystem::CLASSNAME, + Filesystem::signal_post_copy, array( - OC_Filesystem::signal_param_oldpath => $path1, - OC_Filesystem::signal_param_newpath => $path2 + Filesystem::signal_param_oldpath => $path1, + Filesystem::signal_param_newpath => $path2 ) ); if (!$exists) { - OC_Hook::emit( - OC_Filesystem::CLASSNAME, - OC_Filesystem::signal_post_create, - array(OC_Filesystem::signal_param_path => $path2) + \OC_Hook::emit( + Filesystem::CLASSNAME, + Filesystem::signal_post_create, + array(Filesystem::signal_param_path => $path2) ); } - OC_Hook::emit( - OC_Filesystem::CLASSNAME, - OC_Filesystem::signal_post_write, - array(OC_Filesystem::signal_param_path => $path2) + \OC_Hook::emit( + Filesystem::CLASSNAME, + Filesystem::signal_post_write, + array(Filesystem::signal_param_path => $path2) ); } else { // no real copy, file comes from somewhere else, e.g. version rollback -> just update the file cache and the webdav properties without all the other post_write actions - OC_FileCache_Update::update($path2, $this->fakeRoot); - OC_Filesystem::removeETagHook(array("path" => $path2), $this->fakeRoot); +// OC_FileCache_Update::update($path2, $this->fakeRoot); + Filesystem::removeETagHook(array("path" => $path2), $this->fakeRoot); } return $result; + } else { + return false; } + } else { + return false; } } @@ -507,14 +525,14 @@ class View { $hooks[] = 'write'; break; default: - OC_Log::write('core', 'invalid mode (' . $mode . ') for ' . $path, OC_Log::ERROR); + \OC_Log::write('core', 'invalid mode (' . $mode . ') for ' . $path, \OC_Log::ERROR); } return $this->basicOperation('fopen', $path, $hooks, $mode); } public function toTmpFile($path) { - if (OC_Filesystem::isValidPath($path)) { + if (Filesystem::isValidPath($path)) { $source = $this->fopen($path, 'r'); if ($source) { $extension = ''; @@ -522,15 +540,19 @@ class View { if ($extOffset !== false) { $extension = substr($path, strrpos($path, '.')); } - $tmpFile = OC_Helper::tmpFile($extension); + $tmpFile = \OC_Helper::tmpFile($extension); file_put_contents($tmpFile, $source); return $tmpFile; + } else { + return false; } + } else { + return false; } } public function fromTmpFile($tmpFile, $path) { - if (OC_Filesystem::isValidPath($path)) { + if (Filesystem::isValidPath($path)) { if (!$tmpFile) { debug_print_backtrace(); } @@ -540,6 +562,7 @@ class View { unlink($tmpFile); return true; } else { + return false; } } else { return false; @@ -552,22 +575,22 @@ class View { public function hash($type, $path, $raw = false) { $postFix = (substr($path, -1, 1) === '/') ? '/' : ''; - $absolutePath = OC_Filesystem::normalizePath($this->getAbsolutePath($path)); - if (OC_FileProxy::runPreProxies('hash', $absolutePath) && OC_Filesystem::isValidPath($path)) { + $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path)); + if (\OC_FileProxy::runPreProxies('hash', $absolutePath) && Filesystem::isValidPath($path)) { $path = $this->getRelativePath($absolutePath); if ($path == null) { return false; } - if (OC_Filesystem::$loaded && $this->fakeRoot == OC_Filesystem::getRoot()) { - OC_Hook::emit( - OC_Filesystem::CLASSNAME, - OC_Filesystem::signal_read, - array(OC_Filesystem::signal_param_path => $path) + if (Filesystem::$loaded && $this->fakeRoot == Filesystem::getRoot()) { + \OC_Hook::emit( + Filesystem::CLASSNAME, + Filesystem::signal_read, + array(Filesystem::signal_param_path => $path) ); } if ($storage = $this->getStorage($path . $postFix)) { $result = $storage->hash($type, $this->getInternalPath($path . $postFix), $raw); - $result = OC_FileProxy::runPostProxies('hash', $absolutePath, $result); + $result = \OC_FileProxy::runPostProxies('hash', $absolutePath, $result); return $result; } } @@ -581,9 +604,9 @@ class View { /** * @brief abstraction layer for basic filesystem functions: wrapper for \OC\Files\Storage\Storage * @param string $operation - * @param string #path - * @param array (optional) hooks - * @param mixed (optional) $extraParam + * @param string $path + * @param array $hooks (optional) + * @param mixed $extraParam (optional) * @return mixed * * This method takes requests for basic filesystem functions (e.g. reading & writing @@ -592,8 +615,8 @@ class View { */ private function basicOperation($operation, $path, $hooks = array(), $extraParam = null) { $postFix = (substr($path, -1, 1) === '/') ? '/' : ''; - $absolutePath = OC_Filesystem::normalizePath($this->getAbsolutePath($path)); - if (OC_FileProxy::runPreProxies($operation, $absolutePath, $extraParam) and OC_Filesystem::isValidPath($path)) { + $absolutePath = Filesystem::normalizePath($this->getAbsolutePath($path)); + if (\OC_FileProxy::runPreProxies($operation, $absolutePath, $extraParam) and Filesystem::isValidPath($path)) { $path = $this->getRelativePath($absolutePath); if ($path == null) { return false; @@ -606,8 +629,8 @@ class View { } else { $result = $storage->$operation($internalPath); } - $result = OC_FileProxy::runPostProxies($operation, $this->getAbsolutePath($path), $result); - if (OC_Filesystem::$loaded and $this->fakeRoot == OC_Filesystem::getRoot()) { + $result = \OC_FileProxy::runPostProxies($operation, $this->getAbsolutePath($path), $result); + if (Filesystem::$loaded and $this->fakeRoot == Filesystem::getRoot()) { if ($operation != 'fopen') { //no post hooks for fopen, the file stream is still open $this->runHooks($hooks, $path, true); } @@ -621,23 +644,23 @@ class View { private function runHooks($hooks, $path, $post = false) { $prefix = ($post) ? 'post_' : ''; $run = true; - if (OC_Filesystem::$loaded and $this->fakeRoot == OC_Filesystem::getRoot()) { + if (Filesystem::$loaded and $this->fakeRoot == Filesystem::getRoot()) { foreach ($hooks as $hook) { if ($hook != 'read') { - OC_Hook::emit( - OC_Filesystem::CLASSNAME, + \OC_Hook::emit( + Filesystem::CLASSNAME, $prefix . $hook, array( - OC_Filesystem::signal_param_run => &$run, - OC_Filesystem::signal_param_path => $path + Filesystem::signal_param_run => &$run, + Filesystem::signal_param_path => $path ) ); } elseif (!$post) { - OC_Hook::emit( - OC_Filesystem::CLASSNAME, + \OC_Hook::emit( + Filesystem::CLASSNAME, $prefix . $hook, array( - OC_Filesystem::signal_param_path => $path + Filesystem::signal_param_path => $path ) ); } @@ -649,6 +672,7 @@ class View { /** * check if a file or folder has been updated since $time * + * @param string $path * @param int $time * @return bool */ From 4b9fbf46e554f8bb368e6cf5a8f75ca5c56e8228 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 10 Oct 2012 12:50:57 +0200 Subject: [PATCH 030/272] add depricated OC_Filessystem for compatibility --- lib/filesystem.php | 479 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 460 insertions(+), 19 deletions(-) diff --git a/lib/filesystem.php b/lib/filesystem.php index b1200f95bf..4189fe36c2 100644 --- a/lib/filesystem.php +++ b/lib/filesystem.php @@ -1,25 +1,466 @@ . -* -*/ + * Copyright (c) 2012 Robin Appelman + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ +/** + * Class for abstraction of filesystem functions + * This class won't call any filesystem functions for itself but but will pass them to the correct OC_Filestorage object + * this class should also handle all the file permission related stuff + * + * Hooks provided: + * read(path) + * write(path, &run) + * post_write(path) + * create(path, &run) (when a file is created, both create and write will be emitted in that order) + * post_create(path) + * delete(path, &run) + * post_delete(path) + * rename(oldpath,newpath, &run) + * post_rename(oldpath,newpath) + * copy(oldpath,newpath, &run) (if the newpath doesn't exists yes, copy, create and write will be emitted in that order) + * post_rename(oldpath,newpath) + * + * the &run parameter can be set to false to prevent the operation from occurring + */ +/** + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + */ +class OC_Filesystem { + /** + * get the mountpoint of the storage object for a path + ( note: because a storage is not always mounted inside the fakeroot, the returned mountpoint is relative to the absolute root of the filesystem and doesn't take the chroot into account + * + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + * @param string $path + * @return string + */ + static public function getMountPoint($path) { + return \OC\Files\Filesystem::getMountPoint($path); + } + /** + * get the part of the path relative to the mountpoint of the storage it's stored in + * + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + * @param string $path + * @return bool + */ + static public function getInternalPath($path) { + return \OC\Files\Filesystem::getInternalPath($path); + } + + /** + * get the storage object for a path + * + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + * @param string $path + * @return \OC\Files\Storage\Storage + */ + static public function getStorage($path) { + return \OC\Files\Filesystem::getStorage($path); + } + + /** + * resolve a path to a storage and internal path + * + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + * @param string $path + * @return array consisting of the storage and the internal path + */ + static public function resolvePath($path) { + return \OC\Files\Filesystem::resolvePath($path); + } + + /** + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + */ + static public function init($root) { + return \OC\Files\Filesystem::init($root); + } + + /** + * get the default filesystem view + * + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + * @return \OC\Files\View + */ + static public function getView() { + return \OC\Files\Filesystem::getView(); + } + + /** + * tear down the filesystem, removing all storage providers + * + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + */ + static public function tearDown() { + \OC\Files\Filesystem::tearDown(); + } + + /** + * change the root to a fake root + * + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + * @param string $fakeRoot + * @return bool + */ + static public function chroot($fakeRoot) { + return \OC\Files\Filesystem::chroot($fakeRoot); + } + + /** + * @brief get the relative path of the root data directory for the current user + * @return string + * + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + * Returns path like /admin/files + */ + static public function getRoot() { + return \OC\Files\Filesystem::getRoot(); + } + + /** + * clear all mounts and storage backends + * + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + */ + public static function clearMounts() { + \OC\Files\Filesystem::clearMounts(); + } + + /** + * mount an \OC\Files\Storage\Storage in our virtual filesystem + * + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + * @param \OC\Files\Storage\Storage $class + * @param array $arguments + * @param string $mountpoint + */ + static public function mount($class, $arguments, $mountpoint) { + \OC\Files\Filesystem::mount($class, $arguments, $mountpoint); + } + + /** + * return the path to a local version of the file + * we need this because we can't know if a file is stored local or not from outside the filestorage and for some purposes a local file is needed + * + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + * @param string $path + * @return string + */ + static public function getLocalFile($path) { + return \OC\Files\Filesystem::getLocalFile($path); + } + + /** + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + * @param string $path + * @return string + */ + static public function getLocalFolder($path) { + return \OC\Files\Filesystem::getLocalFolder($path); + } + + /** + * return path to file which reflects one visible in browser + * + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + * @param string $path + * @return string + */ + static public function getLocalPath($path) { + return \OC\Files\Filesystem::getLocalPath($path); + } + + /** + * check if the requested path is valid + * + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + * @param string $path + * @return bool + */ + static public function isValidPath($path) { + return \OC\Files\Filesystem::isValidPath($path); + } + + /** + * checks if a file is blacklisted for storage in the filesystem + * Listens to write and rename hooks + * + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + * @param array $data from hook + */ + static public function isBlacklisted($data) { + \OC\Files\Filesystem::isBlacklisted($data); + } + + /** + * following functions are equivalent to their php builtin equivalents for arguments/return values. + * + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + */ + static public function mkdir($path) { + return \OC\Files\Filesystem::mkdir($path); + } + + /** + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + */ + static public function rmdir($path) { + return \OC\Files\Filesystem::rmdir($path); + } + + /** + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + */ + static public function opendir($path) { + return \OC\Files\Filesystem::opendir($path); + } + + /** + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + */ + static public function readdir($path) { + return \OC\Files\Filesystem::readdir($path); + } + + /** + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + */ + static public function is_dir($path) { + return \OC\Files\Filesystem::is_dir($path); + } + + /** + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + */ + static public function is_file($path) { + return \OC\Files\Filesystem::is_file($path); + } + + /** + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + */ + static public function stat($path) { + return \OC\Files\Filesystem::stat($path); + } + + /** + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + */ + static public function filetype($path) { + return \OC\Files\Filesystem::filetype($path); + } + + /** + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + */ + static public function filesize($path) { + return \OC\Files\Filesystem::filesize($path); + } + + /** + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + */ + static public function readfile($path) { + return \OC\Files\Filesystem::readfile($path); + } + + /** + * @deprecated Replaced by isReadable() as part of CRUDS + */ + static public function is_readable($path) { + return \OC\Files\Filesystem::isReadable($path); + } + + /** + * @deprecated Replaced by isCreatable(), isUpdatable(), isDeletable() as part of CRUDS + */ + static public function is_writable($path) { + return \OC\Files\Filesystem::is_writable($path); + } + + /** + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + */ + static public function isCreatable($path) { + return \OC\Files\Filesystem::isCreatable($path); + } + + /** + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + */ + static public function isReadable($path) { + return \OC\Files\Filesystem::isReadable($path); + } + + /** + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + */ + static public function isUpdatable($path) { + return \OC\Files\Filesystem::isUpdatable($path); + } + + /** + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + */ + static public function isDeletable($path) { + return \OC\Files\Filesystem::isDeletable($path); + } + + /** + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + */ + static public function isSharable($path) { + return \OC\Files\Filesystem::isSharable($path); + } + + /** + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + */ + static public function file_exists($path) { + return \OC\Files\Filesystem::file_exists($path); + } + + /** + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + */ + static public function filectime($path) { + return \OC\Files\Filesystem::filectime($path); + } + + /** + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + */ + static public function filemtime($path) { + return \OC\Files\Filesystem::filemtime($path); + } + + /** + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + */ + static public function touch($path, $mtime = null) { + return \OC\Files\Filesystem::touch($path, $mtime); + } + + /** + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + */ + static public function file_get_contents($path) { + return \OC\Files\Filesystem::file_get_contents($path); + } + + /** + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + */ + static public function file_put_contents($path, $data) { + return \OC\Files\Filesystem::file_put_contents($path, $data); + } + + /** + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + */ + static public function unlink($path) { + return \OC\Files\Filesystem::unlink($path); + } + + /** + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + */ + static public function rename($path1, $path2) { + return \OC\Files\Filesystem::rename($path1, $path2); + } + + /** + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + */ + static public function copy($path1, $path2) { + return \OC\Files\Filesystem::copy($path1, $path2); + } + + /** + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + */ + static public function fopen($path, $mode) { + return \OC\Files\Filesystem::fopen($path, $mode); + } + + /** + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + */ + static public function toTmpFile($path) { + return \OC\Files\Filesystem::toTmpFile($path); + } + + /** + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + */ + static public function fromTmpFile($tmpFile, $path) { + return \OC\Files\Filesystem::fromTmpFile($tmpFile, $path); + } + + /** + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + */ + static public function getMimeType($path) { + return \OC\Files\Filesystem::getMimeType($path); + } + + /** + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + */ + static public function hash($type, $path, $raw = false) { + return \OC\Files\Filesystem::hash($type, $path, $raw); + } + + /** + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + */ + static public function free_space($path = '/') { + return \OC\Files\Filesystem::free_space($path); + } + + /** + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + */ + static public function search($query) { + return \OC\Files\Filesystem::search($query); + } + + /** + * check if a file or folder has been updated since $time + * + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + * @param string $path + * @param int $time + * @return bool + */ + static public function hasUpdated($path, $time) { + return \OC\Files\Filesystem::hasUpdated($path, $time); + } + + /** + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + */ + static public function removeETagHook($params, $root = false) { + \OC\Files\Filesystem::removeETagHook($params, $root); + } + + /** + * normalize a path + * + * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem + * @param string $path + * @param bool $stripTrailingSlash + * @return string + */ + public static function normalizePath($path, $stripTrailingSlash = true) { + return \OC\Files\Filesystem::normalizePath($path, $stripTrailingSlash); + } +} From c88c54bbb054fe2d79b3a93604989d527b5dd444 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 10 Oct 2012 12:51:15 +0200 Subject: [PATCH 031/272] make sure we can do our tests again --- lib/cache/file.php | 4 ++-- tests/lib/cache/file.php | 2 +- tests/lib/filesystem.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/cache/file.php b/lib/cache/file.php index 27d8b19f36..f9ecf41dca 100644 --- a/lib/cache/file.php +++ b/lib/cache/file.php @@ -15,11 +15,11 @@ class OC_Cache_File{ } if(OC_User::isLoggedIn()) { $subdir = 'cache'; - $view = new OC_FilesystemView('/'.OC_User::getUser()); + $view = new \OC\Files\View('/'.OC_User::getUser()); if(!$view->file_exists($subdir)) { $view->mkdir($subdir); } - $this->storage = new OC_FilesystemView('/'.OC_User::getUser().'/'.$subdir); + $this->storage = new \OC\Files\View('/'.OC_User::getUser().'/'.$subdir); return $this->storage; }else{ OC_Log::write('core', 'Can\'t get cache storage, user not logged in', OC_Log::ERROR); diff --git a/tests/lib/cache/file.php b/tests/lib/cache/file.php index 1dd1ff7fa8..3e8fd8c1bb 100644 --- a/tests/lib/cache/file.php +++ b/tests/lib/cache/file.php @@ -51,7 +51,7 @@ class Test_Cache_File extends Test_Cache { OC_User::setUserId('test'); //set up the users dir - $rootView=new OC_FilesystemView(''); + $rootView=new \OC\Files\View(''); $rootView->mkdir('/test'); $this->instance=new OC_Cache_File(); diff --git a/tests/lib/filesystem.php b/tests/lib/filesystem.php index 8fc3ce641d..b402b86af1 100644 --- a/tests/lib/filesystem.php +++ b/tests/lib/filesystem.php @@ -84,7 +84,7 @@ class Test_Filesystem extends UnitTestCase { OC_Filesystem::mount('OC\Files\Storage\Temporary', array(), '/'); - $rootView=new OC_FilesystemView(''); + $rootView=new \OC\Files\View(''); $rootView->mkdir('/'.$user); $rootView->mkdir('/'.$user.'/files'); From aaa1b733642c41821a53bc6d04fab246bfe7f1e6 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 10 Oct 2012 13:18:36 +0200 Subject: [PATCH 032/272] don't use depricated OC_Filesystem --- apps/files/ajax/autocomplete.php | 6 +- apps/files/ajax/newfile.php | 6 +- apps/files/ajax/upload.php | 4 +- apps/files/appinfo/filesync.php | 4 +- apps/files/download.php | 8 +-- apps/files/index.php | 14 ++-- apps/files_encryption/lib/crypt.php | 6 +- apps/files_encryption/lib/cryptstream.php | 2 +- apps/files_encryption/lib/proxy.php | 6 +- apps/files_encryption/tests/proxy.php | 30 ++++----- .../ajax/addRootCertificate.php | 12 ++-- apps/files_external/lib/config.php | 12 ++-- apps/files_sharing/lib/sharedstorage.php | 66 +++++++++---------- apps/files_sharing/public.php | 8 +-- apps/files_versions/lib/hooks.php | 4 +- apps/files_versions/lib/versions.php | 16 ++--- lib/connector/sabre/directory.php | 10 +-- lib/connector/sabre/file.php | 10 +-- lib/connector/sabre/node.php | 8 +-- lib/filechunking.php | 38 +++++------ lib/fileproxy/quota.php | 2 +- lib/files.php | 54 +++++++-------- lib/files/file.php | 2 +- lib/helper.php | 2 +- lib/image.php | 2 +- lib/ocs.php | 4 +- lib/public/files.php | 2 +- lib/public/share.php | 6 +- lib/util.php | 10 +-- settings/personal.php | 2 +- tests/lib/cache/file.php | 4 +- tests/lib/filesystem.php | 56 ++++++++-------- 32 files changed, 209 insertions(+), 207 deletions(-) diff --git a/apps/files/ajax/autocomplete.php b/apps/files/ajax/autocomplete.php index fae38368a8..d0dab9c2bd 100644 --- a/apps/files/ajax/autocomplete.php +++ b/apps/files/ajax/autocomplete.php @@ -33,8 +33,8 @@ $query=strtolower($query); $files=array(); -if(OC_Filesystem::file_exists($base) and OC_Filesystem::is_dir($base)) { - $dh = OC_Filesystem::opendir($base); +if(\OC\Files\Filesystem::file_exists($base) and \OC\Files\Filesystem::is_dir($base)) { + $dh = \OC\Files\Filesystem::opendir($base); if($dh) { if(substr($base, -1, 1)!='/') { $base=$base.'/'; @@ -43,7 +43,7 @@ if(OC_Filesystem::file_exists($base) and OC_Filesystem::is_dir($base)) { if ($file != "." && $file != "..") { if(substr(strtolower($file), 0, $queryLen)==$query) { $item=$base.$file; - if((!$dirOnly or OC_Filesystem::is_dir($item))) { + if((!$dirOnly or \OC\Files\Filesystem::is_dir($item))) { $files[]=(object)array('id'=>$item,'label'=>$item,'name'=>$item); } } diff --git a/apps/files/ajax/newfile.php b/apps/files/ajax/newfile.php index 77d866979c..4d73970b68 100644 --- a/apps/files/ajax/newfile.php +++ b/apps/files/ajax/newfile.php @@ -63,12 +63,12 @@ if($source) { $ctx = stream_context_create(null, array('notification' =>'progress')); $sourceStream=fopen($source, 'rb', false, $ctx); $target=$dir.'/'.$filename; - $result=OC_Filesystem::file_put_contents($target, $sourceStream); + $result=\OC\Files\Filesystem::file_put_contents($target, $sourceStream); if($result) { $meta = OC_FileCache::get($target); $mime=$meta['mimetype']; $id = OC_FileCache::getId($target); - $eventSource->send('success', array('mime'=>$mime, 'size'=>OC_Filesystem::filesize($target), 'id' => $id)); + $eventSource->send('success', array('mime'=>$mime, 'size'=>\OC\Files\Filesystem::filesize($target), 'id' => $id)); } else { $eventSource->send('error', "Error while downloading ".$source. ' to '.$target); } @@ -76,7 +76,7 @@ if($source) { exit(); } else { if($content) { - if(OC_Filesystem::file_put_contents($dir.'/'.$filename, $content)) { + if(\OC\Files\Filesystem::file_put_contents($dir.'/'.$filename, $content)) { $meta = OC_FileCache::get($dir.'/'.$filename); $id = OC_FileCache::getId($dir.'/'.$filename); OCP\JSON::success(array("data" => array('content'=>$content, 'id' => $id))); diff --git a/apps/files/ajax/upload.php b/apps/files/ajax/upload.php index a4dcd80a2e..8bf7f10e18 100644 --- a/apps/files/ajax/upload.php +++ b/apps/files/ajax/upload.php @@ -38,7 +38,7 @@ $totalSize=0; foreach($files['size'] as $size) { $totalSize+=$size; } -if($totalSize>OC_Filesystem::free_space('/')) { +if($totalSize>\OC\Files\Filesystem::free_space('/')) { OCP\JSON::error(array("data" => array( "message" => "Not enough space available" ))); exit(); } @@ -48,7 +48,7 @@ if(strpos($dir, '..') === false) { $fileCount=count($files['name']); for($i=0;$i<$fileCount;$i++) { $target = OCP\Files::buildNotExistingFileName(stripslashes($dir), $files['name'][$i]); - if(is_uploaded_file($files['tmp_name'][$i]) and OC_Filesystem::fromTmpFile($files['tmp_name'][$i], $target)) { + if(is_uploaded_file($files['tmp_name'][$i]) and \OC\Files\Filesystem::fromTmpFile($files['tmp_name'][$i], $target)) { $meta = OC_FileCache::get($target); $id = OC_FileCache::getId($target); $result[]=array( "status" => "success", 'mime'=>$meta['mimetype'],'size'=>$meta['size'], 'id'=>$id, 'name'=>basename($target)); diff --git a/apps/files/appinfo/filesync.php b/apps/files/appinfo/filesync.php index c1fe444cec..e755771de6 100644 --- a/apps/files/appinfo/filesync.php +++ b/apps/files/appinfo/filesync.php @@ -43,7 +43,7 @@ if ($type != 'oc_chunked') { die; } -if (!OC_Filesystem::is_file($file)) { +if (!\OC\Files\Filesystem::is_file($file)) { OC_Response::setStatus(OC_Response::STATUS_NOT_FOUND); die; } @@ -51,7 +51,7 @@ if (!OC_Filesystem::is_file($file)) { switch($_SERVER['REQUEST_METHOD']) { case 'PUT': $input = fopen("php://input", "r"); - $org_file = OC_Filesystem::fopen($file, 'rb'); + $org_file = \OC\Files\Filesystem::fopen($file, 'rb'); $info = array( 'name' => basename($file), ); diff --git a/apps/files/download.php b/apps/files/download.php index ff6aefbbe0..b00a50a045 100644 --- a/apps/files/download.php +++ b/apps/files/download.php @@ -29,7 +29,7 @@ OCP\User::checkLoggedIn(); $filename = $_GET["file"]; -if(!OC_Filesystem::file_exists($filename)) { +if(!\OC\Files\Filesystem::file_exists($filename)) { header("HTTP/1.0 404 Not Found"); $tmpl = new OCP\Template( '', '404', 'guest' ); $tmpl->assign('file',$filename); @@ -37,12 +37,12 @@ if(!OC_Filesystem::file_exists($filename)) { exit; } -$ftype=OC_Filesystem::getMimeType( $filename ); +$ftype=\OC\Files\Filesystem::getMimeType( $filename ); header('Content-Type:'.$ftype); header('Content-Disposition: attachment; filename="'.basename($filename).'"'); OCP\Response::disableCaching(); -header('Content-Length: '.OC_Filesystem::filesize($filename)); +header('Content-Length: '.\OC\Files\Filesystem::filesize($filename)); @ob_end_clean(); -OC_Filesystem::readfile( $filename ); +\OC\Files\Filesystem::readfile( $filename ); diff --git a/apps/files/index.php b/apps/files/index.php index 493087d26f..b02aaf81c0 100644 --- a/apps/files/index.php +++ b/apps/files/index.php @@ -38,7 +38,7 @@ OCP\App::setActiveNavigationEntry( 'files_index' ); // Load the files $dir = isset( $_GET['dir'] ) ? stripslashes($_GET['dir']) : ''; // Redirect if directory does not exist -if(!OC_Filesystem::is_dir($dir.'/')) { +if(!\OC\Files\Filesystem::is_dir($dir.'/')) { header('Location: '.$_SERVER['SCRIPT_NAME'].''); exit(); } @@ -85,26 +85,26 @@ $upload_max_filesize = OCP\Util::computerFileSize(ini_get('upload_max_filesize') $post_max_size = OCP\Util::computerFileSize(ini_get('post_max_size')); $maxUploadFilesize = min($upload_max_filesize, $post_max_size); -$freeSpace=OC_Filesystem::free_space('/'); +$freeSpace=\OC\Files\Filesystem::free_space('/'); $freeSpace=max($freeSpace,0); $maxUploadFilesize = min($maxUploadFilesize ,$freeSpace); $permissions = OCP\Share::PERMISSION_READ; -if (OC_Filesystem::isUpdatable($dir.'/')) { +if (\OC\Files\Filesystem::isUpdatable($dir.'/')) { $permissions |= OCP\Share::PERMISSION_UPDATE; } -if (OC_Filesystem::isDeletable($dir.'/')) { +if (\OC\Files\Filesystem::isDeletable($dir.'/')) { $permissions |= OCP\Share::PERMISSION_DELETE; } -if (OC_Filesystem::isSharable($dir.'/')) { +if (\OC\Files\Filesystem::isSharable($dir.'/')) { $permissions |= OCP\Share::PERMISSION_SHARE; } $tmpl = new OCP\Template( 'files', 'index', 'user' ); $tmpl->assign( 'fileList', $list->fetchPage(), false ); $tmpl->assign( 'breadcrumb', $breadcrumbNav->fetchPage(), false ); -$tmpl->assign( 'dir', OC_Filesystem::normalizePath($dir)); -$tmpl->assign( 'isCreatable', OC_Filesystem::isCreatable($dir.'/')); +$tmpl->assign( 'dir', \OC\Files\Filesystem::normalizePath($dir)); +$tmpl->assign( 'isCreatable', \OC\Files\Filesystem::isCreatable($dir.'/')); $tmpl->assign('permissions', $permissions); $tmpl->assign( 'files', $files ); $tmpl->assign( 'uploadMaxFilesize', $maxUploadFilesize); diff --git a/apps/files_encryption/lib/crypt.php b/apps/files_encryption/lib/crypt.php index 38d8edf28c..6bee8accf7 100644 --- a/apps/files_encryption/lib/crypt.php +++ b/apps/files_encryption/lib/crypt.php @@ -44,7 +44,7 @@ class OC_Crypt { } public static function init($login,$password) { - $view=new OC_FilesystemView('/'); + $view=new \OC\Files\View('/'); if(!$view->file_exists('/'.$login)) { $view->mkdir('/'.$login); } @@ -90,7 +90,7 @@ class OC_Crypt { // Write the file $proxyEnabled=OC_FileProxy::$enabled; OC_FileProxy::$enabled=false; - $view=new OC_FilesystemView('/'.$username); + $view=new \OC\Files\View('/'.$username); $view->file_put_contents('/encryption.key',$enckey); OC_FileProxy::$enabled=$proxyEnabled; } @@ -98,7 +98,7 @@ class OC_Crypt { public static function changekeypasscode($oldPassword, $newPassword) { if(OCP\User::isLoggedIn()) { $username=OCP\USER::getUser(); - $view=new OC_FilesystemView('/'.$username); + $view=new \OC\Files\View('/'.$username); // read old key $key=$view->file_get_contents('/encryption.key'); diff --git a/apps/files_encryption/lib/cryptstream.php b/apps/files_encryption/lib/cryptstream.php index 721a1b955d..89a071327a 100644 --- a/apps/files_encryption/lib/cryptstream.php +++ b/apps/files_encryption/lib/cryptstream.php @@ -38,7 +38,7 @@ class OC_CryptStream{ public function stream_open($path, $mode, $options, &$opened_path) { if(!self::$rootView) { - self::$rootView=new OC_FilesystemView(''); + self::$rootView=new \OC\Files\View(''); } $path=str_replace('crypt://','',$path); if(dirname($path)=='streams' and isset(self::$sourceStreams[basename($path)])) { diff --git a/apps/files_encryption/lib/proxy.php b/apps/files_encryption/lib/proxy.php index f61cd1e377..27abe3bb19 100644 --- a/apps/files_encryption/lib/proxy.php +++ b/apps/files_encryption/lib/proxy.php @@ -90,13 +90,13 @@ class OC_FileProxy_Encryption extends OC_FileProxy{ fclose($result); $result=fopen('crypt://'.$path,$meta['mode']); }elseif(self::shouldEncrypt($path) and $meta['mode']!='r' and $meta['mode']!='rb') { - if(OC_Filesystem::file_exists($path) and OC_Filesystem::filesize($path)>0) { + if(\OC\Files\Filesystem::file_exists($path) and \OC\Files\Filesystem::filesize($path)>0) { //first encrypt the target file so we don't end up with a half encrypted file OCP\Util::writeLog('files_encryption','Decrypting '.$path.' before writing',OCP\Util::DEBUG); - $tmp=fopen('php://temp'); + $tmp=fopen('php://temp', 'w+'); OCP\Files::streamCopy($result,$tmp); fclose($result); - OC_Filesystem::file_put_contents($path,$tmp); + \OC\Files\Filesystem::file_put_contents($path,$tmp); fclose($tmp); } $result=fopen('crypt://'.$path,$meta['mode']); diff --git a/apps/files_encryption/tests/proxy.php b/apps/files_encryption/tests/proxy.php index d600bbc407..c04e0e5c4f 100644 --- a/apps/files_encryption/tests/proxy.php +++ b/apps/files_encryption/tests/proxy.php @@ -29,13 +29,13 @@ class Test_CryptProxy extends UnitTestCase { OC_FileProxy::register(new OC_FileProxy_Encryption()); //set up temporary storage - OC_Filesystem::clearMounts(); - OC_Filesystem::mount('\OC\Files\Storage\Temporary',array(),'/'); + \OC\Files\Filesystem::clearMounts(); + \OC\Files\Filesystem::mount('\OC\Files\Storage\Temporary',array(),'/'); - OC_Filesystem::init('/'.$user.'/files'); + \OC\Files\Filesystem::init('/'.$user.'/files'); //set up the users home folder in the temp storage - $rootView=new OC_FilesystemView(''); + $rootView=new \OC\Files\View(''); $rootView->mkdir('/'.$user); $rootView->mkdir('/'.$user.'/files'); } @@ -51,13 +51,13 @@ class Test_CryptProxy extends UnitTestCase { $file=OC::$SERVERROOT.'/3rdparty/MDB2.php'; $original=file_get_contents($file); - OC_Filesystem::file_put_contents('/file',$original); + \OC\Files\Filesystem::file_put_contents('/file',$original); OC_FileProxy::$enabled=false; - $stored=OC_Filesystem::file_get_contents('/file'); + $stored=\OC\Files\Filesystem::file_get_contents('/file'); OC_FileProxy::$enabled=true; - $fromFile=OC_Filesystem::file_get_contents('/file'); + $fromFile=\OC\Files\Filesystem::file_get_contents('/file'); $this->assertNotEqual($original,$stored); $this->assertEqual(strlen($original),strlen($fromFile)); $this->assertEqual($original,$fromFile); @@ -68,8 +68,8 @@ class Test_CryptProxy extends UnitTestCase { $file=OC::$SERVERROOT.'/3rdparty/MDB2.php'; $original=file_get_contents($file); - $rootView=new OC_FilesystemView(''); - $view=new OC_FilesystemView('/'.OC_User::getUser()); + $rootView=new \OC\Files\View(''); + $view=new \OC\Files\View('/'.OC_User::getUser()); $userDir='/'.OC_User::getUser().'/files'; $rootView->file_put_contents($userDir.'/file',$original); @@ -90,13 +90,13 @@ class Test_CryptProxy extends UnitTestCase { $file=__DIR__.'/binary'; $original=file_get_contents($file); - OC_Filesystem::file_put_contents('/file',$original); + \OC\Files\Filesystem::file_put_contents('/file',$original); OC_FileProxy::$enabled=false; - $stored=OC_Filesystem::file_get_contents('/file'); + $stored=\OC\Files\Filesystem::file_get_contents('/file'); OC_FileProxy::$enabled=true; - $fromFile=OC_Filesystem::file_get_contents('/file'); + $fromFile=\OC\Files\Filesystem::file_get_contents('/file'); $this->assertNotEqual($original,$stored); $this->assertEqual(strlen($original),strlen($fromFile)); $this->assertEqual($original,$fromFile); @@ -104,13 +104,13 @@ class Test_CryptProxy extends UnitTestCase { $file=__DIR__.'/zeros'; $original=file_get_contents($file); - OC_Filesystem::file_put_contents('/file',$original); + \OC\Files\Filesystem::file_put_contents('/file',$original); OC_FileProxy::$enabled=false; - $stored=OC_Filesystem::file_get_contents('/file'); + $stored=\OC\Files\Filesystem::file_get_contents('/file'); OC_FileProxy::$enabled=true; - $fromFile=OC_Filesystem::file_get_contents('/file'); + $fromFile=\OC\Files\Filesystem::file_get_contents('/file'); $this->assertNotEqual($original,$stored); $this->assertEqual(strlen($original),strlen($fromFile)); } diff --git a/apps/files_external/ajax/addRootCertificate.php b/apps/files_external/ajax/addRootCertificate.php index e0a0239c95..6fef9aac1e 100644 --- a/apps/files_external/ajax/addRootCertificate.php +++ b/apps/files_external/ajax/addRootCertificate.php @@ -3,23 +3,23 @@ OCP\JSON::checkAppEnabled('files_external'); if ( !($filename = $_FILES['rootcert_import']['name']) ) { - header("Location: settings/personal.php"); + header("Location: settings/personal.php"); exit; } -$fh = fopen($_FILES['rootcert_import']['tmp_name'], 'r'); -$data = fread($fh, filesize($_FILES['rootcert_import']['tmp_name'])); +$fh = fopen($_FILES['rootcert_import']['tmp_name'], 'r'); +$data = fread($fh, filesize($_FILES['rootcert_import']['tmp_name'])); fclose($fh); $filename = $_FILES['rootcert_import']['name']; - -$view = new \OC_FilesystemView('/'.\OCP\User::getUser().'/files_external/uploads'); + +$view = new \OC\Files\View('/'.\OCP\User::getUser().'/files_external/uploads'); if (!$view->file_exists('')) $view->mkdir(''); $isValid = openssl_pkey_get_public($data); //maybe it was just the wrong file format, try to convert it... if ($isValid == false) { - $data = chunk_split(base64_encode($data), 64, "\n"); + $data = chunk_split(base64_encode($data), 64, "\n"); $data = "-----BEGIN CERTIFICATE-----\n".$data."-----END CERTIFICATE-----\n"; $isValid = openssl_pkey_get_public($data); } diff --git a/apps/files_external/lib/config.php b/apps/files_external/lib/config.php index cb32fed203..7be2336019 100755 --- a/apps/files_external/lib/config.php +++ b/apps/files_external/lib/config.php @@ -109,10 +109,10 @@ class OC_Mount_Config { return $personal; } - /** - * Add directory for mount point to the filesystem - * @param OC_Fileview instance $view - * @param string path to mount point + /** + * Add directory for mount point to the filesystem + * @param OC_Fileview instance $view + * @param string path to mount point */ private static function addMountPointDirectory($view, $path) { $dir = ''; @@ -142,11 +142,11 @@ class OC_Mount_Config { if ($applicable != OCP\User::getUser() || $class == '\OC\Files\Storage\Local') { return false; } - $view = new OC_FilesystemView('/'.OCP\User::getUser().'/files'); + $view = new \OC\Files\View('/'.OCP\User::getUser().'/files'); self::addMountPointDirectory($view, ltrim($mountPoint, '/')); $mountPoint = '/'.$applicable.'/files/'.ltrim($mountPoint, '/'); } else { - $view = new OC_FilesystemView('/'); + $view = new \OC\Files\View('/'); switch ($mountType) { case 'user': if ($applicable == "all") { diff --git a/apps/files_sharing/lib/sharedstorage.php b/apps/files_sharing/lib/sharedstorage.php index 876e719956..e17c4b6e04 100644 --- a/apps/files_sharing/lib/sharedstorage.php +++ b/apps/files_sharing/lib/sharedstorage.php @@ -80,7 +80,7 @@ class Shared extends \OC\Files\Storage\Common { $file = $this->getFile($target); if (isset($file['path'])) { $uid = substr($file['path'], 1, strpos($file['path'], '/', 1) - 1); - \OC_Filesystem::mount('\OC\Files\Storage\Local', array('datadir' => \OC_User::getHome($uid)), $uid); + \\OC\Files\Filesystem::mount('\OC\Files\Storage\Local', array('datadir' => \OC_User::getHome($uid)), $uid); return $file['path']; } return false; @@ -105,7 +105,7 @@ class Shared extends \OC\Files\Storage\Common { * @return Source file path with mount point stripped out */ private function getInternalPath($path) { - $mountPoint = \OC_Filesystem::getMountPoint($path); + $mountPoint = \OC\Files\Filesystem::getMountPoint($path); $internalPath = substr($path, strlen($mountPoint)); return $internalPath; } @@ -114,7 +114,7 @@ class Shared extends \OC\Files\Storage\Common { if ($path == '' || $path == '/' || !$this->isCreatable(dirname($path))) { return false; } else if ($source = $this->getSourcePath($path)) { - $storage = \OC_Filesystem::getStorage($source); + $storage = \OC\Files\Filesystem::getStorage($source); return $storage->mkdir($this->getInternalPath($source)); } return false; @@ -122,7 +122,7 @@ class Shared extends \OC\Files\Storage\Common { public function rmdir($path) { if (($source = $this->getSourcePath($path)) && $this->isDeletable($path)) { - $storage = \OC_Filesystem::getStorage($source); + $storage = \OC\Files\Filesystem::getStorage($source); return $storage->rmdir($this->getInternalPath($source)); } return false; @@ -130,11 +130,11 @@ class Shared extends \OC\Files\Storage\Common { public function opendir($path) { if ($path == '' || $path == '/') { - $files = OCP\Share::getItemsSharedWith('file', \OC_Share_Backend_Folder::FORMAT_OPENDIR); + $files = \OCP\Share::getItemsSharedWith('file', \OC_Share_Backend_Folder::FORMAT_OPENDIR); \OC_FakeDirStream::$dirs['shared'] = $files; return opendir('fakedir://shared'); } else if ($source = $this->getSourcePath($path)) { - $storage = \OC_Filesystem::getStorage($source); + $storage = \OC\Files\Filesystem::getStorage($source); return $storage->opendir($this->getInternalPath($source)); } return false; @@ -144,7 +144,7 @@ class Shared extends \OC\Files\Storage\Common { if ($path == '' || $path == '/') { return true; } else if ($source = $this->getSourcePath($path)) { - $storage = \OC_Filesystem::getStorage($source); + $storage = \OC\Files\Filesystem::getStorage($source); return $storage->is_dir($this->getInternalPath($source)); } return false; @@ -152,7 +152,7 @@ class Shared extends \OC\Files\Storage\Common { public function is_file($path) { if ($source = $this->getSourcePath($path)) { - $storage = \OC_Filesystem::getStorage($source); + $storage = \OC\Files\Filesystem::getStorage($source); return $storage->is_file($this->getInternalPath($source)); } return false; @@ -165,7 +165,7 @@ class Shared extends \OC\Files\Storage\Common { $stat['ctime'] = $this->filectime($path); return $stat; } else if ($source = $this->getSourcePath($path)) { - $storage = \OC_Filesystem::getStorage($source); + $storage = \OC\Files\Filesystem::getStorage($source); return $storage->stat($this->getInternalPath($source)); } return false; @@ -175,7 +175,7 @@ class Shared extends \OC\Files\Storage\Common { if ($path == '' || $path == '/') { return 'dir'; } else if ($source = $this->getSourcePath($path)) { - $storage = \OC_Filesystem::getStorage($source); + $storage = \OC\Files\Filesystem::getStorage($source); return $storage->filetype($this->getInternalPath($source)); } return false; @@ -185,7 +185,7 @@ class Shared extends \OC\Files\Storage\Common { if ($path == '' || $path == '/' || $this->is_dir($path)) { return 0; } else if ($source = $this->getSourcePath($path)) { - $storage = \OC_Filesystem::getStorage($source); + $storage = \OC\Files\Filesystem::getStorage($source); return $storage->filesize($this->getInternalPath($source)); } return false; @@ -195,7 +195,7 @@ class Shared extends \OC\Files\Storage\Common { if ($path == '') { return false; } - return ($this->getPermissions($path) & OCP\Share::PERMISSION_CREATE); + return ($this->getPermissions($path) & \OCP\Share::PERMISSION_CREATE); } public function isReadable($path) { @@ -206,28 +206,28 @@ class Shared extends \OC\Files\Storage\Common { if ($path == '') { return false; } - return ($this->getPermissions($path) & OCP\Share::PERMISSION_UPDATE); + return ($this->getPermissions($path) & \OCP\Share::PERMISSION_UPDATE); } public function isDeletable($path) { if ($path == '') { return true; } - return ($this->getPermissions($path) & OCP\Share::PERMISSION_DELETE); + return ($this->getPermissions($path) & \OCP\Share::PERMISSION_DELETE); } public function isSharable($path) { if ($path == '') { return false; } - return ($this->getPermissions($path) & OCP\Share::PERMISSION_SHARE); + return ($this->getPermissions($path) & \OCP\Share::PERMISSION_SHARE); } public function file_exists($path) { if ($path == '' || $path == '/') { return true; } else if ($source = $this->getSourcePath($path)) { - $storage = \OC_Filesystem::getStorage($source); + $storage = \OC\Files\Filesystem::getStorage($source); return $storage->file_exists($this->getInternalPath($source)); } return false; @@ -248,7 +248,7 @@ class Shared extends \OC\Files\Storage\Common { } else { $source = $this->getSourcePath($path); if ($source) { - $storage = \OC_Filesystem::getStorage($source); + $storage = \OC\Files\Filesystem::getStorage($source); return $storage->filectime($this->getInternalPath($source)); } } @@ -269,7 +269,7 @@ class Shared extends \OC\Files\Storage\Common { } else { $source = $this->getSourcePath($path); if ($source) { - $storage = \OC_Filesystem::getStorage($source); + $storage = \OC\Files\Filesystem::getStorage($source); return $storage->filemtime($this->getInternalPath($source)); } } @@ -282,8 +282,8 @@ class Shared extends \OC\Files\Storage\Common { 'target' => $this->sharedFolder.$path, 'source' => $source, ); - OCP\Util::emitHook('\OC\Files\Storage\Shared', 'file_get_contents', $info); - $storage = \OC_Filesystem::getStorage($source); + \OCP\Util::emitHook('\OC\Files\Storage\Shared', 'file_get_contents', $info); + $storage = \OC\Files\Filesystem::getStorage($source); return $storage->file_get_contents($this->getInternalPath($source)); } } @@ -298,8 +298,8 @@ class Shared extends \OC\Files\Storage\Common { 'target' => $this->sharedFolder.$path, 'source' => $source, ); - OCP\Util::emitHook('\OC\Files\Storage\Shared', 'file_put_contents', $info); - $storage = \OC_Filesystem::getStorage($source); + \OCP\Util::emitHook('\OC\Files\Storage\Shared', 'file_put_contents', $info); + $storage = \OC\Files\Filesystem::getStorage($source); $result = $storage->file_put_contents($this->getInternalPath($source), $data); return $result; } @@ -310,7 +310,7 @@ class Shared extends \OC\Files\Storage\Common { // Delete the file if DELETE permission is granted if ($source = $this->getSourcePath($path)) { if ($this->isDeletable($path)) { - $storage = \OC_Filesystem::getStorage($source); + $storage = \OC\Files\Filesystem::getStorage($source); return $storage->unlink($this->getInternalPath($source)); } else if (dirname($path) == '/' || dirname($path) == '.') { // Unshare the file from the user if in the root of the Shared folder @@ -319,7 +319,7 @@ class Shared extends \OC\Files\Storage\Common { } else { $itemType = 'file'; } - return OCP\Share::unshareFromSelf($itemType, $path); + return \OCP\Share::unshareFromSelf($itemType, $path); } } return false; @@ -334,7 +334,7 @@ class Shared extends \OC\Files\Storage\Common { if (dirname($path1) == dirname($path2)) { // Rename the file if UPDATE permission is granted if ($this->isUpdatable($path1)) { - $storage = \OC_Filesystem::getStorage($oldSource); + $storage = \OC\Files\Filesystem::getStorage($oldSource); return $storage->rename($this->getInternalPath($oldSource), $this->getInternalPath($newSource)); } } else { @@ -349,7 +349,7 @@ class Shared extends \OC\Files\Storage\Common { return $this->unlink($path1); } } else { - $storage = \OC_Filesystem::getStorage($oldSource); + $storage = \OC\Files\Filesystem::getStorage($oldSource); return $storage->rename($this->getInternalPath($oldSource), $this->getInternalPath($newSource)); } } @@ -394,8 +394,8 @@ class Shared extends \OC\Files\Storage\Common { 'source' => $source, 'mode' => $mode, ); - OCP\Util::emitHook('\OC\Files\Storage\Shared', 'fopen', $info); - $storage = \OC_Filesystem::getStorage($source); + \OCP\Util::emitHook('\OC\Files\Storage\Shared', 'fopen', $info); + $storage = \OC\Files\Filesystem::getStorage($source); return $storage->fopen($this->getInternalPath($source), $mode); } return false; @@ -406,7 +406,7 @@ class Shared extends \OC\Files\Storage\Common { return 'httpd/unix-directory'; } if ($source = $this->getSourcePath($path)) { - $storage = \OC_Filesystem::getStorage($source); + $storage = \OC\Files\Filesystem::getStorage($source); return $storage->getMimeType($this->getInternalPath($source)); } return false; @@ -415,21 +415,21 @@ class Shared extends \OC\Files\Storage\Common { public function free_space($path) { $source = $this->getSourcePath($path); if ($source) { - $storage = \OC_Filesystem::getStorage($source); + $storage = \OC\Files\Filesystem::getStorage($source); return $storage->free_space($this->getInternalPath($source)); } } public function getLocalFile($path) { if ($source = $this->getSourcePath($path)) { - $storage = \OC_Filesystem::getStorage($source); + $storage = \OC\Files\Filesystem::getStorage($source); return $storage->getLocalFile($this->getInternalPath($source)); } return false; } public function touch($path, $mtime = null) { if ($source = $this->getSourcePath($path)) { - $storage = \OC_Filesystem::getStorage($source); + $storage = \OC\Files\Filesystem::getStorage($source); return $storage->touch($this->getInternalPath($source), $mtime); } return false; @@ -437,7 +437,7 @@ class Shared extends \OC\Files\Storage\Common { public static function setup($options) { $user_dir = $options['user_dir']; - \OC_Filesystem::mount('\OC\Files\Storage\Shared', array('sharedFolder' => '/Shared'), $user_dir.'/Shared/'); + \OC\Files\Filesystem::mount('\OC\Files\Storage\Shared', array('sharedFolder' => '/Shared'), $user_dir.'/Shared/'); } /** diff --git a/apps/files_sharing/public.php b/apps/files_sharing/public.php index 34340102a9..c5f4b39d70 100644 --- a/apps/files_sharing/public.php +++ b/apps/files_sharing/public.php @@ -73,7 +73,7 @@ if (isset($_GET['file']) || isset($_GET['dir'])) { if (isset($_GET['path'])) { $path .= $_GET['path']; $dir .= $_GET['path']; - if (!OC_Filesystem::file_exists($path)) { + if (!\OC\Files\Filesystem::file_exists($path)) { header('HTTP/1.0 404 Not Found'); $tmpl = new OCP\Template('', '404', 'guest'); $tmpl->printPage(); @@ -101,7 +101,7 @@ if (isset($_GET['file']) || isset($_GET['dir'])) { $tmpl = new OCP\Template('files_sharing', 'public', 'base'); $tmpl->assign('owner', $uidOwner); // Show file list - if (OC_Filesystem::is_dir($path)) { + if (\OC\Files\Filesystem::is_dir($path)) { OCP\Util::addStyle('files', 'files'); OCP\Util::addScript('files', 'files'); OCP\Util::addScript('files', 'filelist'); @@ -157,7 +157,7 @@ if (isset($_GET['file']) || isset($_GET['dir'])) { $tmpl->assign('uidOwner', $uidOwner); $tmpl->assign('dir', basename($dir)); $tmpl->assign('filename', basename($path)); - $tmpl->assign('mimetype', OC_Filesystem::getMimeType($path)); + $tmpl->assign('mimetype', \OC\Files\Filesystem::getMimeType($path)); $tmpl->assign('allowZipDownload', intval(OCP\Config::getSystemValue('allowZipDownload', true))); if (isset($_GET['path'])) { $getPath = $_GET['path']; @@ -170,7 +170,7 @@ if (isset($_GET['file']) || isset($_GET['dir'])) { $tmpl->assign('uidOwner', $uidOwner); $tmpl->assign('dir', dirname($path)); $tmpl->assign('filename', basename($path)); - $tmpl->assign('mimetype', OC_Filesystem::getMimeType($path)); + $tmpl->assign('mimetype', \OC\Files\Filesystem::getMimeType($path)); if ($type == 'file') { $tmpl->assign('downloadURL', OCP\Util::linkToPublic('files').'&file='.$_GET['file'].'&download'); } else { diff --git a/apps/files_versions/lib/hooks.php b/apps/files_versions/lib/hooks.php index 9ec0b01a7f..7cb3170df2 100644 --- a/apps/files_versions/lib/hooks.php +++ b/apps/files_versions/lib/hooks.php @@ -21,9 +21,9 @@ class Hooks { if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') { - $versions = new Storage( new \OC_FilesystemView('') ); + $versions = new Storage( new \OC\Files\View('') ); - $path = $params[\OC_Filesystem::signal_param_path]; + $path = $params[\OC\Files\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 index 7d12e58f94..7f7837a742 100644 --- a/apps/files_versions/lib/versions.php +++ b/apps/files_versions/lib/versions.php @@ -58,8 +58,8 @@ class Storage { public function store($filename) { if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') { list($uid, $filename) = self::getUidAndFilename($filename); - $files_view = new \OC_FilesystemView('/'.$uid.'/files'); - $users_view = new \OC_FilesystemView('/'.$uid); + $files_view = new \OC\Files\View('/'.$uid.'/files'); + $users_view = new \OC\Files\View('/'.$uid); //check if source file already exist as version to avoid recursions. // todo does this check work? @@ -94,7 +94,7 @@ class Storage { // check mininterval if the file is being modified by the owner (all shared files should be versioned despite mininterval) if ($uid == \OCP\User::getUser()) { - $versions_fileview = new \OC_FilesystemView('/'.$uid.'/files_versions'); + $versions_fileview = new \OC\Files\View('/'.$uid.'/files_versions'); $versionsFolderName=\OCP\Config::getSystemValue('datadirectory'). $versions_fileview->getAbsolutePath(''); $matches=glob($versionsFolderName.'/'.$filename.'.v*'); sort($matches); @@ -127,7 +127,7 @@ class Storage { if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') { list($uid, $filename) = self::getUidAndFilename($filename); - $users_view = new \OC_FilesystemView('/'.$uid); + $users_view = new \OC\Files\View('/'.$uid); // rollback if( @$users_view->copy('files_versions'.$filename.'.v'.$revision, 'files'.$filename) ) { @@ -150,7 +150,7 @@ class Storage { public static function isversioned($filename) { if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') { list($uid, $filename) = self::getUidAndFilename($filename); - $versions_fileview = new \OC_FilesystemView('/'.$uid.'/files_versions'); + $versions_fileview = new \OC\Files\View('/'.$uid.'/files_versions'); $versionsFolderName=\OCP\Config::getSystemValue('datadirectory'). $versions_fileview->getAbsolutePath(''); @@ -178,7 +178,7 @@ class Storage { if( \OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true' ) { list($uid, $filename) = self::getUidAndFilename($filename); - $versions_fileview = new \OC_FilesystemView('/'.$uid.'/files_versions'); + $versions_fileview = new \OC\Files\View('/'.$uid.'/files_versions'); $versionsFolderName = \OCP\Config::getSystemValue('datadirectory'). $versions_fileview->getAbsolutePath(''); $versions = array(); @@ -190,7 +190,7 @@ class Storage { $i = 0; - $files_view = new \OC_FilesystemView('/'.$uid.'/files'); + $files_view = new \OC\Files\View('/'.$uid.'/files'); $local_file = $files_view->getLocalFile($filename); foreach( $matches as $ma ) { @@ -245,7 +245,7 @@ class Storage { public static function expire($filename) { if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') { list($uid, $filename) = self::getUidAndFilename($filename); - $versions_fileview = new \OC_FilesystemView('/'.$uid.'/files_versions'); + $versions_fileview = new \OC\Files\View('/'.$uid.'/files_versions'); $versionsFolderName=\OCP\Config::getSystemValue('datadirectory'). $versions_fileview->getAbsolutePath(''); diff --git a/lib/connector/sabre/directory.php b/lib/connector/sabre/directory.php index 413efef73b..4fff3ba51b 100644 --- a/lib/connector/sabre/directory.php +++ b/lib/connector/sabre/directory.php @@ -62,7 +62,7 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa } } else { $newPath = $this->path . '/' . $name; - OC_Filesystem::file_put_contents($newPath, $data); + \OC\Files\Filesystem::file_put_contents($newPath, $data); return OC_Connector_Sabre_Node::getETagPropertyForPath($newPath); } @@ -78,7 +78,7 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa public function createDirectory($name) { $newPath = $this->path . '/' . $name; - OC_Filesystem::mkdir($newPath); + \OC\Files\Filesystem::mkdir($newPath); } @@ -154,7 +154,7 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa public function childExists($name) { $path = $this->path . '/' . $name; - return OC_Filesystem::file_exists($path); + return \OC\Files\Filesystem::file_exists($path); } @@ -167,7 +167,7 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa if ($this->path != "/Shared") { foreach($this->getChildren() as $child) $child->delete(); - OC_Filesystem::rmdir($this->path); + \OC\Files\Filesystem::rmdir($this->path); } } @@ -181,7 +181,7 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa $rootInfo=OC_FileCache_Cached::get(''); return array( $rootInfo['size'], - OC_Filesystem::free_space() + \OC\Files\Filesystem::free_space() ); } diff --git a/lib/connector/sabre/file.php b/lib/connector/sabre/file.php index 5bd38240d4..1770b49128 100644 --- a/lib/connector/sabre/file.php +++ b/lib/connector/sabre/file.php @@ -45,7 +45,7 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements Sabre_D */ public function put($data) { - OC_Filesystem::file_put_contents($this->path,$data); + \OC\Files\Filesystem::file_put_contents($this->path,$data); return OC_Connector_Sabre_Node::getETagPropertyForPath($this->path); } @@ -57,7 +57,7 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements Sabre_D */ public function get() { - return OC_Filesystem::fopen($this->path,'rb'); + return \OC\Files\Filesystem::fopen($this->path,'rb'); } @@ -68,7 +68,7 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements Sabre_D */ public function delete() { - OC_Filesystem::unlink($this->path); + \OC\Files\Filesystem::unlink($this->path); } @@ -107,7 +107,7 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements Sabre_D * @return string|null Returns null if the ETag can not effectively be determined */ static protected function createETag($path) { - return OC_Filesystem::hash('md5', $path); + return \OC\Files\Filesystem::hash('md5', $path); } /** @@ -122,7 +122,7 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements Sabre_D return $this->fileinfo_cache['mimetype']; } - return OC_Filesystem::getMimeType($this->path); + return \OC\Files\Filesystem::getMimeType($this->path); } } diff --git a/lib/connector/sabre/node.php b/lib/connector/sabre/node.php index 72de972377..e7e83507ea 100644 --- a/lib/connector/sabre/node.php +++ b/lib/connector/sabre/node.php @@ -80,7 +80,7 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr $newPath = $parentPath . '/' . $newName; $oldPath = $this->path; - OC_Filesystem::rename($this->path,$newPath); + \OC\Files\Filesystem::rename($this->path,$newPath); $this->path = $newPath; @@ -99,9 +99,9 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr */ protected function getFileinfoCache() { if (!isset($this->fileinfo_cache)) { - if ($fileinfo_cache = OC_FileCache::get($this->path)) { + if ($fileinfo_cache = \OC\Files\Filesystem::get($this->path)) { } else { - $fileinfo_cache = OC_Filesystem::stat($this->path); + $fileinfo_cache = \OC\Files\Filesystem::stat($this->path); } $this->fileinfo_cache = $fileinfo_cache; @@ -130,7 +130,7 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr * Even if the modification time is set to a custom value the access time is set to now. */ public function touch($mtime) { - OC_Filesystem::touch($this->path, $mtime); + \OC\Files\Filesystem::touch($this->path, $mtime); } /** diff --git a/lib/filechunking.php b/lib/filechunking.php index 5ab33c77ad..e616e3b12d 100644 --- a/lib/filechunking.php +++ b/lib/filechunking.php @@ -94,49 +94,49 @@ class OC_FileChunking { } public function file_assemble($path) { - $absolutePath = OC_Filesystem::normalizePath(OC_Filesystem::getView()->getAbsolutePath($path)); + $absolutePath = \OC\Files\Filesystem::normalizePath(\OC\Files\Filesystem::getView()->getAbsolutePath($path)); $data = ''; // use file_put_contents as method because that best matches what this function does - if (OC_FileProxy::runPreProxies('file_put_contents', $absolutePath, $data) && OC_Filesystem::isValidPath($path)) { - $path = OC_Filesystem::getView()->getRelativePath($absolutePath); - $exists = OC_Filesystem::file_exists($path); + if (OC_FileProxy::runPreProxies('file_put_contents', $absolutePath, $data) && \OC\Files\Filesystem::isValidPath($path)) { + $path = \OC\Files\Filesystem::getView()->getRelativePath($absolutePath); + $exists = \OC\Files\Filesystem::file_exists($path); $run = true; if(!$exists) { OC_Hook::emit( - OC_Filesystem::CLASSNAME, - OC_Filesystem::signal_create, + \OC\Files\Filesystem::CLASSNAME, + \OC\Files\Filesystem::signal_create, array( - OC_Filesystem::signal_param_path => $path, - OC_Filesystem::signal_param_run => &$run + \OC\Files\Filesystem::signal_param_path => $path, + \OC\Files\Filesystem::signal_param_run => &$run ) ); } OC_Hook::emit( - OC_Filesystem::CLASSNAME, - OC_Filesystem::signal_write, + \OC\Files\Filesystem::CLASSNAME, + \OC\Files\Filesystem::signal_write, array( - OC_Filesystem::signal_param_path => $path, - OC_Filesystem::signal_param_run => &$run + \OC\Files\Filesystem::signal_param_path => $path, + \OC\Files\Filesystem::signal_param_run => &$run ) ); if(!$run) { return false; } - $target = OC_Filesystem::fopen($path, 'w'); + $target = \OC\Files\Filesystem::fopen($path, 'w'); if($target) { $count = $this->assemble($target); fclose($target); if(!$exists) { OC_Hook::emit( - OC_Filesystem::CLASSNAME, - OC_Filesystem::signal_post_create, - array( OC_Filesystem::signal_param_path => $path) + \OC\Files\Filesystem::CLASSNAME, + \OC\Files\Filesystem::signal_post_create, + array( \OC\Files\Filesystem::signal_param_path => $path) ); } OC_Hook::emit( - OC_Filesystem::CLASSNAME, - OC_Filesystem::signal_post_write, - array( OC_Filesystem::signal_param_path => $path) + \OC\Files\Filesystem::CLASSNAME, + \OC\Files\Filesystem::signal_post_write, + array( \OC\Files\Filesystem::signal_param_path => $path) ); OC_FileProxy::runPostProxies('file_put_contents', $absolutePath, $count); return $count > 0; diff --git a/lib/fileproxy/quota.php b/lib/fileproxy/quota.php index 5a0dbdb6fe..bc5ef9c8df 100644 --- a/lib/fileproxy/quota.php +++ b/lib/fileproxy/quota.php @@ -88,7 +88,7 @@ class OC_FileProxy_Quota extends OC_FileProxy{ public function preCopy($path1,$path2) { if(!self::$rootView){ - self::$rootView = new OC_FilesystemView(''); + self::$rootView = new \OC\Files\View(''); } return (self::$rootView->filesize($path1)<$this->getFreeSpace() or $this->getFreeSpace()==0); } diff --git a/lib/files.php b/lib/files.php index 2b2b8b42dc..29322cf2d0 100644 --- a/lib/files.php +++ b/lib/files.php @@ -46,10 +46,10 @@ class OC_Files { if ($path == '/Shared') { list($info) = OCP\Share::getItemsSharedWith('file', OC_Share_Backend_File::FORMAT_FILE_APP_ROOT); }else{ - $info['size'] = OC_Filesystem::filesize($path); - $info['mtime'] = OC_Filesystem::filemtime($path); - $info['ctime'] = OC_Filesystem::filectime($path); - $info['mimetype'] = OC_Filesystem::getMimeType($path); + $info['size'] = \OC\Files\Filesystem::filesize($path); + $info['mtime'] = \OC\Files\Filesystem::filemtime($path); + $info['ctime'] = \OC\Files\Filesystem::filectime($path); + $info['mimetype'] = \OC\Files\Filesystem::getMimeType($path); $info['encrypted'] = false; $info['versioned'] = false; } @@ -64,7 +64,7 @@ class OC_Files { * @param dir $directory path under datadirectory */ public static function getDirectoryContent($directory, $mimetype_filter = '') { - $directory=OC_Filesystem::normalizePath($directory); + $directory=\OC\Files\Filesystem::normalizePath($directory); if($directory=='/') { $directory=''; } @@ -151,17 +151,17 @@ class OC_Files { } foreach($files as $file) { $file=$dir.'/'.$file; - if(OC_Filesystem::is_file($file)) { - $tmpFile=OC_Filesystem::toTmpFile($file); + if(\OC\Files\Filesystem::is_file($file)) { + $tmpFile=OC_F\OC\Files\Filesystemilesystem::toTmpFile($file); self::$tmpFiles[]=$tmpFile; $zip->addFile($tmpFile,basename($file)); - }elseif(OC_Filesystem::is_dir($file)) { + }elseif(\OC\Files\Filesystem::is_dir($file)) { self::zipAddDir($file,$zip); } } $zip->close(); set_time_limit($executionTime); - }elseif(OC_Filesystem::is_dir($dir.'/'.$files)) { + }elseif(\OC\Files\Filesystem::is_dir($dir.'/'.$files)) { self::validateZipDownload($dir,$files); $executionTime = intval(ini_get('max_execution_time')); set_time_limit(0); @@ -179,7 +179,7 @@ class OC_Files { $filename=$dir.'/'.$files; } @ob_end_clean(); - if($zip or OC_Filesystem::is_readable($filename)) { + if($zip or \OC\Files\Filesystem::is_readable($filename)) { header('Content-Disposition: attachment; filename="'.basename($filename).'"'); header('Content-Transfer-Encoding: binary'); OC_Response::disableCaching(); @@ -188,9 +188,9 @@ class OC_Files { header('Content-Type: application/zip'); header('Content-Length: ' . filesize($filename)); }else{ - header('Content-Type: '.OC_Filesystem::getMimeType($filename)); + header('Content-Type: '.\OC\Files\Filesystem::getMimeType($filename)); } - }elseif($zip or !OC_Filesystem::file_exists($filename)) { + }elseif($zip or !\OC\Files\Filesystem::file_exists($filename)) { header("HTTP/1.0 404 Not Found"); $tmpl = new OC_Template( '', '404', 'guest' ); $tmpl->assign('file',$filename); @@ -201,7 +201,7 @@ class OC_Files { } if($only_header) { if(!$zip) - header("Content-Length: ".OC_Filesystem::filesize($filename)); + header("Content-Length: ".\OC\Files\Filesystem::filesize($filename)); return ; } if($zip) { @@ -215,7 +215,7 @@ class OC_Files { } unlink($filename); }else{ - OC_Filesystem::readfile($filename); + \OC\Files\Filesystem::readfile($filename); } foreach(self::$tmpFiles as $tmpFile) { if(file_exists($tmpFile) and is_file($tmpFile)) { @@ -232,11 +232,11 @@ class OC_Files { foreach($files as $file) { $filename=$file['name']; $file=$dir.'/'.$filename; - if(OC_Filesystem::is_file($file)) { - $tmpFile=OC_Filesystem::toTmpFile($file); + if(\OC\Files\Filesystem::is_file($file)) { + $tmpFile=\OC\Files\Filesystem::toTmpFile($file); OC_Files::$tmpFiles[]=$tmpFile; $zip->addFile($tmpFile,$internalDir.$filename); - }elseif(OC_Filesystem::is_dir($file)) { + }elseif(\OC\Files\Filesystem::is_dir($file)) { self::zipAddDir($file,$zip,$internalDir); } } @@ -253,7 +253,7 @@ class OC_Files { if(OC_User::isLoggedIn() && ($sourceDir != '' || $source != 'Shared')) { $targetFile=self::normalizePath($targetDir.'/'.$target); $sourceFile=self::normalizePath($sourceDir.'/'.$source); - return OC_Filesystem::rename($sourceFile,$targetFile); + return \OC\Files\Filesystem::rename($sourceFile,$targetFile); } else { return false; } @@ -271,7 +271,7 @@ class OC_Files { if(OC_User::isLoggedIn()) { $targetFile=$targetDir.'/'.$target; $sourceFile=$sourceDir.'/'.$source; - return OC_Filesystem::copy($sourceFile,$targetFile); + return \OC\Files\Filesystem::copy($sourceFile,$targetFile); } } @@ -286,9 +286,9 @@ class OC_Files { if(OC_User::isLoggedIn()) { $file=$dir.'/'.$name; if($type=='dir') { - return OC_Filesystem::mkdir($file); + return \OC\Files\Filesystem::mkdir($file); }elseif($type=='file') { - $fileHandle=OC_Filesystem::fopen($file, 'w'); + $fileHandle=\OC\Files\Filesystem::fopen($file, 'w'); if($fileHandle) { fclose($fileHandle); return true; @@ -308,7 +308,7 @@ class OC_Files { public static function delete($dir,$file) { if(OC_User::isLoggedIn() && ($dir!= '' || $file != 'Shared')) { $file=$dir.'/'.$file; - return OC_Filesystem::unlink($file); + return \OC\Files\Filesystem::unlink($file); } } @@ -339,10 +339,10 @@ class OC_Files { $totalsize = 0; if(is_array($files)) { foreach($files as $file) { - $totalsize += OC_Filesystem::filesize($dir.'/'.$file); + $totalsize += \OC\Files\Filesystem::filesize($dir.'/'.$file); } }else{ - $totalsize += OC_Filesystem::filesize($dir.'/'.$files); + $totalsize += \OC\Files\Filesystem::filesize($dir.'/'.$files); } if($totalsize > $zipLimit) { $l = OC_L10N::get('lib'); @@ -368,7 +368,7 @@ class OC_Files { * @return string guessed mime type */ static function getMimeType($path) { - return OC_Filesystem::getMimeType($path); + return \OC\Files\Filesystem::getMimeType($path); } /** @@ -378,7 +378,7 @@ class OC_Files { * @return array */ static function getTree($path) { - return OC_Filesystem::getTree($path); + return \OC\Files\Filesystem::getTree($path); } /** @@ -402,7 +402,7 @@ class OC_Files { $httpCode=$info['http_code']; curl_close($ch); if($httpCode==200 or $httpCode==0) { - OC_Filesystem::fromTmpFile($tmpfile,$dir.'/'.$file); + \OC\Files\Filesystem::fromTmpFile($tmpfile,$dir.'/'.$file); return true; }else{ return false; diff --git a/lib/files/file.php b/lib/files/file.php index b6432e6345..0d33cea7ee 100644 --- a/lib/files/file.php +++ b/lib/files/file.php @@ -30,7 +30,7 @@ class File{ public static function resolve($fullPath){ $storage = null; $internalPath = ''; - list($storage, $internalPath) = \OC_Filesystem::resolvePath($fullPath); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($fullPath); return new File($storage, $internalPath); } diff --git a/lib/helper.php b/lib/helper.php index 908a61b5a2..48e16cebd0 100644 --- a/lib/helper.php +++ b/lib/helper.php @@ -560,7 +560,7 @@ class OC_Helper { $newpath = $path . '/' . $filename; $counter = 2; - while (OC_Filesystem::file_exists($newpath)) { + while (\OC\Files\Filesystem::file_exists($newpath)) { $newname = $name . ' (' . $counter . ')' . $ext; $newpath = $path . '/' . $newname; $counter++; diff --git a/lib/image.php b/lib/image.php index 861353e039..94fe3ce827 100644 --- a/lib/image.php +++ b/lib/image.php @@ -470,7 +470,7 @@ class OC_Image { default: // this is mostly file created from encrypted file - $this->resource = imagecreatefromstring(\OC_Filesystem::file_get_contents(\OC_Filesystem::getLocalPath($imagepath))); + $this->resource = imagecreatefromstring(\OC\Files\Filesystem::file_get_contents(\OC\Files\Filesystem::getLocalPath($imagepath))); $itype = IMAGETYPE_PNG; OC_Log::write('core','OC_Image->loadFromFile, Default', OC_Log::DEBUG); break; diff --git a/lib/ocs.php b/lib/ocs.php index 7350c3c882..645380ddba 100644 --- a/lib/ocs.php +++ b/lib/ocs.php @@ -589,11 +589,11 @@ class OC_OCS { if(OC_User::userExists($user)) { // calculate the disc space $user_dir = '/'.$user.'/files'; - OC_Filesystem::init($user_dir); + \OC\Files\Filesystem::init($user_dir); $rootInfo=OC_FileCache::get(''); $sharedInfo=OC_FileCache::get('/Shared'); $used=$rootInfo['size']-$sharedInfo['size']; - $free=OC_Filesystem::free_space(); + $free=\OC\Files\Filesystem::free_space(); $total=$free+$used; if($total==0) $total=1; // prevent division by zero $relative=round(($used/$total)*10000)/100; diff --git a/lib/public/files.php b/lib/public/files.php index 90889c59ad..2d6775e01f 100644 --- a/lib/public/files.php +++ b/lib/public/files.php @@ -98,7 +98,7 @@ class Files { /** * @param string appid * @param $app app - * @return OC_FilesystemView + * @return \OC\Files\View */ public static function getStorage( $app ) { return \OC_App::getStorage( $app ); diff --git a/lib/public/share.php b/lib/public/share.php index 1db3a0b2c1..7c4ede5e88 100644 --- a/lib/public/share.php +++ b/lib/public/share.php @@ -515,7 +515,7 @@ class Share { $backend = self::getBackend($itemType); // Get filesystem root to add it to the file target and remove from the file source, match file_source with the file cache if ($itemType == 'file' || $itemType == 'folder') { - $root = \OC_Filesystem::getRoot(); + $root = \OC\Files\Filesystem::getRoot(); $where = 'INNER JOIN `*PREFIX*fscache` ON `file_source` = `*PREFIX*fscache`.`id`'; if (!isset($item)) { $where .= ' WHERE `file_target` IS NOT NULL'; @@ -602,7 +602,7 @@ class Share { } else { if ($itemType == 'file' || $itemType == 'folder') { $where .= ' `file_target` = ?'; - $item = \OC_Filesystem::normalizePath($item); + $item = \OC\Files\Filesystem::normalizePath($item); } else { $where .= ' `item_target` = ?'; } @@ -751,7 +751,7 @@ class Share { } else { $childItem['file_source'] = \OC_FileCache::getId($child['file_path']); } - $childItem['file_target'] = \OC_Filesystem::normalizePath($child['file_path']); + $childItem['file_target'] = \OC\Files\Filesystem::normalizePath($child['file_path']); } if (isset($item)) { if ($childItem[$column] == $item) { diff --git a/lib/util.php b/lib/util.php index 557f731522..5cf7657457 100755 --- a/lib/util.php +++ b/lib/util.php @@ -34,7 +34,7 @@ class OC_Util { $CONFIG_DATADIRECTORY = OC_Config::getValue( "datadirectory", OC::$SERVERROOT."/data" ); //first set up the local "root" storage if(!self::$rootMounted) { - OC_Filesystem::mount('\OC\Files\Storage\Local',array('datadir'=>$CONFIG_DATADIRECTORY),'/'); + \OC\Files\Filesystem::mount('\OC\Files\Storage\Local',array('datadir'=>$CONFIG_DATADIRECTORY),'/'); self::$rootMounted=true; } @@ -46,8 +46,8 @@ class OC_Util { mkdir( $userdirectory, 0755, true ); } //jail the user into his "home" directory - OC_Filesystem::mount('\OC\Files\Storage\Local', array('datadir' => $user_root), $user); - OC_Filesystem::init($user_dir); + \OC\Files\Filesystem::mount('\OC\Files\Storage\Local', array('datadir' => $user_root), $user); + \OC\Files\Filesystem::init($user_dir); $quotaProxy=new OC_FileProxy_Quota(); OC_FileProxy::register($quotaProxy); // Load personal mount config @@ -55,7 +55,7 @@ class OC_Util { $mountConfig = include($user_root.'/mount.php'); if (isset($mountConfig['user'][$user])) { foreach ($mountConfig['user'][$user] as $mountPoint => $options) { - OC_Filesystem::mount($options['class'], $options['options'], $mountPoint); + \OC\Files\Filesystem::mount($options['class'], $options['options'], $mountPoint); } } } @@ -64,7 +64,7 @@ class OC_Util { } public static function tearDownFS() { - OC_Filesystem::tearDown(); + \OC\Files\Filesystem::tearDown(); self::$fsSetup=false; } diff --git a/settings/personal.php b/settings/personal.php index 9297f5d91c..54002d87c5 100644 --- a/settings/personal.php +++ b/settings/personal.php @@ -25,7 +25,7 @@ if (!isset($sharedInfo['size'])) { } $used=$rootInfo['size']-$sharedSize; if($used<0) $used=0; -$free=OC_Filesystem::free_space(); +$free=\OC\Files\Filesystem::free_space(); $total=$free+$used; if($total==0) $total=1; // prevent division by zero $relative=round(($used/$total)*10000)/100; diff --git a/tests/lib/cache/file.php b/tests/lib/cache/file.php index 3e8fd8c1bb..5dcd326880 100644 --- a/tests/lib/cache/file.php +++ b/tests/lib/cache/file.php @@ -38,8 +38,8 @@ class Test_Cache_File extends Test_Cache { } //set up temporary storage - OC_Filesystem::clearMounts(); - OC_Filesystem::mount('\OC\Files\Storage\Temporary',array(),'/'); + \OC\Files\Filesystem::clearMounts(); + \OC\Files\Filesystem::mount('\OC\Files\Storage\Temporary',array(),'/'); OC_User::clearBackends(); OC_User::useBackend(new OC_User_Dummy()); diff --git a/tests/lib/filesystem.php b/tests/lib/filesystem.php index b402b86af1..af3620f570 100644 --- a/tests/lib/filesystem.php +++ b/tests/lib/filesystem.php @@ -20,6 +20,8 @@ * */ +use \OC\Files\Filesystem as Filesystem; + class Test_Filesystem extends UnitTestCase { /** * @var array tmpDirs @@ -42,64 +44,64 @@ class Test_Filesystem extends UnitTestCase { } public function setUp() { - OC_Filesystem::clearMounts(); + Filesystem::clearMounts(); } public function testMount() { - OC_Filesystem::mount('\OC\Files\Storage\Local',self::getStorageData(),'/'); - $this->assertEqual('/',OC_Filesystem::getMountPoint('/')); - $this->assertEqual('/',OC_Filesystem::getMountPoint('/some/folder')); - $this->assertEqual('',OC_Filesystem::getInternalPath('/')); - $this->assertEqual('some/folder',OC_Filesystem::getInternalPath('/some/folder')); + Filesystem::mount('\OC\Files\Storage\Local',self::getStorageData(),'/'); + $this->assertEqual('/',Filesystem::getMountPoint('/')); + $this->assertEqual('/',Filesystem::getMountPoint('/some/folder')); + $this->assertEqual('',Filesystem::getInternalPath('/')); + $this->assertEqual('some/folder',Filesystem::getInternalPath('/some/folder')); - OC_Filesystem::mount('\OC\Files\Storage\Local',self::getStorageData(),'/some'); - $this->assertEqual('/',OC_Filesystem::getMountPoint('/')); - $this->assertEqual('/some/',OC_Filesystem::getMountPoint('/some/folder')); - $this->assertEqual('/some/',OC_Filesystem::getMountPoint('/some/')); - $this->assertEqual('/',OC_Filesystem::getMountPoint('/some')); - $this->assertEqual('folder',OC_Filesystem::getInternalPath('/some/folder')); + Filesystem::mount('\OC\Files\Storage\Local',self::getStorageData(),'/some'); + $this->assertEqual('/',Filesystem::getMountPoint('/')); + $this->assertEqual('/some/',Filesystem::getMountPoint('/some/folder')); + $this->assertEqual('/some/',Filesystem::getMountPoint('/some/')); + $this->assertEqual('/',Filesystem::getMountPoint('/some')); + $this->assertEqual('folder',Filesystem::getInternalPath('/some/folder')); } public function testNormalize() { - $this->assertEqual('/path', OC_Filesystem::normalizePath('/path/')); - $this->assertEqual('/path/', OC_Filesystem::normalizePath('/path/', false)); - $this->assertEqual('/path', OC_Filesystem::normalizePath('path')); - $this->assertEqual('/path', OC_Filesystem::normalizePath('\path')); - $this->assertEqual('/foo/bar', OC_Filesystem::normalizePath('/foo//bar/')); - $this->assertEqual('/foo/bar', OC_Filesystem::normalizePath('/foo////bar')); + $this->assertEqual('/path', Filesystem::normalizePath('/path/')); + $this->assertEqual('/path/', Filesystem::normalizePath('/path/', false)); + $this->assertEqual('/path', Filesystem::normalizePath('path')); + $this->assertEqual('/path', Filesystem::normalizePath('\path')); + $this->assertEqual('/foo/bar', Filesystem::normalizePath('/foo//bar/')); + $this->assertEqual('/foo/bar', Filesystem::normalizePath('/foo////bar')); if (class_exists('Normalizer')) { - $this->assertEqual("/foo/bar\xC3\xBC", OC_Filesystem::normalizePath("/foo/baru\xCC\x88")); + $this->assertEqual("/foo/bar\xC3\xBC", Filesystem::normalizePath("/foo/baru\xCC\x88")); } } public function testHooks() { - if(OC_Filesystem::getView()){ + if(Filesystem::getView()){ $user = OC_User::getUser(); }else{ $user=uniqid(); - OC_Filesystem::init('/'.$user.'/files'); + Filesystem::init('/'.$user.'/files'); } OC_Hook::clear('OC_Filesystem'); OC_Hook::connect('OC_Filesystem', 'post_write', $this, 'dummyHook'); - OC_Filesystem::mount('OC\Files\Storage\Temporary', array(), '/'); + Filesystem::mount('OC\Files\Storage\Temporary', array(), '/'); $rootView=new \OC\Files\View(''); $rootView->mkdir('/'.$user); $rootView->mkdir('/'.$user.'/files'); - OC_Filesystem::file_put_contents('/foo', 'foo'); - OC_Filesystem::mkdir('/bar'); - OC_Filesystem::file_put_contents('/bar//foo', 'foo'); + Filesystem::file_put_contents('/foo', 'foo'); + Filesystem::mkdir('/bar'); + Filesystem::file_put_contents('/bar//foo', 'foo'); $tmpFile = OC_Helper::tmpFile(); file_put_contents($tmpFile, 'foo'); $fh = fopen($tmpFile, 'r'); - OC_Filesystem::file_put_contents('/bar//foo', $fh); + Filesystem::file_put_contents('/bar//foo', $fh); } public function dummyHook($arguments) { $path = $arguments['path']; - $this->assertEqual($path, OC_Filesystem::normalizePath($path)); //the path passed to the hook should already be normalized + $this->assertEqual($path, Filesystem::normalizePath($path)); //the path passed to the hook should already be normalized } } From 08bb5dbe3a9b2bf502e5c6e0c944867015c3795b Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 3 Oct 2012 18:19:45 +0200 Subject: [PATCH 033/272] few more test cases for scanning folders --- tests/lib/files/cache/scanner.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/lib/files/cache/scanner.php b/tests/lib/files/cache/scanner.php index 41286e69d3..1f11b9d01d 100644 --- a/tests/lib/files/cache/scanner.php +++ b/tests/lib/files/cache/scanner.php @@ -91,6 +91,13 @@ class Scanner extends \UnitTestCase { $this->assertEqual($cachedDataFolder['size'], -1); $this->assertEqual($cachedDataFolder2['size'], -1); + + $this->scanner->scan('folder', \OC\Files\Cache\Scanner::SCAN_SHALLOW); + + $cachedDataFolder = $this->cache->get(''); + $cachedDataFolder2 = $this->cache->get('folder'); + + $this->assertNotEqual($cachedDataFolder['size'], -1); } function setUp() { From a2785f57d2335d06f3fa9900298343c1d7f9253a Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 10 Oct 2012 17:45:47 +0200 Subject: [PATCH 034/272] fix cache scanner test case --- tests/lib/files/cache/scanner.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/lib/files/cache/scanner.php b/tests/lib/files/cache/scanner.php index 1f11b9d01d..e3f47047f5 100644 --- a/tests/lib/files/cache/scanner.php +++ b/tests/lib/files/cache/scanner.php @@ -94,10 +94,9 @@ class Scanner extends \UnitTestCase { $this->scanner->scan('folder', \OC\Files\Cache\Scanner::SCAN_SHALLOW); - $cachedDataFolder = $this->cache->get(''); $cachedDataFolder2 = $this->cache->get('folder'); - $this->assertNotEqual($cachedDataFolder['size'], -1); + $this->assertNotEqual($cachedDataFolder2['size'], -1); } function setUp() { From 5c6e9518edde10e0c23d0d734d2cc6d161fc15c0 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 10 Oct 2012 17:46:29 +0200 Subject: [PATCH 035/272] drop Filesystem::getInternalPath and Filesystem::getStorage in favor of Filesystem::resolvePath --- apps/files_sharing/lib/sharedstorage.php | 86 ++++++++++++------------ lib/files/filesystem.php | 31 --------- lib/files/view.php | 61 ++++++----------- lib/filesystem.php | 22 ------ tests/lib/filesystem.php | 9 ++- 5 files changed, 71 insertions(+), 138 deletions(-) diff --git a/apps/files_sharing/lib/sharedstorage.php b/apps/files_sharing/lib/sharedstorage.php index e17c4b6e04..e12027a4f2 100644 --- a/apps/files_sharing/lib/sharedstorage.php +++ b/apps/files_sharing/lib/sharedstorage.php @@ -114,16 +114,16 @@ class Shared extends \OC\Files\Storage\Common { if ($path == '' || $path == '/' || !$this->isCreatable(dirname($path))) { return false; } else if ($source = $this->getSourcePath($path)) { - $storage = \OC\Files\Filesystem::getStorage($source); - return $storage->mkdir($this->getInternalPath($source)); + list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($source); + return $storage->mkdir($internalPath); } return false; } public function rmdir($path) { if (($source = $this->getSourcePath($path)) && $this->isDeletable($path)) { - $storage = \OC\Files\Filesystem::getStorage($source); - return $storage->rmdir($this->getInternalPath($source)); + list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($source); + return $storage->rmdir($internalPath); } return false; } @@ -134,8 +134,8 @@ class Shared extends \OC\Files\Storage\Common { \OC_FakeDirStream::$dirs['shared'] = $files; return opendir('fakedir://shared'); } else if ($source = $this->getSourcePath($path)) { - $storage = \OC\Files\Filesystem::getStorage($source); - return $storage->opendir($this->getInternalPath($source)); + list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($source); + return $storage->opendir($internalPath); } return false; } @@ -144,16 +144,16 @@ class Shared extends \OC\Files\Storage\Common { if ($path == '' || $path == '/') { return true; } else if ($source = $this->getSourcePath($path)) { - $storage = \OC\Files\Filesystem::getStorage($source); - return $storage->is_dir($this->getInternalPath($source)); + list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($source); + return $storage->is_dir($internalPath); } return false; } public function is_file($path) { if ($source = $this->getSourcePath($path)) { - $storage = \OC\Files\Filesystem::getStorage($source); - return $storage->is_file($this->getInternalPath($source)); + list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($source); + return $storage->is_file($internalPath); } return false; } @@ -165,8 +165,8 @@ class Shared extends \OC\Files\Storage\Common { $stat['ctime'] = $this->filectime($path); return $stat; } else if ($source = $this->getSourcePath($path)) { - $storage = \OC\Files\Filesystem::getStorage($source); - return $storage->stat($this->getInternalPath($source)); + list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($source); + return $storage->stat($internalPath); } return false; } @@ -175,8 +175,8 @@ class Shared extends \OC\Files\Storage\Common { if ($path == '' || $path == '/') { return 'dir'; } else if ($source = $this->getSourcePath($path)) { - $storage = \OC\Files\Filesystem::getStorage($source); - return $storage->filetype($this->getInternalPath($source)); + list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($source); + return $storage->filetype($internalPath); } return false; } @@ -185,8 +185,8 @@ class Shared extends \OC\Files\Storage\Common { if ($path == '' || $path == '/' || $this->is_dir($path)) { return 0; } else if ($source = $this->getSourcePath($path)) { - $storage = \OC\Files\Filesystem::getStorage($source); - return $storage->filesize($this->getInternalPath($source)); + list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($source); + return $storage->filesize($internalPath); } return false; } @@ -227,8 +227,8 @@ class Shared extends \OC\Files\Storage\Common { if ($path == '' || $path == '/') { return true; } else if ($source = $this->getSourcePath($path)) { - $storage = \OC\Files\Filesystem::getStorage($source); - return $storage->file_exists($this->getInternalPath($source)); + list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($source); + return $storage->file_exists($internalPath); } return false; } @@ -248,8 +248,8 @@ class Shared extends \OC\Files\Storage\Common { } else { $source = $this->getSourcePath($path); if ($source) { - $storage = \OC\Files\Filesystem::getStorage($source); - return $storage->filectime($this->getInternalPath($source)); + list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($source); + return $storage->filectime($internalPath); } } } @@ -269,8 +269,8 @@ class Shared extends \OC\Files\Storage\Common { } else { $source = $this->getSourcePath($path); if ($source) { - $storage = \OC\Files\Filesystem::getStorage($source); - return $storage->filemtime($this->getInternalPath($source)); + list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($source); + return $storage->filemtime($internalPath); } } } @@ -283,8 +283,8 @@ class Shared extends \OC\Files\Storage\Common { 'source' => $source, ); \OCP\Util::emitHook('\OC\Files\Storage\Shared', 'file_get_contents', $info); - $storage = \OC\Files\Filesystem::getStorage($source); - return $storage->file_get_contents($this->getInternalPath($source)); + list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($source); + return $storage->file_get_contents($internalPath); } } @@ -299,8 +299,8 @@ class Shared extends \OC\Files\Storage\Common { 'source' => $source, ); \OCP\Util::emitHook('\OC\Files\Storage\Shared', 'file_put_contents', $info); - $storage = \OC\Files\Filesystem::getStorage($source); - $result = $storage->file_put_contents($this->getInternalPath($source), $data); + list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($source); + $result = $storage->file_put_contents($internalPath, $data); return $result; } return false; @@ -310,8 +310,8 @@ class Shared extends \OC\Files\Storage\Common { // Delete the file if DELETE permission is granted if ($source = $this->getSourcePath($path)) { if ($this->isDeletable($path)) { - $storage = \OC\Files\Filesystem::getStorage($source); - return $storage->unlink($this->getInternalPath($source)); + list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($source); + return $storage->unlink($internalPath); } else if (dirname($path) == '/' || dirname($path) == '.') { // Unshare the file from the user if in the root of the Shared folder if ($this->is_dir($path)) { @@ -334,8 +334,9 @@ class Shared extends \OC\Files\Storage\Common { if (dirname($path1) == dirname($path2)) { // Rename the file if UPDATE permission is granted if ($this->isUpdatable($path1)) { - $storage = \OC\Files\Filesystem::getStorage($oldSource); - return $storage->rename($this->getInternalPath($oldSource), $this->getInternalPath($newSource)); + list($storage, $oldInternalPath)=\OC\Files\Filesystem::resolvePath($oldSource); + list( , $newInternalPath)=\OC\Files\Filesystem::resolvePath($newSource); + return $storage->rename($oldInternalPath, $newInternalPath); } } else { // Move the file if DELETE and CREATE permissions are granted @@ -349,8 +350,9 @@ class Shared extends \OC\Files\Storage\Common { return $this->unlink($path1); } } else { - $storage = \OC\Files\Filesystem::getStorage($oldSource); - return $storage->rename($this->getInternalPath($oldSource), $this->getInternalPath($newSource)); + list($storage, $oldInternalPath)=\OC\Files\Filesystem::resolvePath($oldSource); + list( , $newInternalPath)=\OC\Files\Filesystem::resolvePath($newSource); + return $storage->rename($oldInternalPath, $newInternalPath); } } } @@ -395,8 +397,8 @@ class Shared extends \OC\Files\Storage\Common { 'mode' => $mode, ); \OCP\Util::emitHook('\OC\Files\Storage\Shared', 'fopen', $info); - $storage = \OC\Files\Filesystem::getStorage($source); - return $storage->fopen($this->getInternalPath($source), $mode); + list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($source); + return $storage->fopen($internalPath, $mode); } return false; } @@ -406,8 +408,8 @@ class Shared extends \OC\Files\Storage\Common { return 'httpd/unix-directory'; } if ($source = $this->getSourcePath($path)) { - $storage = \OC\Files\Filesystem::getStorage($source); - return $storage->getMimeType($this->getInternalPath($source)); + list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($source); + return $storage->getMimeType($internalPath); } return false; } @@ -415,22 +417,22 @@ class Shared extends \OC\Files\Storage\Common { public function free_space($path) { $source = $this->getSourcePath($path); if ($source) { - $storage = \OC\Files\Filesystem::getStorage($source); - return $storage->free_space($this->getInternalPath($source)); + list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($source); + return $storage->free_space($internalPath); } } public function getLocalFile($path) { if ($source = $this->getSourcePath($path)) { - $storage = \OC\Files\Filesystem::getStorage($source); - return $storage->getLocalFile($this->getInternalPath($source)); + list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($source); + return $storage->getLocalFile($internalPath); } return false; } public function touch($path, $mtime = null) { if ($source = $this->getSourcePath($path)) { - $storage = \OC\Files\Filesystem::getStorage($source); - return $storage->touch($this->getInternalPath($source), $mtime); + list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($source); + return $storage->touch($internalPath, $mtime); } return false; } diff --git a/lib/files/filesystem.php b/lib/files/filesystem.php index f23f3a79e9..841f30c767 100644 --- a/lib/files/filesystem.php +++ b/lib/files/filesystem.php @@ -164,37 +164,6 @@ class Filesystem { return $foundMountPoint; } - /** - * get the part of the path relative to the mountpoint of the storage it's stored in - * - * @param string $path - * @return bool - */ - static public function getInternalPath($path) { - $mountPoint = self::getMountPoint($path); - $internalPath = substr($path, strlen($mountPoint)); - return $internalPath; - } - - /** - * get the storage object for a path - * - * @param string $path - * @return \OC\Files\Storage\Storage - */ - static public function getStorage($path) { - $mountpoint = self::getMountPoint($path); - if ($mountpoint) { - if (!isset(self::$storages[$mountpoint])) { - $mount = self::$mounts[$mountpoint]; - self::$storages[$mountpoint] = self::createStorage($mount['class'], $mount['arguments']); - } - return self::$storages[$mountpoint]; - }else{ - return null; - } - } - /** * resolve a path to a storage and internal path * diff --git a/lib/files/view.php b/lib/files/view.php index 230455479c..bffeb434f2 100644 --- a/lib/files/view.php +++ b/lib/files/view.php @@ -68,19 +68,6 @@ class View { return $this->fakeRoot; } - /** - * get the part of the path relative to the mountpoint of the storage it's stored in - * - * @param string $path - * @return bool - */ - public function getInternalPath($path) { - if (!isset($this->internal_path_cache[$path])) { - $this->internal_path_cache[$path] = Filesystem::getInternalPath($this->getAbsolutePath($path)); - } - return $this->internal_path_cache[$path]; - } - /** * get path relative to the root of the view * @@ -103,19 +90,6 @@ class View { } } - /** - * get the storage object for a path - * - * @param string $path - * @return \OC\Files\Storage\Storage - */ - public function getStorage($path) { - if (!isset($this->storage_cache[$path])) { - $this->storage_cache[$path] = Filesystem::getStorage($this->getAbsolutePath($path)); - } - return $this->storage_cache[$path]; - } - /** * get the mountpoint of the storage object for a path ( note: because a storage is not always mounted inside the fakeroot, the returned mountpoint is relative to the absolute root of the filesystem and doesn't take the chroot into account @@ -136,8 +110,9 @@ class View { */ public function getLocalFile($path) { $parent = substr($path, 0, strrpos($path, '/')); - if (Filesystem::isValidPath($parent) and $storage = $this->getStorage($path)) { - return $storage->getLocalFile($this->getInternalPath($path)); + list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($path); + if (Filesystem::isValidPath($parent) and $storage) { + return $storage->getLocalFile($internalPath); } else { return null; } @@ -149,8 +124,9 @@ class View { */ public function getLocalFolder($path) { $parent = substr($path, 0, strrpos($path, '/')); - if (Filesystem::isValidPath($parent) and $storage = $this->getStorage($path)) { - return $storage->getLocalFolder($this->getInternalPath($path)); + list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($path); + if (Filesystem::isValidPath($parent) and $storage) { + return $storage->getLocalFolder($internalPath); } else { return null; } @@ -373,8 +349,10 @@ class View { $mp1 = $this->getMountPoint($path1 . $postFix1); $mp2 = $this->getMountPoint($path2 . $postFix2); if ($mp1 == $mp2) { - if ($storage = $this->getStorage($path1)) { - $result = $storage->rename($this->getInternalPath($path1 . $postFix1), $this->getInternalPath($path2 . $postFix2)); + list($storage, $internalPath1)=\OC\Files\Filesystem::resolvePath($path1 . $postFix1); + list( , $internalPath2)=\OC\Files\Filesystem::resolvePath($path2 . $postFix2); + if ($storage) { + $result = $storage->rename($internalPath1, $internalPath2); } else { $result = false; } @@ -382,8 +360,8 @@ class View { $source = $this->fopen($path1 . $postFix1, 'r'); $target = $this->fopen($path2 . $postFix2, 'w'); $count = \OC_Helper::streamCopy($source, $target); - $storage1 = $this->getStorage($path1); - $storage1->unlink($this->getInternalPath($path1 . $postFix1)); + list($storage1, $internalPath1)=\OC\Files\Filesystem::resolvePath($path1 . $postFix1); + $storage1->unlink($internalPath1); $result = $count > 0; } if ($this->fakeRoot == Filesystem::getRoot()) { @@ -454,8 +432,10 @@ class View { $mp1 = $this->getMountPoint($path1 . $postFix1); $mp2 = $this->getMountPoint($path2 . $postFix2); if ($mp1 == $mp2) { - if ($storage = $this->getStorage($path1 . $postFix1)) { - $result = $storage->copy($this->getInternalPath($path1 . $postFix1), $this->getInternalPath($path2 . $postFix2)); + list($storage, $internalPath1)=\OC\Files\Filesystem::resolvePath($path1 . $postFix1); + list( , $internalPath2)=\OC\Files\Filesystem::resolvePath($path2 . $postFix2); + if ($storage) { + $result = $storage->copy($internalPath1, $internalPath2); } else { $result = false; } @@ -588,8 +568,9 @@ class View { array(Filesystem::signal_param_path => $path) ); } - if ($storage = $this->getStorage($path . $postFix)) { - $result = $storage->hash($type, $this->getInternalPath($path . $postFix), $raw); + list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($path . $postFix); + if ($storage) { + $result = $storage->hash($type, $internalPath, $raw); $result = \OC_FileProxy::runPostProxies('hash', $absolutePath, $result); return $result; } @@ -621,9 +602,9 @@ class View { if ($path == null) { return false; } - $internalPath = $this->getInternalPath($path . $postFix); $run = $this->runHooks($hooks, $path); - if ($run and $storage = $this->getStorage($path . $postFix)) { + list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($path . $postFix); + if ($run and $storage) { if (!is_null($extraParam)) { $result = $storage->$operation($internalPath, $extraParam); } else { diff --git a/lib/filesystem.php b/lib/filesystem.php index 4189fe36c2..587eb50d9e 100644 --- a/lib/filesystem.php +++ b/lib/filesystem.php @@ -44,28 +44,6 @@ class OC_Filesystem { return \OC\Files\Filesystem::getMountPoint($path); } - /** - * get the part of the path relative to the mountpoint of the storage it's stored in - * - * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem - * @param string $path - * @return bool - */ - static public function getInternalPath($path) { - return \OC\Files\Filesystem::getInternalPath($path); - } - - /** - * get the storage object for a path - * - * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem - * @param string $path - * @return \OC\Files\Storage\Storage - */ - static public function getStorage($path) { - return \OC\Files\Filesystem::getStorage($path); - } - /** * resolve a path to a storage and internal path * diff --git a/tests/lib/filesystem.php b/tests/lib/filesystem.php index af3620f570..6e7b4fb781 100644 --- a/tests/lib/filesystem.php +++ b/tests/lib/filesystem.php @@ -51,15 +51,18 @@ class Test_Filesystem extends UnitTestCase { Filesystem::mount('\OC\Files\Storage\Local',self::getStorageData(),'/'); $this->assertEqual('/',Filesystem::getMountPoint('/')); $this->assertEqual('/',Filesystem::getMountPoint('/some/folder')); - $this->assertEqual('',Filesystem::getInternalPath('/')); - $this->assertEqual('some/folder',Filesystem::getInternalPath('/some/folder')); + list( , $internalPath)=\OC\Files\Filesystem::resolvePath('/'); + $this->assertEqual('',$internalPath); + list( , $internalPath)=\OC\Files\Filesystem::resolvePath('/some/folder'); + $this->assertEqual('some/folder',$internalPath); Filesystem::mount('\OC\Files\Storage\Local',self::getStorageData(),'/some'); $this->assertEqual('/',Filesystem::getMountPoint('/')); $this->assertEqual('/some/',Filesystem::getMountPoint('/some/folder')); $this->assertEqual('/some/',Filesystem::getMountPoint('/some/')); $this->assertEqual('/',Filesystem::getMountPoint('/some')); - $this->assertEqual('folder',Filesystem::getInternalPath('/some/folder')); + list( , $internalPath)=\OC\Files\Filesystem::resolvePath('/some/folder'); + $this->assertEqual('folder',$internalPath); } public function testNormalize() { From 542869114ad67295d61450e637b15a1f4d3ae209 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 11 Oct 2012 23:06:57 +0200 Subject: [PATCH 036/272] implement getId for the external storage providers --- apps/files_external/lib/amazons3.php | 6 ++++++ apps/files_external/lib/dropbox.php | 6 ++++++ apps/files_external/lib/ftp.php | 4 ++++ apps/files_external/lib/google.php | 7 ++++++- apps/files_external/lib/smb.php | 4 ++++ apps/files_external/lib/swift.php | 7 +++++++ apps/files_external/lib/webdav.php | 4 ++++ 7 files changed, 37 insertions(+), 1 deletion(-) diff --git a/apps/files_external/lib/amazons3.php b/apps/files_external/lib/amazons3.php index a17a70ed38..2f7c8b35be 100644 --- a/apps/files_external/lib/amazons3.php +++ b/apps/files_external/lib/amazons3.php @@ -29,12 +29,14 @@ class AmazonS3 extends \OC\Files\Storage\Common { private $s3; private $bucket; private $objects = array(); + private $id; private static $tempFiles = array(); // TODO options: storage class, encryption server side, encrypt before upload? public function __construct($params) { + $this->id = 'amazon::'.$params['key'] . md5($params['secret']); $this->s3 = new \AmazonS3(array('key' => $params['key'], 'secret' => $params['secret'])); $this->bucket = $params['bucket']; } @@ -59,6 +61,10 @@ class AmazonS3 extends \OC\Files\Storage\Common { return false; } + public function getId(){ + return $this->id; + } + public function mkdir($path) { // Folders in Amazon S3 are 0 byte objects with a '/' at the end of the name if (substr($path, -1) != '/') { diff --git a/apps/files_external/lib/dropbox.php b/apps/files_external/lib/dropbox.php index 86dbdab9ca..351a2840d9 100755 --- a/apps/files_external/lib/dropbox.php +++ b/apps/files_external/lib/dropbox.php @@ -28,12 +28,14 @@ class Dropbox extends \OC\Files\Storage\Common { private $dropbox; private $root; + private $id; private $metaData = array(); private static $tempFiles = array(); public function __construct($params) { if (isset($params['configured']) && $params['configured'] == 'true' && isset($params['app_key']) && isset($params['app_secret']) && isset($params['token']) && isset($params['token_secret'])) { + $this->id = 'dropbox::'.$params['app_key'] . $params['token']. '/' . $params['root']; $this->root=isset($params['root'])?$params['root']:''; $oauth = new \Dropbox_OAuth_Curl($params['app_key'], $params['app_secret']); $oauth->setToken($params['token'], $params['token_secret']); @@ -81,6 +83,10 @@ class Dropbox extends \OC\Files\Storage\Common { } } + public function getId(){ + return $this->id; + } + public function mkdir($path) { $path = $this->root.$path; try { diff --git a/apps/files_external/lib/ftp.php b/apps/files_external/lib/ftp.php index 140a21ffe1..731581dacc 100644 --- a/apps/files_external/lib/ftp.php +++ b/apps/files_external/lib/ftp.php @@ -32,6 +32,10 @@ class FTP extends \OC\Files\Storage\StreamWrapper{ } } + public function getId(){ + return 'ftp::' . $this->user . '@' . $this->host . '/' . $this->root; + } + /** * construct the ftp url * @param string path diff --git a/apps/files_external/lib/google.php b/apps/files_external/lib/google.php index 51c5d219aa..e4be4de5a0 100644 --- a/apps/files_external/lib/google.php +++ b/apps/files_external/lib/google.php @@ -30,6 +30,7 @@ class Google extends \OC\Files\Storage\Common { private $oauth_token; private $sig_method; private $entries; + private $id; private static $tempFiles = array(); @@ -37,6 +38,7 @@ class Google extends \OC\Files\Storage\Common { if (isset($params['configured']) && $params['configured'] == 'true' && isset($params['token']) && isset($params['token_secret'])) { $consumer_key = isset($params['consumer_key']) ? $params['consumer_key'] : 'anonymous'; $consumer_secret = isset($params['consumer_secret']) ? $params['consumer_secret'] : 'anonymous'; + $this->id = 'google::' . $consumer_key . $consumer_secret; $this->consumer = new \OAuthConsumer($consumer_key, $consumer_secret); $this->oauth_token = new \OAuthToken($params['token'], $params['token_secret']); $this->sig_method = new \OAuthSignatureMethod_HMAC_SHA1(); @@ -177,6 +179,9 @@ class Google extends \OC\Files\Storage\Common { } } + public function getId(){ + return $this->id; + } public function mkdir($path) { $collection = dirname($path); @@ -539,4 +544,4 @@ class Google extends \OC\Files\Storage\Common { } -} \ No newline at end of file +} diff --git a/apps/files_external/lib/smb.php b/apps/files_external/lib/smb.php index 8fcdd1f722..71c7a91ec9 100644 --- a/apps/files_external/lib/smb.php +++ b/apps/files_external/lib/smb.php @@ -42,6 +42,10 @@ class SMB extends \OC\Files\Storage\StreamWrapper{ } } + public function getId(){ + return 'smb::' . $this->user . '@' . $this->host . '/' . $this->share . '/' . $this->root; + } + public function constructUrl($path) { if(substr($path,-1)=='/') { $path=substr($path,0,-1); diff --git a/apps/files_external/lib/swift.php b/apps/files_external/lib/swift.php index 8d402b2521..61f58766e8 100644 --- a/apps/files_external/lib/swift.php +++ b/apps/files_external/lib/swift.php @@ -11,6 +11,7 @@ require_once 'php-cloudfiles/cloudfiles.php'; namespace OC\Files\Storage; class SWIFT extends \OC\Files\Storage\Common{ + private $id; private $host; private $root; private $user; @@ -274,6 +275,8 @@ class SWIFT extends \OC\Files\Storage\Common{ $this->user=$params['user']; $this->root=isset($params['root'])?$params['root']:'/'; $this->secure=isset($params['secure'])?(bool)$params['secure']:true; + + $this->id = 'swift::' . $this->user . '@' . $this->host . '/' . $this->root; if(!$this->root || $this->root[0]!='/') { $this->root='/'.$this->root; } @@ -289,6 +292,10 @@ class SWIFT extends \OC\Files\Storage\Common{ } } + public function getId(){ + return $this->id; + } + public function mkdir($path) { if($this->containerExists($path)) { diff --git a/apps/files_external/lib/webdav.php b/apps/files_external/lib/webdav.php index 7c61d06a01..6ed93e9035 100644 --- a/apps/files_external/lib/webdav.php +++ b/apps/files_external/lib/webdav.php @@ -56,6 +56,10 @@ class DAV extends \OC\Files\Storage\Common{ $this->mkdir(''); } + public function getId(){ + return 'webdav::' . $this->user . '@' . $this->host . '/' . $this->root; + } + private function createBaseUri() { $baseUri='http'; if($this->secure) { From 141ff806c6cfbd349ffa232502a89a620882c409 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 11 Oct 2012 23:17:59 +0200 Subject: [PATCH 037/272] fix namespace problems for external storage backends --- apps/files_external/lib/amazons3.php | 4 +- apps/files_external/lib/dropbox.php | 4 +- apps/files_external/lib/ftp.php | 7 +-- apps/files_external/lib/google.php | 12 ++--- apps/files_external/lib/smb.php | 5 +- apps/files_external/lib/swift.php | 69 ++++++++++++++-------------- apps/files_external/lib/webdav.php | 32 ++++++------- apps/files_external/tests/config.php | 2 +- 8 files changed, 65 insertions(+), 70 deletions(-) diff --git a/apps/files_external/lib/amazons3.php b/apps/files_external/lib/amazons3.php index 2f7c8b35be..832127d3e7 100644 --- a/apps/files_external/lib/amazons3.php +++ b/apps/files_external/lib/amazons3.php @@ -20,10 +20,10 @@ * License along with this library. If not, see . */ -require_once 'aws-sdk/sdk.class.php'; - namespace OC\Files\Storage; +require_once 'aws-sdk/sdk.class.php'; + class AmazonS3 extends \OC\Files\Storage\Common { private $s3; diff --git a/apps/files_external/lib/dropbox.php b/apps/files_external/lib/dropbox.php index 351a2840d9..0f7593162e 100755 --- a/apps/files_external/lib/dropbox.php +++ b/apps/files_external/lib/dropbox.php @@ -20,10 +20,10 @@ * License along with this library. If not, see . */ -require_once 'Dropbox/autoload.php'; - namespace OC\Files\Storage; +require_once 'Dropbox/autoload.php'; + class Dropbox extends \OC\Files\Storage\Common { private $dropbox; diff --git a/apps/files_external/lib/ftp.php b/apps/files_external/lib/ftp.php index 731581dacc..dea44728f3 100644 --- a/apps/files_external/lib/ftp.php +++ b/apps/files_external/lib/ftp.php @@ -26,7 +26,7 @@ class FTP extends \OC\Files\Storage\StreamWrapper{ if(!$this->root || $this->root[0]!='/') { $this->root='/'.$this->root; } - //create the root folder if necesary + //create the root folder if necessary if (!$this->is_dir('')) { $this->mkdir(''); } @@ -38,7 +38,7 @@ class FTP extends \OC\Files\Storage\StreamWrapper{ /** * construct the ftp url - * @param string path + * @param string $path * @return string */ public function constructUrl($path) { @@ -74,7 +74,7 @@ class FTP extends \OC\Files\Storage\StreamWrapper{ }else{ $ext=''; } - $tmpFile=OCP\Files::tmpFile($ext); + $tmpFile=\OCP\Files::tmpFile($ext); \OC_CloseStreamWrapper::$callBacks[$tmpFile]=array($this,'writeBack'); if($this->file_exists($path)) { $this->getFile($path,$tmpFile); @@ -82,6 +82,7 @@ class FTP extends \OC\Files\Storage\StreamWrapper{ self::$tempFiles[$tmpFile]=$path; return fopen('close://'.$tmpFile,$mode); } + return false; } public function writeBack($tmpFile) { diff --git a/apps/files_external/lib/google.php b/apps/files_external/lib/google.php index e4be4de5a0..141fc619e3 100644 --- a/apps/files_external/lib/google.php +++ b/apps/files_external/lib/google.php @@ -20,10 +20,10 @@ * License along with this library. If not, see . */ -require_once 'Google/common.inc.php'; - namespace OC\Files\Storage; +require_once 'Google/common.inc.php'; + class Google extends \OC\Files\Storage\Common { private $consumer; @@ -62,7 +62,7 @@ class Google extends \OC\Files\Storage\Common { $tempStr .= '&' . urlencode($key) . '=' . urlencode($value); } $uri = preg_replace('/&/', '?', $tempStr, 1); - $request = OAuthRequest::from_consumer_and_token($this->consumer, $this->oauth_token, $httpMethod, $uri, $params); + $request = \OAuthRequest::from_consumer_and_token($this->consumer, $this->oauth_token, $httpMethod, $uri, $params); $request->sign_request($this->sig_method, $this->consumer, $this->oauth_token); $auth_header = $request->to_header(); $headers = array($auth_header, 'GData-Version: 3.0'); @@ -129,7 +129,7 @@ class Google extends \OC\Files\Storage\Common { private function getFeed($feedUri, $httpMethod, $postData = null) { $result = $this->sendRequest($feedUri, $httpMethod, $postData); if ($result) { - $dom = new DOMDocument(); + $dom = new \DOMDocument(); $dom->loadXML($result); return $dom; } @@ -248,7 +248,7 @@ class Google extends \OC\Files\Storage\Common { $this->entries[$name] = $entry; } } - OC_FakeDirStream::$dirs['google'.$path] = $files; + \OC_FakeDirStream::$dirs['google'.$path] = $files; return opendir('fakedir://google'.$path); } @@ -407,7 +407,7 @@ class Google extends \OC\Files\Storage\Common { $ext = ''; } $tmpFile = \OC_Helper::tmpFile($ext); - OC_CloseStreamWrapper::$callBacks[$tmpFile] = array($this, 'writeBack'); + \OC_CloseStreamWrapper::$callBacks[$tmpFile] = array($this, 'writeBack'); if ($this->file_exists($path)) { $source = $this->fopen($path, 'r'); file_put_contents($tmpFile, $source); diff --git a/apps/files_external/lib/smb.php b/apps/files_external/lib/smb.php index 71c7a91ec9..dfba3105ec 100644 --- a/apps/files_external/lib/smb.php +++ b/apps/files_external/lib/smb.php @@ -6,10 +6,10 @@ * See the COPYING-README file. */ -require_once 'smb4php/smb.php'; - namespace OC\Files\Storage; +require_once 'smb4php/smb.php'; + class SMB extends \OC\Files\Storage\StreamWrapper{ private $password; private $user; @@ -70,6 +70,7 @@ class SMB extends \OC\Files\Storage\StreamWrapper{ /** * check if a file or folder has been updated since $time + * @param string $path * @param int $time * @return bool */ diff --git a/apps/files_external/lib/swift.php b/apps/files_external/lib/swift.php index 61f58766e8..4b3f38166d 100644 --- a/apps/files_external/lib/swift.php +++ b/apps/files_external/lib/swift.php @@ -6,10 +6,10 @@ * See the COPYING-README file. */ -require_once 'php-cloudfiles/cloudfiles.php'; - namespace OC\Files\Storage; +require_once 'php-cloudfiles/cloudfiles.php'; + class SWIFT extends \OC\Files\Storage\Common{ private $id; private $host; @@ -18,15 +18,15 @@ class SWIFT extends \OC\Files\Storage\Common{ private $token; private $secure; /** - * @var CF_Authentication auth + * @var \CF_Authentication auth */ private $auth; /** - * @var CF_Connection conn + * @var \CF_Connection conn */ private $conn; /** - * @var CF_Container rootContainer + * @var \CF_Container rootContainer */ private $rootContainer; @@ -38,7 +38,7 @@ class SWIFT extends \OC\Files\Storage\Common{ /** * translate directory path to container name - * @param string path + * @param string $path * @return string */ private function getContainerName($path) { @@ -48,8 +48,8 @@ class SWIFT extends \OC\Files\Storage\Common{ /** * get container by path - * @param string path - * @return CF_Container + * @param string $path + * @return \CF_Container */ private function getContainer($path) { if($path=='' or $path=='/') { @@ -62,15 +62,15 @@ class SWIFT extends \OC\Files\Storage\Common{ $container=$this->conn->get_container($this->getContainerName($path)); $this->containers[$path]=$container; return $container; - }catch(NoSuchContainerException $e) { + }catch(\NoSuchContainerException $e) { return null; } } /** * create container - * @param string path - * @return CF_Container + * @param string $path + * @return \CF_Container */ private function createContainer($path) { if($path=='' or $path=='/' or $path=='.') { @@ -92,8 +92,8 @@ class SWIFT extends \OC\Files\Storage\Common{ /** * get object by path - * @param string path - * @return CF_Object + * @param string $path + * @return \CF_Object */ private function getObject($path) { if(isset($this->objects[$path])) { @@ -110,7 +110,7 @@ class SWIFT extends \OC\Files\Storage\Common{ $obj=$container->get_object(basename($path)); $this->objects[$path]=$obj; return $obj; - }catch(NoSuchObjectException $e) { + }catch(\NoSuchObjectException $e) { return null; } } @@ -135,8 +135,8 @@ class SWIFT extends \OC\Files\Storage\Common{ /** * create object - * @param string path - * @return CF_Object + * @param string $path + * @return \CF_Object */ private function createObject($path) { $container=$this->getContainer(dirname($path)); @@ -157,7 +157,7 @@ class SWIFT extends \OC\Files\Storage\Common{ /** * check if container for path exists - * @param string path + * @param string $path * @return bool */ private function containerExists($path) { @@ -166,15 +166,15 @@ class SWIFT extends \OC\Files\Storage\Common{ /** * get the list of emulated sub containers - * @param CF_Container container + * @param \CF_Container $container * @return array */ private function getSubContainers($container) { - $tmpFile=OCP\Files::tmpFile(); + $tmpFile=\OCP\Files::tmpFile(); $obj=$this->getSubContainerFile($container); try{ $obj->save_to_filename($tmpFile); - }catch(Exception $e) { + }catch(\Exception $e) { return array(); } $obj->save_to_filename($tmpFile); @@ -188,15 +188,15 @@ class SWIFT extends \OC\Files\Storage\Common{ /** * add an emulated sub container - * @param CF_Container container - * @param string name + * @param CF_Container $container + * @param string $name * @return bool */ private function addSubContainer($container,$name) { if(!$name) { return false; } - $tmpFile=OCP\Files::tmpFile(); + $tmpFile=\OCP\Files::tmpFile(); $obj=$this->getSubContainerFile($container); try{ $obj->save_to_filename($tmpFile); @@ -211,8 +211,7 @@ class SWIFT extends \OC\Files\Storage\Common{ $fh=fopen($tmpFile,'a'); fwrite($fh,$name."\n"); } - }catch(Exception $e) { - $containers=array(); + }catch(\Exception $e) { file_put_contents($tmpFile,$name."\n"); } @@ -223,20 +222,20 @@ class SWIFT extends \OC\Files\Storage\Common{ /** * remove an emulated sub container - * @param CF_Container container - * @param string name + * @param CF_Container $container + * @param string $name * @return bool */ private function removeSubContainer($container,$name) { if(!$name) { return false; } - $tmpFile=OCP\Files::tmpFile(); + $tmpFile=\OCP\Files::tmpFile(); $obj=$this->getSubContainerFile($container); try{ $obj->save_to_filename($tmpFile); $containers=file($tmpFile); - }catch(Exception $e) { + }catch(\Exception $e) { return false; } foreach($containers as &$sub) { @@ -258,13 +257,13 @@ class SWIFT extends \OC\Files\Storage\Common{ /** * ensure a subcontainer file exists and return it's object - * @param CF_Container container - * @return CF_Object + * @param \CF_Container $container + * @return \CF_Object */ private function getSubContainerFile($container) { try{ return $container->get_object(self::SUBCONTAINER_FILE); - }catch(NoSuchObjectException $e) { + }catch(\NoSuchObjectException $e) { return $container->create_object(self::SUBCONTAINER_FILE); } } @@ -526,11 +525,11 @@ class SWIFT extends \OC\Files\Storage\Common{ private function getTmpFile($path) { $obj=$this->getObject($path); if(!is_null($obj)) { - $tmpFile=OCP\Files::tmpFile(); + $tmpFile=\OCP\Files::tmpFile(); $obj->save_to_filename($tmpFile); return $tmpFile; }else{ - return OCP\Files::tmpFile(); + return \OCP\Files::tmpFile(); } } @@ -545,7 +544,7 @@ class SWIFT extends \OC\Files\Storage\Common{ /** * remove custom mtime metadata - * @param CF_Object obj + * @param \CF_Object $obj */ private function resetMTime($obj) { if(isset($obj->metadata['Mtime'])) { diff --git a/apps/files_external/lib/webdav.php b/apps/files_external/lib/webdav.php index 6ed93e9035..73231b6ad8 100644 --- a/apps/files_external/lib/webdav.php +++ b/apps/files_external/lib/webdav.php @@ -15,7 +15,7 @@ class DAV extends \OC\Files\Storage\Common{ private $secure; private $root; /** - * @var Sabre_DAV_Client + * @var \Sabre_DAV_Client */ private $client; @@ -92,7 +92,7 @@ class DAV extends \OC\Files\Storage\Common{ \OC_FakeDirStream::$dirs[$id][]=$file; } return opendir('fakedir://'.$id); - }catch(Exception $e) { + }catch(\Exception $e) { return false; } } @@ -103,7 +103,7 @@ class DAV extends \OC\Files\Storage\Common{ $response=$this->client->propfind($path, array('{DAV:}resourcetype')); $responseType=$response["{DAV:}resourcetype"]->resourceType; return (count($responseType)>0 and $responseType[0]=="{DAV:}collection")?'dir':'file'; - }catch(Exception $e) { + }catch(\Exception $e) { error_log($e->getMessage()); \OCP\Util::writeLog("webdav client", \OCP\Util::sanitizeHTML($e->getMessage()), \OCP\Util::ERROR); return false; @@ -123,7 +123,7 @@ class DAV extends \OC\Files\Storage\Common{ try{ $this->client->propfind($path, array('{DAV:}resourcetype')); return true;//no 404 exception - }catch(Exception $e) { + }catch(\Exception $e) { return false; } } @@ -169,7 +169,7 @@ class DAV extends \OC\Files\Storage\Common{ }else{ $ext=''; } - $tmpFile=OCP\Files::tmpFile($ext); + $tmpFile=\OCP\Files::tmpFile($ext); \OC_CloseStreamWrapper::$callBacks[$tmpFile]=array($this,'writeBack'); if($this->file_exists($path)) { $this->getFile($path,$tmpFile); @@ -195,7 +195,7 @@ class DAV extends \OC\Files\Storage\Common{ }else{ return 0; } - }catch(Exception $e) { + }catch(\Exception $e) { return 0; } } @@ -231,12 +231,9 @@ class DAV extends \OC\Files\Storage\Common{ $path1=$this->cleanPath($path1); $path2=$this->root.$this->cleanPath($path2); try{ - $response=$this->client->request('MOVE',$path1,null,array('Destination'=>$path2)); + $this->client->request('MOVE',$path1,null,array('Destination'=>$path2)); return true; - }catch(Exception $e) { - echo $e; - echo 'fail'; - var_dump($response); + }catch(\Exception $e) { return false; } } @@ -245,12 +242,9 @@ class DAV extends \OC\Files\Storage\Common{ $path1=$this->cleanPath($path1); $path2=$this->root.$this->cleanPath($path2); try{ - $response=$this->client->request('COPY',$path1,null,array('Destination'=>$path2)); + $this->client->request('COPY',$path1,null,array('Destination'=>$path2)); return true; - }catch(Exception $e) { - echo $e; - echo 'fail'; - var_dump($response); + }catch(\Exception $e) { return false; } } @@ -264,7 +258,7 @@ class DAV extends \OC\Files\Storage\Common{ 'size'=>(int)isset($response['{DAV:}getcontentlength']) ? $response['{DAV:}getcontentlength'] : 0, 'ctime'=>-1, ); - }catch(Exception $e) { + }catch(\Exception $e) { return array(); } } @@ -282,7 +276,7 @@ class DAV extends \OC\Files\Storage\Common{ }else{ return false; } - }catch(Exception $e) { + }catch(\Exception $e) { return false; } } @@ -300,7 +294,7 @@ class DAV extends \OC\Files\Storage\Common{ try{ $response=$this->client->request($method,$path,$body); return $response['statusCode']==$expected; - }catch(Exception $e) { + }catch(\Exception $e) { return false; } } diff --git a/apps/files_external/tests/config.php b/apps/files_external/tests/config.php index ff16b1c1d8..5af317675c 100644 --- a/apps/files_external/tests/config.php +++ b/apps/files_external/tests/config.php @@ -8,7 +8,7 @@ return array( 'root'=>'/test', ), 'webdav'=>array( - 'run'=>false, + 'run'=>true, 'host'=>'localhost', 'user'=>'test', 'password'=>'test', From cfa036eaa916c1adf38fb161a16c50cd050c0a3b Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sat, 20 Oct 2012 23:54:37 +0200 Subject: [PATCH 038/272] drop filectime from the filesystem api's --- apps/files_external/lib/amazons3.php | 2 -- apps/files_external/lib/dropbox.php | 1 - apps/files_external/lib/google.php | 2 -- apps/files_external/lib/swift.php | 2 -- apps/files_external/lib/webdav.php | 1 - apps/files_sharing/lib/sharedstorage.php | 22 ---------------------- lib/files.php | 3 +-- lib/files/filesystem.php | 4 ---- lib/files/storage/common.php | 4 ---- lib/files/storage/local.php | 3 --- lib/files/storage/storage.php | 1 - lib/files/view.php | 4 ---- lib/filesystem.php | 7 ------- 13 files changed, 1 insertion(+), 55 deletions(-) diff --git a/apps/files_external/lib/amazons3.php b/apps/files_external/lib/amazons3.php index 832127d3e7..c3fa4651f6 100644 --- a/apps/files_external/lib/amazons3.php +++ b/apps/files_external/lib/amazons3.php @@ -115,12 +115,10 @@ class AmazonS3 extends \OC\Files\Storage\Common { $stat['size'] = $this->s3->get_bucket_filesize($this->bucket); $stat['atime'] = time(); $stat['mtime'] = $stat['atime']; - $stat['ctime'] = $stat['atime']; } else if ($object = $this->getObject($path)) { $stat['size'] = $object['Size']; $stat['atime'] = time(); $stat['mtime'] = strtotime($object['LastModified']); - $stat['ctime'] = $stat['mtime']; } if (isset($stat)) { return $stat; diff --git a/apps/files_external/lib/dropbox.php b/apps/files_external/lib/dropbox.php index 0f7593162e..0e82f80668 100755 --- a/apps/files_external/lib/dropbox.php +++ b/apps/files_external/lib/dropbox.php @@ -119,7 +119,6 @@ class Dropbox extends \OC\Files\Storage\Common { $stat['size'] = $metaData['bytes']; $stat['atime'] = time(); $stat['mtime'] = (isset($metaData['modified'])) ? strtotime($metaData['modified']) : time(); - $stat['ctime'] = $stat['mtime']; return $stat; } return false; diff --git a/apps/files_external/lib/google.php b/apps/files_external/lib/google.php index 141fc619e3..7ee5b97f8b 100644 --- a/apps/files_external/lib/google.php +++ b/apps/files_external/lib/google.php @@ -257,14 +257,12 @@ class Google extends \OC\Files\Storage\Common { $stat['size'] = $this->free_space($path); $stat['atime'] = time(); $stat['mtime'] = time(); - $stat['ctime'] = time(); } else if ($entry = $this->getResource($path)) { // NOTE: Native resources don't have a file size $stat['size'] = $entry->getElementsByTagNameNS('http://schemas.google.com/g/2005', 'quotaBytesUsed')->item(0)->nodeValue; // if (isset($atime = $entry->getElementsByTagNameNS('http://schemas.google.com/g/2005', 'lastViewed')->item(0)->nodeValue)) // $stat['atime'] = strtotime($entry->getElementsByTagNameNS('http://schemas.google.com/g/2005', 'lastViewed')->item(0)->nodeValue); $stat['mtime'] = strtotime($entry->getElementsByTagName('updated')->item(0)->nodeValue); - $stat['ctime'] = strtotime($entry->getElementsByTagName('published')->item(0)->nodeValue); } if (isset($stat)) { return $stat; diff --git a/apps/files_external/lib/swift.php b/apps/files_external/lib/swift.php index 4b3f38166d..c3578b0c23 100644 --- a/apps/files_external/lib/swift.php +++ b/apps/files_external/lib/swift.php @@ -500,7 +500,6 @@ class SWIFT extends \OC\Files\Storage\Common{ return array( 'mtime'=>-1, 'size'=>$container->bytes_used, - 'ctime'=>-1 ); } @@ -518,7 +517,6 @@ class SWIFT extends \OC\Files\Storage\Common{ return array( 'mtime'=>$mtime, 'size'=>$obj->content_length, - 'ctime'=>-1, ); } diff --git a/apps/files_external/lib/webdav.php b/apps/files_external/lib/webdav.php index 73231b6ad8..c9785f3eb1 100644 --- a/apps/files_external/lib/webdav.php +++ b/apps/files_external/lib/webdav.php @@ -256,7 +256,6 @@ class DAV extends \OC\Files\Storage\Common{ return array( 'mtime'=>strtotime($response['{DAV:}getlastmodified']), 'size'=>(int)isset($response['{DAV:}getcontentlength']) ? $response['{DAV:}getcontentlength'] : 0, - 'ctime'=>-1, ); }catch(\Exception $e) { return array(); diff --git a/apps/files_sharing/lib/sharedstorage.php b/apps/files_sharing/lib/sharedstorage.php index e12027a4f2..521dfb69f2 100644 --- a/apps/files_sharing/lib/sharedstorage.php +++ b/apps/files_sharing/lib/sharedstorage.php @@ -162,7 +162,6 @@ class Shared extends \OC\Files\Storage\Common { if ($path == '' || $path == '/') { $stat['size'] = $this->filesize($path); $stat['mtime'] = $this->filemtime($path); - $stat['ctime'] = $this->filectime($path); return $stat; } else if ($source = $this->getSourcePath($path)) { list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($source); @@ -233,27 +232,6 @@ class Shared extends \OC\Files\Storage\Common { return false; } - public function filectime($path) { - if ($path == '' || $path == '/') { - $ctime = 0; - if ($dh = $this->opendir($path)) { - while (($filename = readdir($dh)) !== false) { - $tempctime = $this->filectime($filename); - if ($tempctime < $ctime) { - $ctime = $tempctime; - } - } - } - return $ctime; - } else { - $source = $this->getSourcePath($path); - if ($source) { - list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($source); - return $storage->filectime($internalPath); - } - } - } - public function filemtime($path) { if ($path == '' || $path == '/') { $mtime = 0; diff --git a/lib/files.php b/lib/files.php index 29322cf2d0..28c8d0b449 100644 --- a/lib/files.php +++ b/lib/files.php @@ -30,13 +30,12 @@ class OC_Files { /** * get the filesystem info - * @param string path + * @param string $path * @return array * * returns an associative array with the following keys: * - size * - mtime - * - ctime * - mimetype * - encrypted * - versioned diff --git a/lib/files/filesystem.php b/lib/files/filesystem.php index e4c269257c..94e8a56256 100644 --- a/lib/files/filesystem.php +++ b/lib/files/filesystem.php @@ -478,10 +478,6 @@ class Filesystem { return self::$defaultInstance->file_exists($path); } - static public function filectime($path) { - return self::$defaultInstance->filectime($path); - } - static public function filemtime($path) { return self::$defaultInstance->filemtime($path); } diff --git a/lib/files/storage/common.php b/lib/files/storage/common.php index 3886a04182..1740584555 100644 --- a/lib/files/storage/common.php +++ b/lib/files/storage/common.php @@ -74,10 +74,6 @@ abstract class Common extends \OC\Files\Storage\Storage { return $permissions; } // abstract public function file_exists($path); - public function filectime($path) { - $stat = $this->stat($path); - return $stat['ctime']; - } public function filemtime($path) { $stat = $this->stat($path); return $stat['mtime']; diff --git a/lib/files/storage/local.php b/lib/files/storage/local.php index 66ae5542e3..ccd69e3971 100644 --- a/lib/files/storage/local.php +++ b/lib/files/storage/local.php @@ -66,9 +66,6 @@ class Local extends \OC\Files\Storage\Common{ public function file_exists($path) { return file_exists($this->datadir.$path); } - public function filectime($path) { - return filectime($this->datadir.$path); - } public function filemtime($path) { return filemtime($this->datadir.$path); } diff --git a/lib/files/storage/storage.php b/lib/files/storage/storage.php index f149c61830..941bc3b077 100644 --- a/lib/files/storage/storage.php +++ b/lib/files/storage/storage.php @@ -29,7 +29,6 @@ abstract class Storage{ abstract public function isSharable($path); abstract public function getPermissions($path); abstract public function file_exists($path); - abstract public function filectime($path); abstract public function filemtime($path); abstract public function file_get_contents($path); abstract public function file_put_contents($path,$data); diff --git a/lib/files/view.php b/lib/files/view.php index bffeb434f2..58e3ee6f05 100644 --- a/lib/files/view.php +++ b/lib/files/view.php @@ -236,10 +236,6 @@ class View { return $this->basicOperation('file_exists', $path); } - public function filectime($path) { - return $this->basicOperation('filectime', $path); - } - public function filemtime($path) { return $this->basicOperation('filemtime', $path); } diff --git a/lib/filesystem.php b/lib/filesystem.php index 587eb50d9e..9ce75aaf8c 100644 --- a/lib/filesystem.php +++ b/lib/filesystem.php @@ -306,13 +306,6 @@ class OC_Filesystem { return \OC\Files\Filesystem::file_exists($path); } - /** - * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem - */ - static public function filectime($path) { - return \OC\Files\Filesystem::filectime($path); - } - /** * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem */ From 1901ac8b17c99eef3ccd99edf877fd0a86737d17 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sun, 21 Oct 2012 00:13:16 +0200 Subject: [PATCH 039/272] drop depricated is_readable and is_writable --- lib/files.php | 2 +- lib/files/filesystem.php | 14 -------------- lib/files/view.php | 14 -------------- lib/filesystem.php | 7 ------- 4 files changed, 1 insertion(+), 36 deletions(-) diff --git a/lib/files.php b/lib/files.php index 28c8d0b449..ba111bee96 100644 --- a/lib/files.php +++ b/lib/files.php @@ -178,7 +178,7 @@ class OC_Files { $filename=$dir.'/'.$files; } @ob_end_clean(); - if($zip or \OC\Files\Filesystem::is_readable($filename)) { + if($zip or \OC\Files\Filesystem::isReadable($filename)) { header('Content-Disposition: attachment; filename="'.basename($filename).'"'); header('Content-Transfer-Encoding: binary'); OC_Response::disableCaching(); diff --git a/lib/files/filesystem.php b/lib/files/filesystem.php index 94e8a56256..0dae774feb 100644 --- a/lib/files/filesystem.php +++ b/lib/files/filesystem.php @@ -440,20 +440,6 @@ class Filesystem { return self::$defaultInstance->readfile($path); } - /** - * @deprecated Replaced by isReadable() as part of CRUDS - */ - static public function is_readable($path) { - return self::$defaultInstance->isReadable($path); - } - - /** - * @deprecated Replaced by isCreatable(), isUpdatable(), isDeletable() as part of CRUDS - */ - static public function is_writable($path) { - return self::$defaultInstance->is_writable($path); - } - static public function isCreatable($path) { return self::$defaultInstance->isCreatable($path); } diff --git a/lib/files/view.php b/lib/files/view.php index 58e3ee6f05..a59ad8105d 100644 --- a/lib/files/view.php +++ b/lib/files/view.php @@ -195,20 +195,6 @@ class View { return false; } - /** - * @deprecated Replaced by isReadable() as part of CRUDS - */ - public function is_readable($path) { - return $this->basicOperation('isReadable', $path); - } - - /** - * @deprecated Replaced by isCreatable(), isUpdatable(), isDeletable() as part of CRUDS - */ - public function is_writable($path) { - return $this->basicOperation('isUpdatable', $path); - } - public function isCreatable($path) { return $this->basicOperation('isCreatable', $path); } diff --git a/lib/filesystem.php b/lib/filesystem.php index 9ce75aaf8c..ea0a289c8d 100644 --- a/lib/filesystem.php +++ b/lib/filesystem.php @@ -257,13 +257,6 @@ class OC_Filesystem { return \OC\Files\Filesystem::isReadable($path); } - /** - * @deprecated Replaced by isCreatable(), isUpdatable(), isDeletable() as part of CRUDS - */ - static public function is_writable($path) { - return \OC\Files\Filesystem::is_writable($path); - } - /** * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem */ From 6f1fbf97f7678f7a9a8b2e92d1cdd24fc5710d17 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sun, 21 Oct 2012 00:27:55 +0200 Subject: [PATCH 040/272] some namespaces fixed for the shared backend --- apps/files_sharing/lib/sharedstorage.php | 25 +++++++----------------- 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/apps/files_sharing/lib/sharedstorage.php b/apps/files_sharing/lib/sharedstorage.php index 521dfb69f2..e354314cc3 100644 --- a/apps/files_sharing/lib/sharedstorage.php +++ b/apps/files_sharing/lib/sharedstorage.php @@ -37,7 +37,7 @@ class Shared extends \OC\Files\Storage\Common { /** * @brief Get the source file path and the permissions granted for a shared file * @param string Shared target file path - * @return Returns array with the keys path and permissions or false if not found + * @return array with the keys path and permissions or false if not found */ private function getFile($target) { $target = '/'.$target; @@ -52,7 +52,7 @@ class Shared extends \OC\Files\Storage\Common { if (isset($this->files[$folder])) { $file = $this->files[$folder]; } else { - $file = OCP\Share::getItemSharedWith('folder', $folder, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE); + $file = \OCP\Share::getItemSharedWith('folder', $folder, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE); } if ($file) { $this->files[$target]['path'] = $file['path'].substr($target, strlen($folder)); @@ -60,13 +60,13 @@ class Shared extends \OC\Files\Storage\Common { return $this->files[$target]; } } else { - $file = OCP\Share::getItemSharedWith('file', $target, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE); + $file = \OCP\Share::getItemSharedWith('file', $target, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE); if ($file) { $this->files[$target] = $file; return $this->files[$target]; } } - OCP\Util::writeLog('files_sharing', 'File source not found for: '.$target, OCP\Util::ERROR); + \OCP\Util::writeLog('files_sharing', 'File source not found for: '.$target, \OCP\Util::ERROR); return false; } } @@ -74,13 +74,13 @@ class Shared extends \OC\Files\Storage\Common { /** * @brief Get the source file path for a shared file * @param string Shared target file path - * @return Returns source file path or false if not found + * @return string source file path or false if not found */ private function getSourcePath($target) { $file = $this->getFile($target); if (isset($file['path'])) { $uid = substr($file['path'], 1, strpos($file['path'], '/', 1) - 1); - \\OC\Files\Filesystem::mount('\OC\Files\Storage\Local', array('datadir' => \OC_User::getHome($uid)), $uid); + \OC\Files\Filesystem::mount('\OC\Files\Storage\Local', array('datadir' => \OC_User::getHome($uid)), $uid); return $file['path']; } return false; @@ -89,7 +89,7 @@ class Shared extends \OC\Files\Storage\Common { /** * @brief Get the permissions granted for a shared file * @param string Shared target file path - * @return Returns CRUDS permissions granted or false if not found + * @return int CRUDS permissions granted or false if not found */ public function getPermissions($target) { $file = $this->getFile($target); @@ -99,17 +99,6 @@ class Shared extends \OC\Files\Storage\Common { return false; } - /** - * @brief Get the internal path to pass to the storage filesystem call - * @param string Source file path - * @return Source file path with mount point stripped out - */ - private function getInternalPath($path) { - $mountPoint = \OC\Files\Filesystem::getMountPoint($path); - $internalPath = substr($path, strlen($mountPoint)); - return $internalPath; - } - public function mkdir($path) { if ($path == '' || $path == '/' || !$this->isCreatable(dirname($path))) { return false; From 3ff0772a05c70592360c7b11b280cc4cf45a385e Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sun, 21 Oct 2012 00:31:32 +0200 Subject: [PATCH 041/272] add getCache and getScanner to storage api in order to allow storage backends to overwride caching behaviour --- lib/files/storage/common.php | 50 ++++++------------------- lib/files/storage/storage.php | 70 ++++++++++++++++++----------------- 2 files changed, 48 insertions(+), 72 deletions(-) diff --git a/lib/files/storage/common.php b/lib/files/storage/common.php index 1740584555..de02c0d5d8 100644 --- a/lib/files/storage/common.php +++ b/lib/files/storage/common.php @@ -20,7 +20,7 @@ namespace OC\Files\Storage; * in classes which extend it, e.g. $this->stat() . */ -abstract class Common extends \OC\Files\Storage\Storage { +abstract class Common implements \OC\Files\Storage\Storage { public function __construct($parameters) {} // abstract public function getId(); @@ -123,64 +123,30 @@ abstract class Common extends \OC\Files\Storage\Storage { * deleted together with its contents. To avoid this set $empty to true */ public function deleteAll( $directory, $empty = false ) { - - // strip leading slash - if( substr( $directory, 0, 1 ) == "/" ) { - - $directory = substr( $directory, 1 ); - - } - - // strip trailing slash - if( substr( $directory, -1) == "/" ) { - - $directory = substr( $directory, 0, -1 ); - - } + $directory = trim($directory,'/'); if ( !$this->file_exists( \OCP\USER::getUser() . '/' . $directory ) || !$this->is_dir( \OCP\USER::getUser() . '/' . $directory ) ) { - return false; - } elseif( !$this->is_readable( \OCP\USER::getUser() . '/' . $directory ) ) { - return false; - } else { - $directoryHandle = $this->opendir( \OCP\USER::getUser() . '/' . $directory ); - while ( $contents = readdir( $directoryHandle ) ) { - if ( $contents != '.' && $contents != '..') { - $path = $directory . "/" . $contents; - if ( $this->is_dir( $path ) ) { - deleteAll( $path ); - } else { - $this->unlink( \OCP\USER::getUser() .'/' . $path ); // TODO: make unlink use same system path as is_dir - } } - } - //$this->closedir( $directoryHandle ); // TODO: implement closedir in OC_FSV - if ( $empty == false ) { - if ( !$this->rmdir( $directory ) ) { - - return false; - + return false; } - } - return true; } @@ -209,7 +175,7 @@ abstract class Common extends \OC\Files\Storage\Storage { return $mime; } public function hash($type,$path,$raw = false) { - $tmpFile=$this->getLocalFile(); + $tmpFile=$this->getLocalFile($path); $hash=hash($type,$tmpFile,$raw); unlink($tmpFile); return $hash; @@ -283,4 +249,12 @@ abstract class Common extends \OC\Files\Storage\Storage { public function hasUpdated($path,$time) { return $this->filemtime($path)>$time; } + + public function getCache(){ + return new \OC\Files\Cache\Cache($this); + } + + public function getScanner(){ + return new \OC\Files\Cache\Scanner($this); + } } diff --git a/lib/files/storage/storage.php b/lib/files/storage/storage.php index 941bc3b077..b1ec0bdb50 100644 --- a/lib/files/storage/storage.php +++ b/lib/files/storage/storage.php @@ -9,40 +9,40 @@ namespace OC\Files\Storage; /** - * Provde a common interface to all different storage options + * Provide a common interface to all different storage options */ -abstract class Storage{ - abstract public function __construct($parameters); - abstract public function getId(); - abstract public function mkdir($path); - abstract public function rmdir($path); - abstract public function opendir($path); - abstract public function is_dir($path); - abstract public function is_file($path); - abstract public function stat($path); - abstract public function filetype($path); - abstract public function filesize($path); - abstract public function isCreatable($path); - abstract public function isReadable($path); - abstract public function isUpdatable($path); - abstract public function isDeletable($path); - abstract public function isSharable($path); - abstract public function getPermissions($path); - abstract public function file_exists($path); - abstract public function filemtime($path); - abstract public function file_get_contents($path); - abstract public function file_put_contents($path,$data); - abstract public function unlink($path); - abstract public function rename($path1,$path2); - abstract public function copy($path1,$path2); - abstract public function fopen($path,$mode); - abstract public function getMimeType($path); - abstract public function hash($type,$path,$raw = false); - abstract public function free_space($path); - abstract public function search($query); - abstract public function touch($path, $mtime=null); - abstract public function getLocalFile($path);// get a path to a local version of the file, whether the original file is local or remote - abstract public function getLocalFolder($path);// get a path to a local version of the folder, whether the original file is local or remote +interface Storage{ + public function __construct($parameters); + public function getId(); + public function mkdir($path); + public function rmdir($path); + public function opendir($path); + public function is_dir($path); + public function is_file($path); + public function stat($path); + public function filetype($path); + public function filesize($path); + public function isCreatable($path); + public function isReadable($path); + public function isUpdatable($path); + public function isDeletable($path); + public function isSharable($path); + public function getPermissions($path); + public function file_exists($path); + public function filemtime($path); + public function file_get_contents($path); + public function file_put_contents($path,$data); + public function unlink($path); + public function rename($path1,$path2); + public function copy($path1,$path2); + public function fopen($path,$mode); + public function getMimeType($path); + public function hash($type,$path,$raw = false); + public function free_space($path); + public function search($query); + public function touch($path, $mtime=null); + public function getLocalFile($path);// get a path to a local version of the file, whether the original file is local or remote + public function getLocalFolder($path);// get a path to a local version of the folder, whether the original file is local or remote /** * check if a file or folder has been updated since $time * @param int $time @@ -51,5 +51,7 @@ abstract class Storage{ * hasUpdated for folders should return at least true if a file inside the folder is add, removed or renamed. * returning true for other changes in the folder is optional */ - abstract public function hasUpdated($path,$time); + public function hasUpdated($path,$time); + public function getCache(); + public function getScanner(); } From 01594b8610ce4f08e28010994d9cbecda4c52e35 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sun, 21 Oct 2012 00:54:34 +0200 Subject: [PATCH 042/272] remove chroot from filesystem api --- lib/files/filesystem.php | 10 ---------- lib/filesystem.php | 11 ----------- 2 files changed, 21 deletions(-) diff --git a/lib/files/filesystem.php b/lib/files/filesystem.php index 0dae774feb..05dd698ab4 100644 --- a/lib/files/filesystem.php +++ b/lib/files/filesystem.php @@ -281,16 +281,6 @@ class Filesystem { } } - /** - * change the root to a fake root - * - * @param string $fakeRoot - * @return bool - */ - static public function chroot($fakeRoot) { - return self::$defaultInstance->chroot($fakeRoot); - } - /** * @brief get the relative path of the root data directory for the current user * @return string diff --git a/lib/filesystem.php b/lib/filesystem.php index ea0a289c8d..20b5ab2790 100644 --- a/lib/filesystem.php +++ b/lib/filesystem.php @@ -81,17 +81,6 @@ class OC_Filesystem { \OC\Files\Filesystem::tearDown(); } - /** - * change the root to a fake root - * - * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem - * @param string $fakeRoot - * @return bool - */ - static public function chroot($fakeRoot) { - return \OC\Files\Filesystem::chroot($fakeRoot); - } - /** * @brief get the relative path of the root data directory for the current user * @return string From 2522c25af726a8487ac13855e8a035b990cd69a4 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sun, 21 Oct 2012 02:12:58 +0200 Subject: [PATCH 043/272] use OC_Files::getFileInfo and OC_Files::getDirectoryContent as high level api for the filecache most apps would want to use this api instead of using the cache directly --- lib/files.php | 108 +++++++++++++++++----------------- lib/files/filesystem.php | 73 ++++++++++++++++++----- lib/files/storage/storage.php | 7 +++ tests/lib/files.php | 87 +++++++++++++++++++++++++++ 4 files changed, 204 insertions(+), 71 deletions(-) create mode 100644 tests/lib/files.php diff --git a/lib/files.php b/lib/files.php index ba111bee96..8d402c0001 100644 --- a/lib/files.php +++ b/lib/files.php @@ -41,69 +41,67 @@ class OC_Files { * - versioned */ public static function getFileInfo($path) { - if (($path == '/Shared' || substr($path, 0, 8) == '/Shared/') && OC_App::isEnabled('files_sharing')) { - if ($path == '/Shared') { - list($info) = OCP\Share::getItemsSharedWith('file', OC_Share_Backend_File::FORMAT_FILE_APP_ROOT); - }else{ - $info['size'] = \OC\Files\Filesystem::filesize($path); - $info['mtime'] = \OC\Files\Filesystem::filemtime($path); - $info['ctime'] = \OC\Files\Filesystem::filectime($path); - $info['mimetype'] = \OC\Files\Filesystem::getMimeType($path); - $info['encrypted'] = false; - $info['versioned'] = false; + $path=\OC\Files\Filesystem::normalizePath($path); + /** + * @var \OC\Files\Storage\Storage $storage + * @var string $path + */ + list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($path); + $cache=$storage->getCache(); + $data = $cache->get($internalPath); + + if($data['mimetype'] === 'httpd/unix-directory'){ + //add the sizes of other mountpoints to the folder + $mountPoints = \OC\Files\Filesystem::getMountPoints($path); + foreach($mountPoints as $mountPoint){ + $subStorage = \OC\Files\Filesystem::getStorage($mountPoint); + $subCache = $subStorage->getCache(); + $rootEntry = $subCache->get(''); + + $data['size'] += $rootEntry['size']; } - } else { - $info = OC_FileCache::get($path); } - return $info; + + return $data; } /** * get the content of a directory - * @param dir $directory path under datadirectory + * @param string $directory path under datadirectory + * @return array */ public static function getDirectoryContent($directory, $mimetype_filter = '') { - $directory=\OC\Files\Filesystem::normalizePath($directory); - if($directory=='/') { - $directory=''; - } - $files = array(); - if (($directory == '/Shared' || substr($directory, 0, 8) == '/Shared/') && OC_App::isEnabled('files_sharing')) { - if ($directory == '/Shared') { - $files = OCP\Share::getItemsSharedWith('file', OC_Share_Backend_File::FORMAT_FILE_APP, array('folder' => $directory, 'mimetype_filter' => $mimetype_filter)); - } else { - $pos = strpos($directory, '/', 8); - // Get shared folder name - if ($pos !== false) { - $itemTarget = substr($directory, 7, $pos - 7); - } else { - $itemTarget = substr($directory, 7); + $path=\OC\Files\Filesystem::normalizePath($directory); + /** + * @var \OC\Files\Storage\Storage $storage + * @var string $path + */ + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($path); + $cache=$storage->getCache(); + $files=$cache->getFolderContents($internalPath); //TODO: mimetype_filter + + //add a folder for any mountpoint in this directory and add the sizes of other mountpoints to the folders + $mountPoints = \OC\Files\Filesystem::getMountPoints($directory); + $dirLength = strlen($path); + foreach($mountPoints as $mountPoint){ + $subStorage = \OC\Files\Filesystem::getStorage($mountPoint); + $subCache = $subStorage->getCache(); + $rootEntry = $subCache->get(''); + + $relativePath = trim(substr($mountPoint, $dirLength), '/'); + if($pos = strpos($relativePath, '/')){ //mountpoint inside subfolder add size to the correct folder + $entryName = substr($relativePath, 0, $pos); + foreach($files as &$entry){ + if($entry['name'] === $entryName){ + $entry['size'] += $rootEntry['size']; + } } - $files = OCP\Share::getItemSharedWith('folder', $itemTarget, OC_Share_Backend_File::FORMAT_FILE_APP, array('folder' => $directory, 'mimetype_filter' => $mimetype_filter)); - } - } else { - $files = OC_FileCache::getFolderContent($directory, false, $mimetype_filter); - foreach ($files as &$file) { - $file['directory'] = $directory; - $file['type'] = ($file['mimetype'] == 'httpd/unix-directory') ? 'dir' : 'file'; - $permissions = OCP\Share::PERMISSION_READ; - // NOTE: Remove check when new encryption is merged - if (!$file['encrypted']) { - $permissions |= OCP\Share::PERMISSION_SHARE; - } - if ($file['type'] == 'dir' && $file['writable']) { - $permissions |= OCP\Share::PERMISSION_CREATE; - } - if ($file['writable']) { - $permissions |= OCP\Share::PERMISSION_UPDATE | OCP\Share::PERMISSION_DELETE; - } - $file['permissions'] = $permissions; - } - if ($directory == '' && OC_App::isEnabled('files_sharing')) { - // Add 'Shared' folder - $files = array_merge($files, OCP\Share::getItemsSharedWith('file', OC_Share_Backend_File::FORMAT_FILE_APP_ROOT)); + }else{ //mountpoint in this folder, add an entry for it + $rootEntry['name'] = $relativePath; + $files[] = $rootEntry; } } + usort($files, "fileCmp");//TODO: remove this once ajax is merged return $files; } @@ -145,7 +143,7 @@ class OC_Files { set_time_limit(0); $zip = new ZipArchive(); $filename = OC_Helper::tmpFile('.zip'); - if ($zip->open($filename, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE)!==TRUE) { + if ($zip->open($filename, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE)!==true) { exit("cannot open <$filename>\n"); } foreach($files as $file) { @@ -166,7 +164,7 @@ class OC_Files { set_time_limit(0); $zip = new ZipArchive(); $filename = OC_Helper::tmpFile('.zip'); - if ($zip->open($filename, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE)!==TRUE) { + if ($zip->open($filename, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE)!==true) { exit("cannot open <$filename>\n"); } $file=$dir.'/'.$files; @@ -446,7 +444,7 @@ class OC_Files { $setting = 'php_value '.$key.' '.$size; $hasReplaced = 0; $content = preg_replace($pattern, $setting, $htaccess, 1, $hasReplaced); - if($content !== NULL) { + if($content !== null) { $htaccess = $content; } if($hasReplaced == 0) { diff --git a/lib/files/filesystem.php b/lib/files/filesystem.php index 05dd698ab4..b7f8483fbf 100644 --- a/lib/files/filesystem.php +++ b/lib/files/filesystem.php @@ -164,6 +164,43 @@ class Filesystem { return $foundMountPoint; } + /** + * get a list of all mount points in a directory + * + * @param string $path + * @return string[] + */ + static public function getMountPoints($path) { + $path = self::normalizePath($path); + if (strlen($path) > 1) { + $path .= '/'; + } + $pathLength = strlen($path); + + $mountPoints = array_keys(self::$mounts); + $result = array(); + foreach ($mountPoints as $mountPoint) { + if (substr($mountPoint, 0, $pathLength) === $path and strlen($mountPoint) > $pathLength) { + $result[] = $mountPoint; + } + } + return $result; + } + + /** + * get the storage mounted at $mountPoint + * + * @param string $mountPoint + * @return \OC\Files\Storage\Storage + */ + public static function getStorage($mountPoint) { + if (!isset(self::$storages[$mountPoint])) { + $mount = self::$mounts[$mountPoint]; + self::$storages[$mountPoint] = self::createStorage($mount['class'], $mount['arguments']); + } + return self::$storages[$mountPoint]; + } + /** * resolve a path to a storage and internal path * @@ -173,14 +210,14 @@ class Filesystem { static public function resolvePath($path) { $mountpoint = self::getMountPoint($path); if ($mountpoint) { - if (!isset(self::$storages[$mountpoint])) { - $mount = self::$mounts[$mountpoint]; - self::$storages[$mountpoint] = self::createStorage($mount['class'], $mount['arguments']); + $storage = self::getStorage($mountpoint); + if ($mountpoint === $path) { + $internalPath = ''; + } else { + $internalPath = substr($path, strlen($mountpoint)); } - $storage = self::$storages[$mountpoint]; - $internalPath = substr($path, strlen($mountpoint)); return array($storage, $internalPath); - }else{ + } else { return array(null, null); } } @@ -302,18 +339,22 @@ class Filesystem { /** * mount an \OC\Files\Storage\Storage in our virtual filesystem * - * @param \OC\Files\Storage\Storage $storage + * @param \OC\Files\Storage\Storage|string $class * @param array $arguments * @param string $mountpoint */ static public function mount($class, $arguments, $mountpoint) { - if ($mountpoint[0] != '/') { - $mountpoint = '/' . $mountpoint; + $mountpoint = self::normalizePath($mountpoint); + if (strlen($mountpoint) > 1) { + $mountpoint .= '/'; } - if (substr($mountpoint, -1) !== '/') { - $mountpoint = $mountpoint . '/'; + + if ($class instanceof \OC\Files\Storage\Storage) { + self::$mounts[$mountpoint] = array('class' => get_class($class), 'arguments' => $arguments); + self::$storages[$mountpoint] = $class; + } else { + self::$mounts[$mountpoint] = array('class' => $class, 'arguments' => $arguments); } - self::$mounts[$mountpoint] = array('class' => $class, 'arguments' => $arguments); } /** @@ -522,15 +563,15 @@ class Filesystem { static public function removeETagHook($params, $root = false) { if (isset($params['path'])) { - $path=$params['path']; + $path = $params['path']; } else { - $path=$params['oldpath']; + $path = $params['oldpath']; } if ($root) { // reduce path to the required part of it (no 'username/files') - $fakeRootView = new OC_FilesystemView($root); + $fakeRootView = new View($root); $count = 1; - $path=str_replace(OC_App::getStorage("files")->getAbsolutePath(), "", $fakeRootView->getAbsolutePath($path), $count); + $path = str_replace(\OC_App::getStorage("files")->getAbsolutePath(''), "", $fakeRootView->getAbsolutePath($path), $count); } $path = self::normalizePath($path); diff --git a/lib/files/storage/storage.php b/lib/files/storage/storage.php index b1ec0bdb50..853e8ba519 100644 --- a/lib/files/storage/storage.php +++ b/lib/files/storage/storage.php @@ -52,6 +52,13 @@ interface Storage{ * returning true for other changes in the folder is optional */ public function hasUpdated($path,$time); + + /** + * @return \OC\Files\Cache\Cache + */ public function getCache(); + /** + * @return \OC\Files\Cache\Scanner + */ public function getScanner(); } diff --git a/tests/lib/files.php b/tests/lib/files.php new file mode 100644 index 0000000000..9cb5276845 --- /dev/null +++ b/tests/lib/files.php @@ -0,0 +1,87 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. */ + +use \OC\Files\Filesystem as Filesystem; + +class Test_Files extends PHPUnit_Framework_TestCase { + /** + * @var \OC\Files\Storage\Storage[] $storages; + */ + private $storages = array(); + + public function setUp() { + Filesystem::clearMounts(); + } + + public function tearDown() { + foreach ($this->storages as $storage) { + $cache = $storage->getCache(); + $cache->clear(); + } + } + + public function testCacheAPI() { + $storage1 = $this->getTestStorage(); + $storage2 = $this->getTestStorage(); + $storage3 = $this->getTestStorage(); + Filesystem::mount($storage1, array(), '/'); + Filesystem::mount($storage2, array(), '/substorage'); + Filesystem::mount($storage3, array(), '/folder/anotherstorage'); + $textSize = strlen("dummy file data\n"); + $imageSize = filesize(\OC::$SERVERROOT . '/core/img/logo.png'); + $storageSize = $textSize * 2 + $imageSize; + + $cachedData = OC_Files::getFileInfo('/foo.txt'); + $this->assertEquals($textSize, $cachedData['size']); + $this->assertEquals('text/plain', $cachedData['mimetype']); + + $cachedData = OC_Files::getFileInfo('/'); + $this->assertEquals($storageSize * 3, $cachedData['size']); + $this->assertEquals('httpd/unix-directory', $cachedData['mimetype']); + + $cachedData = OC_Files::getFileInfo('/folder'); + $this->assertEquals($storageSize + $textSize, $cachedData['size']); + $this->assertEquals('httpd/unix-directory', $cachedData['mimetype']); + + $folderData = OC_Files::getDirectoryContent('/'); + /** + * expected entries: + * folder + * foo.png + * foo.txt + * substorage + */ + $this->assertEquals(4, count($folderData)); + $this->assertEquals('folder', $folderData[0]['name']); + $this->assertEquals('foo.png', $folderData[1]['name']); + $this->assertEquals('foo.txt', $folderData[2]['name']); + $this->assertEquals('substorage', $folderData[3]['name']); + + $this->assertEquals($storageSize + $textSize, $folderData[0]['size']); + $this->assertEquals($imageSize, $folderData[1]['size']); + $this->assertEquals($textSize, $folderData[2]['size']); + $this->assertEquals($storageSize, $folderData[3]['size']); + } + + /** + * @return OC\Files\Storage\Storage + */ + private function getTestStorage() { + $storage = new \OC\Files\Storage\Temporary(array()); + $textData = "dummy file data\n"; + $imgData = file_get_contents(\OC::$SERVERROOT . '/core/img/logo.png'); + $storage->mkdir('folder'); + $storage->file_put_contents('foo.txt', $textData); + $storage->file_put_contents('foo.png', $imgData); + $storage->file_put_contents('folder/bar.txt', $textData); + + $scanner = $storage->getScanner(); + $scanner->scan(''); + $this->storages[] = $storage; + return $storage; + } +} From 33cabcf590401763609570a86f7bc7540dbf1fc5 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sun, 21 Oct 2012 22:04:45 +0200 Subject: [PATCH 044/272] postpone the cost of setting up some of the external storage backends untill we actually need it --- apps/files_external/lib/ftp.php | 2 ++ apps/files_external/lib/smb.php | 7 ++---- apps/files_external/lib/streamwrapper.php | 26 ++++++++++++++++++++ apps/files_external/lib/swift.php | 29 +++++++++++++++++++++-- apps/files_external/lib/webdav.php | 23 ++++++++++++++++++ apps/files_external/tests/config.php | 2 +- 6 files changed, 81 insertions(+), 8 deletions(-) diff --git a/apps/files_external/lib/ftp.php b/apps/files_external/lib/ftp.php index dea44728f3..e76eca0be3 100644 --- a/apps/files_external/lib/ftp.php +++ b/apps/files_external/lib/ftp.php @@ -50,6 +50,7 @@ class FTP extends \OC\Files\Storage\StreamWrapper{ return $url; } public function fopen($path,$mode) { + $this->init(); switch($mode) { case 'r': case 'rb': @@ -86,6 +87,7 @@ class FTP extends \OC\Files\Storage\StreamWrapper{ } public function writeBack($tmpFile) { + $this->init(); if(isset(self::$tempFiles[$tmpFile])) { $this->uploadFile($tmpFile,self::$tempFiles[$tmpFile]); unlink($tmpFile); diff --git a/apps/files_external/lib/smb.php b/apps/files_external/lib/smb.php index dfba3105ec..4382f63003 100644 --- a/apps/files_external/lib/smb.php +++ b/apps/files_external/lib/smb.php @@ -35,11 +35,6 @@ class SMB extends \OC\Files\Storage\StreamWrapper{ if(substr($this->share,-1,1)=='/') { $this->share=substr($this->share,0,-1); } - - //create the root folder if necesary - if(!$this->is_dir('')) { - $this->mkdir(''); - } } public function getId(){ @@ -54,6 +49,7 @@ class SMB extends \OC\Files\Storage\StreamWrapper{ } public function stat($path) { + $this->init(); if(!$path and $this->root=='/') {//mtime doesn't work for shares $mtime=$this->shareMTime(); $stat=stat($this->constructUrl($path)); @@ -75,6 +71,7 @@ class SMB extends \OC\Files\Storage\StreamWrapper{ * @return bool */ public function hasUpdated($path,$time) { + $this->init(); if(!$path and $this->root=='/') { //mtime doesn't work for shares, but giving the nature of the backend, doing a full update is still just fast enough return true; diff --git a/apps/files_external/lib/streamwrapper.php b/apps/files_external/lib/streamwrapper.php index 750bdbaf4d..bc1c95c5e8 100644 --- a/apps/files_external/lib/streamwrapper.php +++ b/apps/files_external/lib/streamwrapper.php @@ -9,13 +9,29 @@ namespace OC\Files\Storage; abstract class StreamWrapper extends \OC\Files\Storage\Common{ + private $ready = false; + + protected function init(){ + if($this->ready){ + return; + } + $this->ready = true; + + //create the root folder if necesary + if(!$this->is_dir('')) { + $this->mkdir(''); + } + } + abstract public function constructUrl($path); public function mkdir($path) { + $this->init(); return mkdir($this->constructUrl($path)); } public function rmdir($path) { + $this->init(); if($this->file_exists($path)) { $succes=rmdir($this->constructUrl($path)); clearstatcache(); @@ -26,10 +42,12 @@ abstract class StreamWrapper extends \OC\Files\Storage\Common{ } public function opendir($path) { + $this->init(); return opendir($this->constructUrl($path)); } public function filetype($path) { + $this->init(); return filetype($this->constructUrl($path)); } @@ -42,16 +60,19 @@ abstract class StreamWrapper extends \OC\Files\Storage\Common{ } public function file_exists($path) { + $this->init(); return file_exists($this->constructUrl($path)); } public function unlink($path) { + $this->init(); $succes=unlink($this->constructUrl($path)); clearstatcache(); return $succes; } public function fopen($path,$mode) { + $this->init(); return fopen($this->constructUrl($path),$mode); } @@ -60,6 +81,7 @@ abstract class StreamWrapper extends \OC\Files\Storage\Common{ } public function touch($path,$mtime=null) { + $this->init(); if(is_null($mtime)) { $fh=$this->fopen($path,'a'); fwrite($fh,''); @@ -70,18 +92,22 @@ abstract class StreamWrapper extends \OC\Files\Storage\Common{ } public function getFile($path,$target) { + $this->init(); return copy($this->constructUrl($path),$target); } public function uploadFile($path,$target) { + $this->init(); return copy($path,$this->constructUrl($target)); } public function rename($path1,$path2) { + $this->init(); return rename($this->constructUrl($path1),$this->constructUrl($path2)); } public function stat($path) { + $this->init(); return stat($this->constructUrl($path)); } diff --git a/apps/files_external/lib/swift.php b/apps/files_external/lib/swift.php index c3578b0c23..55c2c3e0ac 100644 --- a/apps/files_external/lib/swift.php +++ b/apps/files_external/lib/swift.php @@ -17,6 +17,7 @@ class SWIFT extends \OC\Files\Storage\Common{ private $user; private $token; private $secure; + private $ready = false; /** * @var \CF_Authentication auth */ @@ -188,7 +189,7 @@ class SWIFT extends \OC\Files\Storage\Common{ /** * add an emulated sub container - * @param CF_Container $container + * @param \CF_Container $container * @param string $name * @return bool */ @@ -222,7 +223,7 @@ class SWIFT extends \OC\Files\Storage\Common{ /** * remove an emulated sub container - * @param CF_Container $container + * @param \CF_Container $container * @param string $name * @return bool */ @@ -279,6 +280,15 @@ class SWIFT extends \OC\Files\Storage\Common{ if(!$this->root || $this->root[0]!='/') { $this->root='/'.$this->root; } + + } + + private function init(){ + if($this->ready){ + return; + } + $this->ready = true; + $this->auth = new \CF_Authentication($this->user, $this->token, null, $this->host); $this->auth->authenticate(); @@ -297,6 +307,7 @@ class SWIFT extends \OC\Files\Storage\Common{ public function mkdir($path) { + $this->init(); if($this->containerExists($path)) { return false; }else{ @@ -306,6 +317,7 @@ class SWIFT extends \OC\Files\Storage\Common{ } public function rmdir($path) { + $this->init(); if(!$this->containerExists($path)) { return false; }else{ @@ -343,6 +355,7 @@ class SWIFT extends \OC\Files\Storage\Common{ } public function opendir($path) { + $this->init(); $container=$this->getContainer($path); $files=$this->getObjects($container); $i=array_search(self::SUBCONTAINER_FILE,$files); @@ -357,6 +370,7 @@ class SWIFT extends \OC\Files\Storage\Common{ } public function filetype($path) { + $this->init(); if($this->containerExists($path)) { return 'dir'; }else{ @@ -373,6 +387,7 @@ class SWIFT extends \OC\Files\Storage\Common{ } public function file_exists($path) { + $this->init(); if($this->is_dir($path)) { return true; }else{ @@ -381,6 +396,7 @@ class SWIFT extends \OC\Files\Storage\Common{ } public function file_get_contents($path) { + $this->init(); $obj=$this->getObject($path); if(is_null($obj)) { return false; @@ -389,6 +405,7 @@ class SWIFT extends \OC\Files\Storage\Common{ } public function file_put_contents($path,$content) { + $this->init(); $obj=$this->getObject($path); if(is_null($obj)) { $container=$this->getContainer(dirname($path)); @@ -402,6 +419,7 @@ class SWIFT extends \OC\Files\Storage\Common{ } public function unlink($path) { + $this->init(); if($this->containerExists($path)) { return $this->rmdir($path); } @@ -415,6 +433,7 @@ class SWIFT extends \OC\Files\Storage\Common{ } public function fopen($path,$mode) { + $this->init(); switch($mode) { case 'r': case 'rb': @@ -458,6 +477,7 @@ class SWIFT extends \OC\Files\Storage\Common{ } public function touch($path,$mtime=null) { + $this->init(); $obj=$this->getObject($path); if(is_null($obj)) { return false; @@ -472,6 +492,7 @@ class SWIFT extends \OC\Files\Storage\Common{ } public function rename($path1,$path2) { + $this->init(); $sourceContainer=$this->getContainer(dirname($path1)); $targetContainer=$this->getContainer(dirname($path2)); $result=$sourceContainer->move_object_to(basename($path1),$targetContainer,basename($path2)); @@ -484,6 +505,7 @@ class SWIFT extends \OC\Files\Storage\Common{ } public function copy($path1,$path2) { + $this->init(); $sourceContainer=$this->getContainer(dirname($path1)); $targetContainer=$this->getContainer(dirname($path2)); $result=$sourceContainer->copy_object_to(basename($path1),$targetContainer,basename($path2)); @@ -495,6 +517,7 @@ class SWIFT extends \OC\Files\Storage\Common{ } public function stat($path) { + $this->init(); $container=$this->getContainer($path); if (!is_null($container)) { return array( @@ -521,6 +544,7 @@ class SWIFT extends \OC\Files\Storage\Common{ } private function getTmpFile($path) { + $this->init(); $obj=$this->getObject($path); if(!is_null($obj)) { $tmpFile=\OCP\Files::tmpFile(); @@ -532,6 +556,7 @@ class SWIFT extends \OC\Files\Storage\Common{ } private function fromTmpFile($tmpFile,$path) { + $this->init(); $obj=$this->getObject($path); if(is_null($obj)) { $obj=$this->createObject($path); diff --git a/apps/files_external/lib/webdav.php b/apps/files_external/lib/webdav.php index c9785f3eb1..470a80f8cf 100644 --- a/apps/files_external/lib/webdav.php +++ b/apps/files_external/lib/webdav.php @@ -14,6 +14,7 @@ class DAV extends \OC\Files\Storage\Common{ private $host; private $secure; private $root; + private $ready; /** * @var \Sabre_DAV_Client */ @@ -37,6 +38,13 @@ class DAV extends \OC\Files\Storage\Common{ if(substr($this->root,-1,1)!='/') { $this->root.='/'; } + } + + private function init(){ + if($this->ready){ + return; + } + $this->ready = true; $settings = array( 'baseUri' => $this->createBaseUri(), @@ -70,16 +78,19 @@ class DAV extends \OC\Files\Storage\Common{ } public function mkdir($path) { + $this->init(); $path=$this->cleanPath($path); return $this->simpleResponse('MKCOL',$path,null,201); } public function rmdir($path) { + $this->init(); $path=$this->cleanPath($path); return $this->simpleResponse('DELETE',$path,null,204); } public function opendir($path) { + $this->init(); $path=$this->cleanPath($path); try{ $response=$this->client->propfind($path, array(),1); @@ -98,6 +109,7 @@ class DAV extends \OC\Files\Storage\Common{ } public function filetype($path) { + $this->init(); $path=$this->cleanPath($path); try{ $response=$this->client->propfind($path, array('{DAV:}resourcetype')); @@ -119,6 +131,7 @@ class DAV extends \OC\Files\Storage\Common{ } public function file_exists($path) { + $this->init(); $path=$this->cleanPath($path); try{ $this->client->propfind($path, array('{DAV:}resourcetype')); @@ -129,10 +142,12 @@ class DAV extends \OC\Files\Storage\Common{ } public function unlink($path) { + $this->init(); return $this->simpleResponse('DELETE',$path,null,204); } public function fopen($path,$mode) { + $this->init(); $path=$this->cleanPath($path); switch($mode) { case 'r': @@ -187,6 +202,7 @@ class DAV extends \OC\Files\Storage\Common{ } public function free_space($path) { + $this->init(); $path=$this->cleanPath($path); try{ $response=$this->client->propfind($path, array('{DAV:}quota-available-bytes')); @@ -201,6 +217,7 @@ class DAV extends \OC\Files\Storage\Common{ } public function touch($path,$mtime=null) { + $this->init(); if(is_null($mtime)) { $mtime=time(); } @@ -209,11 +226,13 @@ class DAV extends \OC\Files\Storage\Common{ } public function getFile($path,$target) { + $this->init(); $source=$this->fopen($path,'r'); file_put_contents($target,$source); } public function uploadFile($path,$target) { + $this->init(); $source=fopen($path,'r'); $curl = curl_init(); @@ -228,6 +247,7 @@ class DAV extends \OC\Files\Storage\Common{ } public function rename($path1,$path2) { + $this->init(); $path1=$this->cleanPath($path1); $path2=$this->root.$this->cleanPath($path2); try{ @@ -239,6 +259,7 @@ class DAV extends \OC\Files\Storage\Common{ } public function copy($path1,$path2) { + $this->init(); $path1=$this->cleanPath($path1); $path2=$this->root.$this->cleanPath($path2); try{ @@ -250,6 +271,7 @@ class DAV extends \OC\Files\Storage\Common{ } public function stat($path) { + $this->init(); $path=$this->cleanPath($path); try{ $response=$this->client->propfind($path, array('{DAV:}getlastmodified','{DAV:}getcontentlength')); @@ -263,6 +285,7 @@ class DAV extends \OC\Files\Storage\Common{ } public function getMimeType($path) { + $this->init(); $path=$this->cleanPath($path); try{ $response=$this->client->propfind($path, array('{DAV:}getcontenttype','{DAV:}resourcetype')); diff --git a/apps/files_external/tests/config.php b/apps/files_external/tests/config.php index 5af317675c..65127175ad 100644 --- a/apps/files_external/tests/config.php +++ b/apps/files_external/tests/config.php @@ -30,7 +30,7 @@ return array( 'root'=>'/', ), 'smb'=>array( - 'run'=>false, + 'run'=>true, 'user'=>'test', 'password'=>'test', 'host'=>'localhost', From 707bd68bb4e77b4184b578699d508750653e2d42 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sun, 21 Oct 2012 22:05:29 +0200 Subject: [PATCH 045/272] automatically scan files when needed --- lib/files.php | 463 +++++++++++++++++++++++--------------------- tests/lib/files.php | 27 ++- 2 files changed, 265 insertions(+), 225 deletions(-) diff --git a/lib/files.php b/lib/files.php index 8d402c0001..a5eec1f2a6 100644 --- a/lib/files.php +++ b/lib/files.php @@ -1,35 +1,36 @@ . -* -*/ + * ownCloud + * + * @author Frank Karlitschek + * @copyright 2012 Frank Karlitschek frank@owncloud.org + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see . + * + */ /** * Class for fileserver access * */ class OC_Files { - static $tmpFiles=array(); + static $tmpFiles = array(); /** * get the filesystem info + * * @param string $path * @return array * @@ -41,19 +42,25 @@ class OC_Files { * - versioned */ public static function getFileInfo($path) { - $path=\OC\Files\Filesystem::normalizePath($path); + $path = \OC\Files\Filesystem::normalizePath($path); /** * @var \OC\Files\Storage\Storage $storage * @var string $path */ - list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($path); - $cache=$storage->getCache(); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($path); + $cache = $storage->getCache(); + + if (!$cache->inCache($internalPath)) { + $scanner = $storage->getScanner(); + $scanner->scan($internalPath, \OC\Files\Cache\Scanner::SCAN_SHALLOW); + } + $data = $cache->get($internalPath); - if($data['mimetype'] === 'httpd/unix-directory'){ + if ($data['mimetype'] === 'httpd/unix-directory') { //add the sizes of other mountpoints to the folder $mountPoints = \OC\Files\Filesystem::getMountPoints($path); - foreach($mountPoints as $mountPoint){ + foreach ($mountPoints as $mountPoint) { $subStorage = \OC\Files\Filesystem::getStorage($mountPoint); $subCache = $subStorage->getCache(); $rootEntry = $subCache->get(''); @@ -66,43 +73,50 @@ class OC_Files { } /** - * get the content of a directory - * @param string $directory path under datadirectory - * @return array - */ + * get the content of a directory + * + * @param string $directory path under datadirectory + * @return array + */ public static function getDirectoryContent($directory, $mimetype_filter = '') { - $path=\OC\Files\Filesystem::normalizePath($directory); + $path = \OC\Files\Filesystem::normalizePath($directory); /** * @var \OC\Files\Storage\Storage $storage * @var string $path */ list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($path); - $cache=$storage->getCache(); - $files=$cache->getFolderContents($internalPath); //TODO: mimetype_filter + $cache = $storage->getCache(); + + if (!$cache->inCache($internalPath)) { + $scanner = $storage->getScanner(); + $scanner->scan($internalPath, \OC\Files\Cache\Scanner::SCAN_SHALLOW); + } + + $files = $cache->getFolderContents($internalPath); //TODO: mimetype_filter //add a folder for any mountpoint in this directory and add the sizes of other mountpoints to the folders $mountPoints = \OC\Files\Filesystem::getMountPoints($directory); $dirLength = strlen($path); - foreach($mountPoints as $mountPoint){ + foreach ($mountPoints as $mountPoint) { $subStorage = \OC\Files\Filesystem::getStorage($mountPoint); $subCache = $subStorage->getCache(); $rootEntry = $subCache->get(''); $relativePath = trim(substr($mountPoint, $dirLength), '/'); - if($pos = strpos($relativePath, '/')){ //mountpoint inside subfolder add size to the correct folder + if ($pos = strpos($relativePath, '/')) { //mountpoint inside subfolder add size to the correct folder $entryName = substr($relativePath, 0, $pos); - foreach($files as &$entry){ - if($entry['name'] === $entryName){ + foreach ($files as &$entry) { + if ($entry['name'] === $entryName) { $entry['size'] += $rootEntry['size']; } } - }else{ //mountpoint in this folder, add an entry for it + } else { //mountpoint in this folder, add an entry for it $rootEntry['name'] = $relativePath; $files[] = $rootEntry; } } - usort($files, "fileCmp");//TODO: remove this once ajax is merged + usort($files, "fileCmp"); //TODO: remove this once ajax is merged return $files; } @@ -113,12 +127,11 @@ class OC_Files { // get next subdir to check $dir = array_pop($dirs_to_check); $dir_content = self::getDirectoryContent($dir, $mimetype_filter); - foreach($dir_content as $file) { + foreach ($dir_content as $file) { if ($file['type'] == 'file') { - $files[] = $dir.'/'.$file['name']; - } - else { - $dirs_to_check[] = $dir.'/'.$file['name']; + $files[] = $dir . '/' . $file['name']; + } else { + $dirs_to_check[] = $dir . '/' . $file['name']; } } } @@ -126,170 +139,171 @@ class OC_Files { } /** - * return the content of a file or return a zip file containning multiply files - * - * @param dir $dir - * @param file $file ; seperated list of files to download - * @param boolean $only_header ; boolean to only send header of the request - */ - public static function get($dir,$files, $only_header = false) { - if(strpos($files,';')) { - $files=explode(';',$files); + * return the content of a file or return a zip file containning multiply files + * + * @param dir $dir + * @param file $file ; seperated list of files to download + * @param boolean $only_header ; boolean to only send header of the request + */ + public static function get($dir, $files, $only_header = false) { + if (strpos($files, ';')) { + $files = explode(';', $files); } - if(is_array($files)) { - self::validateZipDownload($dir,$files); + if (is_array($files)) { + self::validateZipDownload($dir, $files); $executionTime = intval(ini_get('max_execution_time')); set_time_limit(0); $zip = new ZipArchive(); $filename = OC_Helper::tmpFile('.zip'); - if ($zip->open($filename, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE)!==true) { + if ($zip->open($filename, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE) !== true) { exit("cannot open <$filename>\n"); } - foreach($files as $file) { - $file=$dir.'/'.$file; - if(\OC\Files\Filesystem::is_file($file)) { - $tmpFile=OC_F\OC\Files\Filesystemilesystem::toTmpFile($file); - self::$tmpFiles[]=$tmpFile; - $zip->addFile($tmpFile,basename($file)); - }elseif(\OC\Files\Filesystem::is_dir($file)) { - self::zipAddDir($file,$zip); + foreach ($files as $file) { + $file = $dir . '/' . $file; + if (\OC\Files\Filesystem::is_file($file)) { + $tmpFile = OC_F\OC\Files\Filesystemilesystem::toTmpFile($file); + self::$tmpFiles[] = $tmpFile; + $zip->addFile($tmpFile, basename($file)); + } elseif (\OC\Files\Filesystem::is_dir($file)) { + self::zipAddDir($file, $zip); } } $zip->close(); set_time_limit($executionTime); - }elseif(\OC\Files\Filesystem::is_dir($dir.'/'.$files)) { - self::validateZipDownload($dir,$files); + } elseif (\OC\Files\Filesystem::is_dir($dir . '/' . $files)) { + self::validateZipDownload($dir, $files); $executionTime = intval(ini_get('max_execution_time')); set_time_limit(0); $zip = new ZipArchive(); $filename = OC_Helper::tmpFile('.zip'); - if ($zip->open($filename, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE)!==true) { + if ($zip->open($filename, ZIPARCHIVE::CREATE | ZIPARCHIVE::OVERWRITE) !== true) { exit("cannot open <$filename>\n"); } - $file=$dir.'/'.$files; - self::zipAddDir($file,$zip); + $file = $dir . '/' . $files; + self::zipAddDir($file, $zip); $zip->close(); set_time_limit($executionTime); - }else{ - $zip=false; - $filename=$dir.'/'.$files; + } else { + $zip = false; + $filename = $dir . '/' . $files; } @ob_end_clean(); - if($zip or \OC\Files\Filesystem::isReadable($filename)) { - header('Content-Disposition: attachment; filename="'.basename($filename).'"'); + if ($zip or \OC\Files\Filesystem::isReadable($filename)) { + header('Content-Disposition: attachment; filename="' . basename($filename) . '"'); header('Content-Transfer-Encoding: binary'); OC_Response::disableCaching(); - if($zip) { + if ($zip) { ini_set('zlib.output_compression', 'off'); header('Content-Type: application/zip'); header('Content-Length: ' . filesize($filename)); - }else{ - header('Content-Type: '.\OC\Files\Filesystem::getMimeType($filename)); + } else { + header('Content-Type: ' . \OC\Files\Filesystem::getMimeType($filename)); } - }elseif($zip or !\OC\Files\Filesystem::file_exists($filename)) { + } elseif ($zip or !\OC\Files\Filesystem::file_exists($filename)) { header("HTTP/1.0 404 Not Found"); - $tmpl = new OC_Template( '', '404', 'guest' ); - $tmpl->assign('file',$filename); + $tmpl = new OC_Template('', '404', 'guest'); + $tmpl->assign('file', $filename); $tmpl->printPage(); - }else{ + } else { header("HTTP/1.0 403 Forbidden"); die('403 Forbidden'); } - if($only_header) { - if(!$zip) - header("Content-Length: ".\OC\Files\Filesystem::filesize($filename)); - return ; + if ($only_header) { + if (!$zip) + header("Content-Length: " . \OC\Files\Filesystem::filesize($filename)); + return; } - if($zip) { - $handle=fopen($filename,'r'); + if ($zip) { + $handle = fopen($filename, 'r'); if ($handle) { - $chunkSize = 8*1024;// 1 MB chunks + $chunkSize = 8 * 1024; // 1 MB chunks while (!feof($handle)) { echo fread($handle, $chunkSize); flush(); } } unlink($filename); - }else{ + } else { \OC\Files\Filesystem::readfile($filename); } - foreach(self::$tmpFiles as $tmpFile) { - if(file_exists($tmpFile) and is_file($tmpFile)) { + foreach (self::$tmpFiles as $tmpFile) { + if (file_exists($tmpFile) and is_file($tmpFile)) { unlink($tmpFile); } } } - public static function zipAddDir($dir,$zip,$internalDir='') { - $dirname=basename($dir); - $zip->addEmptyDir($internalDir.$dirname); - $internalDir.=$dirname.='/'; - $files=OC_Files::getdirectorycontent($dir); - foreach($files as $file) { - $filename=$file['name']; - $file=$dir.'/'.$filename; - if(\OC\Files\Filesystem::is_file($file)) { - $tmpFile=\OC\Files\Filesystem::toTmpFile($file); - OC_Files::$tmpFiles[]=$tmpFile; - $zip->addFile($tmpFile,$internalDir.$filename); - }elseif(\OC\Files\Filesystem::is_dir($file)) { - self::zipAddDir($file,$zip,$internalDir); + public static function zipAddDir($dir, $zip, $internalDir = '') { + $dirname = basename($dir); + $zip->addEmptyDir($internalDir . $dirname); + $internalDir .= $dirname .= '/'; + $files = OC_Files::getdirectorycontent($dir); + foreach ($files as $file) { + $filename = $file['name']; + $file = $dir . '/' . $filename; + if (\OC\Files\Filesystem::is_file($file)) { + $tmpFile = \OC\Files\Filesystem::toTmpFile($file); + OC_Files::$tmpFiles[] = $tmpFile; + $zip->addFile($tmpFile, $internalDir . $filename); + } elseif (\OC\Files\Filesystem::is_dir($file)) { + self::zipAddDir($file, $zip, $internalDir); } } } + /** - * move a file or folder - * - * @param dir $sourceDir - * @param file $source - * @param dir $targetDir - * @param file $target - */ - public static function move($sourceDir,$source,$targetDir,$target) { - if(OC_User::isLoggedIn() && ($sourceDir != '' || $source != 'Shared')) { - $targetFile=self::normalizePath($targetDir.'/'.$target); - $sourceFile=self::normalizePath($sourceDir.'/'.$source); - return \OC\Files\Filesystem::rename($sourceFile,$targetFile); + * move a file or folder + * + * @param dir $sourceDir + * @param file $source + * @param dir $targetDir + * @param file $target + */ + public static function move($sourceDir, $source, $targetDir, $target) { + if (OC_User::isLoggedIn() && ($sourceDir != '' || $source != 'Shared')) { + $targetFile = self::normalizePath($targetDir . '/' . $target); + $sourceFile = self::normalizePath($sourceDir . '/' . $source); + return \OC\Files\Filesystem::rename($sourceFile, $targetFile); } else { return false; } } /** - * copy a file or folder - * - * @param dir $sourceDir - * @param file $source - * @param dir $targetDir - * @param file $target - */ - public static function copy($sourceDir,$source,$targetDir,$target) { - if(OC_User::isLoggedIn()) { - $targetFile=$targetDir.'/'.$target; - $sourceFile=$sourceDir.'/'.$source; - return \OC\Files\Filesystem::copy($sourceFile,$targetFile); + * copy a file or folder + * + * @param dir $sourceDir + * @param file $source + * @param dir $targetDir + * @param file $target + */ + public static function copy($sourceDir, $source, $targetDir, $target) { + if (OC_User::isLoggedIn()) { + $targetFile = $targetDir . '/' . $target; + $sourceFile = $sourceDir . '/' . $source; + return \OC\Files\Filesystem::copy($sourceFile, $targetFile); } } /** - * create a new file or folder - * - * @param dir $dir - * @param file $name - * @param type $type - */ - public static function newFile($dir,$name,$type) { - if(OC_User::isLoggedIn()) { - $file=$dir.'/'.$name; - if($type=='dir') { + * create a new file or folder + * + * @param dir $dir + * @param file $name + * @param type $type + */ + public static function newFile($dir, $name, $type) { + if (OC_User::isLoggedIn()) { + $file = $dir . '/' . $name; + if ($type == 'dir') { return \OC\Files\Filesystem::mkdir($file); - }elseif($type=='file') { - $fileHandle=\OC\Files\Filesystem::fopen($file, 'w'); - if($fileHandle) { + } elseif ($type == 'file') { + $fileHandle = \OC\Files\Filesystem::fopen($file, 'w'); + if ($fileHandle) { fclose($fileHandle); return true; - }else{ + } else { return false; } } @@ -297,29 +311,29 @@ class OC_Files { } /** - * deletes a file or folder - * - * @param dir $dir - * @param file $name - */ - public static function delete($dir,$file) { - if(OC_User::isLoggedIn() && ($dir!= '' || $file != 'Shared')) { - $file=$dir.'/'.$file; + * deletes a file or folder + * + * @param dir $dir + * @param file $name + */ + public static function delete($dir, $file) { + if (OC_User::isLoggedIn() && ($dir != '' || $file != 'Shared')) { + $file = $dir . '/' . $file; return \OC\Files\Filesystem::unlink($file); } } /** - * checks if the selected files are within the size constraint. If not, outputs an error page. - * - * @param dir $dir - * @param files $files - */ + * checks if the selected files are within the size constraint. If not, outputs an error page. + * + * @param dir $dir + * @param files $files + */ static function validateZipDownload($dir, $files) { - if(!OC_Config::getValue('allowZipDownload', true)) { + if (!OC_Config::getValue('allowZipDownload', true)) { $l = OC_L10N::get('lib'); header("HTTP/1.0 409 Conflict"); - $tmpl = new OC_Template( '', 'error', 'user' ); + $tmpl = new OC_Template('', 'error', 'user'); $errors = array( array( 'error' => $l->t('ZIP download is turned off.'), @@ -332,19 +346,19 @@ class OC_Files { } $zipLimit = OC_Config::getValue('maxZipInputSize', OC_Helper::computerFileSize('800 MB')); - if($zipLimit > 0) { + if ($zipLimit > 0) { $totalsize = 0; - if(is_array($files)) { - foreach($files as $file) { - $totalsize += \OC\Files\Filesystem::filesize($dir.'/'.$file); + if (is_array($files)) { + foreach ($files as $file) { + $totalsize += \OC\Files\Filesystem::filesize($dir . '/' . $file); } - }else{ - $totalsize += \OC\Files\Filesystem::filesize($dir.'/'.$files); + } else { + $totalsize += \OC\Files\Filesystem::filesize($dir . '/' . $files); } - if($totalsize > $zipLimit) { + if ($totalsize > $zipLimit) { $l = OC_L10N::get('lib'); header("HTTP/1.0 409 Conflict"); - $tmpl = new OC_Template( '', 'error', 'user' ); + $tmpl = new OC_Template('', 'error', 'user'); $errors = array( array( 'error' => $l->t('Selected files too large to generate zip file.'), @@ -359,78 +373,80 @@ class OC_Files { } /** - * try to detect the mime type of a file - * - * @param string path - * @return string guessed mime type - */ + * try to detect the mime type of a file + * + * @param string path + * @return string guessed mime type + */ static function getMimeType($path) { return \OC\Files\Filesystem::getMimeType($path); } /** - * get a file tree - * - * @param string path - * @return array - */ + * get a file tree + * + * @param string path + * @return array + */ static function getTree($path) { return \OC\Files\Filesystem::getTree($path); } /** - * pull a file from a remote server - * @param string source - * @param string token - * @param string dir - * @param string file - * @return string guessed mime type - */ - static function pull($source,$token,$dir,$file) { - $tmpfile=tempnam(get_temp_dir(),'remoteCloudFile'); - $fp=fopen($tmpfile,'w+'); - $url=$source.="/files/pull.php?token=$token"; - $ch=curl_init(); - curl_setopt($ch,CURLOPT_URL,$url); + * pull a file from a remote server + * + * @param string source + * @param string token + * @param string dir + * @param string file + * @return string guessed mime type + */ + static function pull($source, $token, $dir, $file) { + $tmpfile = tempnam(get_temp_dir(), 'remoteCloudFile'); + $fp = fopen($tmpfile, 'w+'); + $url = $source .= "/files/pull.php?token=$token"; + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_FILE, $fp); curl_exec($ch); fclose($fp); - $info=curl_getinfo($ch); - $httpCode=$info['http_code']; + $info = curl_getinfo($ch); + $httpCode = $info['http_code']; curl_close($ch); - if($httpCode==200 or $httpCode==0) { - \OC\Files\Filesystem::fromTmpFile($tmpfile,$dir.'/'.$file); + if ($httpCode == 200 or $httpCode == 0) { + \OC\Files\Filesystem::fromTmpFile($tmpfile, $dir . '/' . $file); return true; - }else{ + } else { return false; } } /** * set the maximum upload size limit for apache hosts using .htaccess + * * @param int size filesisze in bytes * @return false on failure, size on success */ static function setUploadLimit($size) { //don't allow user to break his config -- upper boundary - if($size > PHP_INT_MAX) { + if ($size > PHP_INT_MAX) { //max size is always 1 byte lower than computerFileSize returns - if($size > PHP_INT_MAX+1) + if ($size > PHP_INT_MAX + 1) return false; - $size -=1; + $size -= 1; } else { - $size=OC_Helper::humanFileSize($size); - $size=substr($size,0,-1);//strip the B - $size=str_replace(' ','',$size); //remove the space between the size and the postfix + $size = OC_Helper::humanFileSize($size); + $size = substr($size, 0, -1); //strip the B + $size = str_replace(' ', '', $size); //remove the space between the size and the postfix } //don't allow user to break his config -- broken or malicious size input - if(intval($size) == 0) { + if (intval($size) == 0) { return false; } - $htaccess = @file_get_contents(OC::$SERVERROOT.'/.htaccess'); //supress errors in case we don't have permissions for - if(!$htaccess) { + $htaccess = @file_get_contents(OC::$SERVERROOT . '/.htaccess'); //supress errors in case we don't have permissions for + if (!$htaccess) { return false; } @@ -439,50 +455,53 @@ class OC_Files { 'post_max_size' ); - foreach($phpValueKeys as $key) { - $pattern = '/php_value '.$key.' (\S)*/'; - $setting = 'php_value '.$key.' '.$size; - $hasReplaced = 0; - $content = preg_replace($pattern, $setting, $htaccess, 1, $hasReplaced); - if($content !== null) { + foreach ($phpValueKeys as $key) { + $pattern = '/php_value ' . $key . ' (\S)*/'; + $setting = 'php_value ' . $key . ' ' . $size; + $hasReplaced = 0; + $content = preg_replace($pattern, $setting, $htaccess, 1, $hasReplaced); + if ($content !== null) { $htaccess = $content; } - if($hasReplaced == 0) { + if ($hasReplaced == 0) { $htaccess .= "\n" . $setting; } } //check for write permissions - if(is_writable(OC::$SERVERROOT.'/.htaccess')) { - file_put_contents(OC::$SERVERROOT.'/.htaccess', $htaccess); + if (is_writable(OC::$SERVERROOT . '/.htaccess')) { + file_put_contents(OC::$SERVERROOT . '/.htaccess', $htaccess); return OC_Helper::computerFileSize($size); - } else { OC_Log::write('files','Can\'t write upload limit to '.OC::$SERVERROOT.'/.htaccess. Please check the file permissions',OC_Log::WARN); } + } else { + OC_Log::write('files', 'Can\'t write upload limit to ' . OC::$SERVERROOT . '/.htaccess. Please check the file permissions', OC_Log::WARN); + } return false; } /** * normalize a path, removing any double, add leading /, etc + * * @param string $path * @return string */ static public function normalizePath($path) { - $path='/'.$path; - $old=''; - while($old!=$path) {//replace any multiplicity of slashes with a single one - $old=$path; - $path=str_replace('//','/',$path); + $path = '/' . $path; + $old = ''; + while ($old != $path) { //replace any multiplicity of slashes with a single one + $old = $path; + $path = str_replace('//', '/', $path); } return $path; } } -function fileCmp($a,$b) { - if($a['type']=='dir' and $b['type']!='dir') { +function fileCmp($a, $b) { + if ($a['type'] == 'dir' and $b['type'] != 'dir') { return -1; - }elseif($a['type']!='dir' and $b['type']=='dir') { + } elseif ($a['type'] != 'dir' and $b['type'] == 'dir') { return 1; - }else{ - return strnatcasecmp($a['name'],$b['name']); + } else { + return strnatcasecmp($a['name'], $b['name']); } } diff --git a/tests/lib/files.php b/tests/lib/files.php index 9cb5276845..d978ac3fd1 100644 --- a/tests/lib/files.php +++ b/tests/lib/files.php @@ -67,10 +67,29 @@ class Test_Files extends PHPUnit_Framework_TestCase { $this->assertEquals($storageSize, $folderData[3]['size']); } + public function testAutoScan() { + $storage1 = $this->getTestStorage(false); + $storage2 = $this->getTestStorage(false); + Filesystem::mount($storage1, array(), '/'); + Filesystem::mount($storage2, array(), '/substorage'); + $textSize = strlen("dummy file data\n"); + $imageSize = filesize(\OC::$SERVERROOT . '/core/img/logo.png'); + $storageSize = $textSize * 2 + $imageSize; + + $cachedData = \OC_Files::getFileInfo('/'); + $this->assertEquals('httpd/unix-directory', $cachedData['mimetype']); + $this->assertEquals(-1, $cachedData['size']); + + $folderData = \OC_Files::getDirectoryContent('/substorage/folder'); + $this->assertEquals('text/plain', $folderData[0]['mimetype']); + $this->assertEquals($textSize, $folderData[0]['size']); + } + /** + * @param bool $scan * @return OC\Files\Storage\Storage */ - private function getTestStorage() { + private function getTestStorage($scan = true) { $storage = new \OC\Files\Storage\Temporary(array()); $textData = "dummy file data\n"; $imgData = file_get_contents(\OC::$SERVERROOT . '/core/img/logo.png'); @@ -79,8 +98,10 @@ class Test_Files extends PHPUnit_Framework_TestCase { $storage->file_put_contents('foo.png', $imgData); $storage->file_put_contents('folder/bar.txt', $textData); - $scanner = $storage->getScanner(); - $scanner->scan(''); + if ($scan) { + $scanner = $storage->getScanner(); + $scanner->scan(''); + } $this->storages[] = $storage; return $storage; } From 56c7ee799cb13f7a4db60663f60b76cdf223614b Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 23 Oct 2012 16:34:58 +0200 Subject: [PATCH 046/272] use strtotime on non-nummeric times in oc_filesystem::touch --- lib/files/view.php | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/lib/files/view.php b/lib/files/view.php index a59ad8105d..18d9193035 100644 --- a/lib/files/view.php +++ b/lib/files/view.php @@ -110,7 +110,7 @@ class View { */ public function getLocalFile($path) { $parent = substr($path, 0, strrpos($path, '/')); - list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($path); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($path); if (Filesystem::isValidPath($parent) and $storage) { return $storage->getLocalFile($internalPath); } else { @@ -124,7 +124,7 @@ class View { */ public function getLocalFolder($path) { $parent = substr($path, 0, strrpos($path, '/')); - list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($path); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($path); if (Filesystem::isValidPath($parent) and $storage) { return $storage->getLocalFolder($internalPath); } else { @@ -227,6 +227,9 @@ class View { } public function touch($path, $mtime = null) { + if (!is_null($mtime) and !is_numeric($mtime)) { + $mtime = strtotime($mtime); + } return $this->basicOperation('touch', $path, array('write'), $mtime); } @@ -331,8 +334,8 @@ class View { $mp1 = $this->getMountPoint($path1 . $postFix1); $mp2 = $this->getMountPoint($path2 . $postFix2); if ($mp1 == $mp2) { - list($storage, $internalPath1)=\OC\Files\Filesystem::resolvePath($path1 . $postFix1); - list( , $internalPath2)=\OC\Files\Filesystem::resolvePath($path2 . $postFix2); + list($storage, $internalPath1) = \OC\Files\Filesystem::resolvePath($path1 . $postFix1); + list(, $internalPath2) = \OC\Files\Filesystem::resolvePath($path2 . $postFix2); if ($storage) { $result = $storage->rename($internalPath1, $internalPath2); } else { @@ -342,7 +345,7 @@ class View { $source = $this->fopen($path1 . $postFix1, 'r'); $target = $this->fopen($path2 . $postFix2, 'w'); $count = \OC_Helper::streamCopy($source, $target); - list($storage1, $internalPath1)=\OC\Files\Filesystem::resolvePath($path1 . $postFix1); + list($storage1, $internalPath1) = \OC\Files\Filesystem::resolvePath($path1 . $postFix1); $storage1->unlink($internalPath1); $result = $count > 0; } @@ -414,8 +417,8 @@ class View { $mp1 = $this->getMountPoint($path1 . $postFix1); $mp2 = $this->getMountPoint($path2 . $postFix2); if ($mp1 == $mp2) { - list($storage, $internalPath1)=\OC\Files\Filesystem::resolvePath($path1 . $postFix1); - list( , $internalPath2)=\OC\Files\Filesystem::resolvePath($path2 . $postFix2); + list($storage, $internalPath1) = \OC\Files\Filesystem::resolvePath($path1 . $postFix1); + list(, $internalPath2) = \OC\Files\Filesystem::resolvePath($path2 . $postFix2); if ($storage) { $result = $storage->copy($internalPath1, $internalPath2); } else { @@ -550,7 +553,7 @@ class View { array(Filesystem::signal_param_path => $path) ); } - list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($path . $postFix); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($path . $postFix); if ($storage) { $result = $storage->hash($type, $internalPath, $raw); $result = \OC_FileProxy::runPostProxies('hash', $absolutePath, $result); @@ -585,7 +588,7 @@ class View { return false; } $run = $this->runHooks($hooks, $path); - list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($path . $postFix); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($path . $postFix); if ($run and $storage) { if (!is_null($extraParam)) { $result = $storage->$operation($internalPath, $extraParam); From 5a3d6805a2613c4f55daa971e112cc77f17b060f Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 24 Oct 2012 15:52:30 +0200 Subject: [PATCH 047/272] cleanup OC_Files a bit --- apps/files/ajax/delete.php | 23 +++--- apps/files/ajax/move.php | 13 +++- apps/files/ajax/newfile.php | 2 +- apps/files/ajax/newfolder.php | 2 +- apps/files/ajax/rename.php | 14 ++-- lib/files.php | 136 ---------------------------------- 6 files changed, 34 insertions(+), 156 deletions(-) diff --git a/apps/files/ajax/delete.php b/apps/files/ajax/delete.php index 57c8c15c19..ae6158a05a 100644 --- a/apps/files/ajax/delete.php +++ b/apps/files/ajax/delete.php @@ -12,17 +12,22 @@ $files = isset($_POST["file"]) ? stripslashes($_POST["file"]) : stripslashes($_P $files = explode(';', $files); $filesWithError = ''; -$success = true; -//Now delete -foreach($files as $file) { - if( !OC_Files::delete( $dir, $file )) { - $filesWithError .= $file . "\n"; - $success = false; +if (OC_User::isLoggedIn()) { + $success = true; + + //Now delete + foreach ($files as $file) { + if ($dir != '' || $file != 'Shared' && !\OC\Files\Filesystem::unlink($dir . '/' . $file)) { + $filesWithError .= $file . "\n"; + $success = false; + } } +} else { + $success = false; } -if($success) { - OCP\JSON::success(array("data" => array( "dir" => $dir, "files" => $files ))); +if ($success) { + OCP\JSON::success(array("data" => array("dir" => $dir, "files" => $files))); } else { - OCP\JSON::error(array("data" => array( "message" => "Could not delete:\n" . $filesWithError ))); + OCP\JSON::error(array("data" => array("message" => "Could not delete:\n" . $filesWithError))); } diff --git a/apps/files/ajax/move.php b/apps/files/ajax/move.php index 8b3149ef14..f872783122 100644 --- a/apps/files/ajax/move.php +++ b/apps/files/ajax/move.php @@ -11,9 +11,14 @@ $dir = stripslashes($_GET["dir"]); $file = stripslashes($_GET["file"]); $target = stripslashes($_GET["target"]); - -if(OC_Files::move($dir, $file, $target, $file)) { - OCP\JSON::success(array("data" => array( "dir" => $dir, "files" => $file ))); -} else { +if (OC_User::isLoggedIn() && ($dir != '' || $file != 'Shared')) { + $targetFile = \OC\Files\Filesystem::normalizePath($dir . '/' . $file); + $sourceFile = \OC\Files\Filesystem::normalizePath($target . '/' . $file); + if(\OC\Files\Filesystem::rename($sourceFile, $targetFile)) { + OCP\JSON::success(array("data" => array( "dir" => $dir, "files" => $file ))); + } else { + OCP\JSON::error(array("data" => array( "message" => "Could not move $file" ))); + } +}else{ OCP\JSON::error(array("data" => array( "message" => "Could not move $file" ))); } diff --git a/apps/files/ajax/newfile.php b/apps/files/ajax/newfile.php index 4d73970b68..f94d984d5b 100644 --- a/apps/files/ajax/newfile.php +++ b/apps/files/ajax/newfile.php @@ -82,7 +82,7 @@ if($source) { OCP\JSON::success(array("data" => array('content'=>$content, 'id' => $id))); exit(); } - }elseif(OC_Files::newFile($dir, $filename, 'file')) { + }elseif(\OC\Files\Filesystem::touch($dir . '/' . $filename)) { $meta = OC_FileCache::get($dir.'/'.$filename); $id = OC_FileCache::getId($dir.'/'.$filename); OCP\JSON::success(array("data" => array('content'=>$content, 'id' => $id))); diff --git a/apps/files/ajax/newfolder.php b/apps/files/ajax/newfolder.php index 0f1f2f14eb..c36c208455 100644 --- a/apps/files/ajax/newfolder.php +++ b/apps/files/ajax/newfolder.php @@ -19,7 +19,7 @@ if(strpos($foldername, '/')!==false) { exit(); } -if(OC_Files::newFile($dir, stripslashes($foldername), 'dir')) { +if(\OC\Files\Filesystem::mkdir($dir . '/' . stripslashes($foldername))) { if ( $dir != '/') { $path = $dir.'/'.$foldername; } else { diff --git a/apps/files/ajax/rename.php b/apps/files/ajax/rename.php index 45448279fa..a2b9b8de25 100644 --- a/apps/files/ajax/rename.php +++ b/apps/files/ajax/rename.php @@ -11,10 +11,14 @@ $dir = stripslashes($_GET["dir"]); $file = stripslashes($_GET["file"]); $newname = stripslashes($_GET["newname"]); -// Delete -if( OC_Files::move( $dir, $file, $dir, $newname )) { - OCP\JSON::success(array("data" => array( "dir" => $dir, "file" => $file, "newname" => $newname ))); -} -else{ +if (OC_User::isLoggedIn() && ($dir != '' || $file != 'Shared')) { + $targetFile = \OC\Files\Filesystem::normalizePath($dir . '/' . $file); + $sourceFile = \OC\Files\Filesystem::normalizePath($dir . '/' . $newname); + if(\OC\Files\Filesystem::rename($sourceFile, $targetFile)) { + OCP\JSON::success(array("data" => array( "dir" => $dir, "file" => $file, "newname" => $newname ))); + } else { + OCP\JSON::error(array("data" => array( "message" => "Unable to rename file" ))); + } +}else{ OCP\JSON::error(array("data" => array( "message" => "Unable to rename file" ))); } diff --git a/lib/files.php b/lib/files.php index ff9befa89e..7ab7c89201 100644 --- a/lib/files.php +++ b/lib/files.php @@ -252,77 +252,6 @@ class OC_Files { } } - /** - * move a file or folder - * - * @param dir $sourceDir - * @param file $source - * @param dir $targetDir - * @param file $target - */ - public static function move($sourceDir, $source, $targetDir, $target) { - if (OC_User::isLoggedIn() && ($sourceDir != '' || $source != 'Shared')) { - $targetFile = self::normalizePath($targetDir . '/' . $target); - $sourceFile = self::normalizePath($sourceDir . '/' . $source); - return \OC\Files\Filesystem::rename($sourceFile, $targetFile); - } else { - return false; - } - } - - /** - * copy a file or folder - * - * @param dir $sourceDir - * @param file $source - * @param dir $targetDir - * @param file $target - */ - public static function copy($sourceDir, $source, $targetDir, $target) { - if (OC_User::isLoggedIn()) { - $targetFile = $targetDir . '/' . $target; - $sourceFile = $sourceDir . '/' . $source; - return \OC\Files\Filesystem::copy($sourceFile, $targetFile); - } - } - - /** - * create a new file or folder - * - * @param dir $dir - * @param file $name - * @param type $type - */ - public static function newFile($dir, $name, $type) { - if (OC_User::isLoggedIn()) { - $file = $dir . '/' . $name; - if ($type == 'dir') { - return \OC\Files\Filesystem::mkdir($file); - } elseif ($type == 'file') { - $fileHandle = \OC\Files\Filesystem::fopen($file, 'w'); - if ($fileHandle) { - fclose($fileHandle); - return true; - } else { - return false; - } - } - } - } - - /** - * deletes a file or folder - * - * @param dir $dir - * @param file $name - */ - public static function delete($dir, $file) { - if (OC_User::isLoggedIn() && ($dir != '' || $file != 'Shared')) { - $file = $dir . '/' . $file; - return \OC\Files\Filesystem::unlink($file); - } - } - /** * checks if the selected files are within the size constraint. If not, outputs an error page. * @@ -372,55 +301,6 @@ class OC_Files { } } - /** - * try to detect the mime type of a file - * - * @param string path - * @return string guessed mime type - */ - static function getMimeType($path) { - return \OC\Files\Filesystem::getMimeType($path); - } - - /** - * get a file tree - * - * @param string path - * @return array - */ - static function getTree($path) { - return \OC\Files\Filesystem::getTree($path); - } - - /** - * pull a file from a remote server - * - * @param string source - * @param string token - * @param string dir - * @param string file - * @return string guessed mime type - */ - static function pull($source, $token, $dir, $file) { - $tmpfile = tempnam(get_temp_dir(), 'remoteCloudFile'); - $fp = fopen($tmpfile, 'w+'); - $url = $source .= "/files/pull.php?token=$token"; - $ch = curl_init(); - curl_setopt($ch, CURLOPT_URL, $url); - curl_setopt($ch, CURLOPT_FILE, $fp); - curl_exec($ch); - fclose($fp); - $info = curl_getinfo($ch); - $httpCode = $info['http_code']; - curl_close($ch); - if ($httpCode == 200 or $httpCode == 0) { - \OC\Files\Filesystem::fromTmpFile($tmpfile, $dir . '/' . $file); - return true; - } else { - return false; - } - } - /** * set the maximum upload size limit for apache hosts using .htaccess * @@ -478,22 +358,6 @@ class OC_Files { return false; } - - /** - * normalize a path, removing any double, add leading /, etc - * - * @param string $path - * @return string - */ - static public function normalizePath($path) { - $path = '/' . $path; - $old = ''; - while ($old != $path) { //replace any multiplicity of slashes with a single one - $old = $path; - $path = str_replace('//', '/', $path); - } - return $path; - } } function fileCmp($a, $b) { From 39adadd3e3e50dcf3bf577a22870aaec52f63052 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 26 Oct 2012 12:30:25 +0200 Subject: [PATCH 048/272] move the cache api from OC_Files to filesystem(view) --- lib/files.php | 92 ------------------------- lib/files/cache/cache.php | 5 ++ lib/files/filesystem.php | 27 ++++++++ lib/files/view.php | 92 +++++++++++++++++++++++++ tests/lib/{files.php => files/view.php} | 23 ++++--- 5 files changed, 138 insertions(+), 101 deletions(-) rename tests/lib/{files.php => files/view.php} (86%) diff --git a/lib/files.php b/lib/files.php index 7ab7c89201..422e7f4ffe 100644 --- a/lib/files.php +++ b/lib/files.php @@ -28,98 +28,6 @@ class OC_Files { static $tmpFiles = array(); - /** - * get the filesystem info - * - * @param string $path - * @return array - * - * returns an associative array with the following keys: - * - size - * - mtime - * - mimetype - * - encrypted - * - versioned - */ - public static function getFileInfo($path) { - $path = \OC\Files\Filesystem::normalizePath($path); - /** - * @var \OC\Files\Storage\Storage $storage - * @var string $path - */ - list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($path); - $cache = $storage->getCache(); - - if (!$cache->inCache($internalPath)) { - $scanner = $storage->getScanner(); - $scanner->scan($internalPath, \OC\Files\Cache\Scanner::SCAN_SHALLOW); - } - - $data = $cache->get($internalPath); - - if ($data['mimetype'] === 'httpd/unix-directory') { - //add the sizes of other mountpoints to the folder - $mountPoints = \OC\Files\Filesystem::getMountPoints($path); - foreach ($mountPoints as $mountPoint) { - $subStorage = \OC\Files\Filesystem::getStorage($mountPoint); - $subCache = $subStorage->getCache(); - $rootEntry = $subCache->get(''); - - $data['size'] += $rootEntry['size']; - } - } - - return $data; - } - - /** - * get the content of a directory - * - * @param string $directory path under datadirectory - * @return array - */ - public static function getDirectoryContent($directory, $mimetype_filter = '') { - $path = \OC\Files\Filesystem::normalizePath($directory); - /** - * @var \OC\Files\Storage\Storage $storage - * @var string $path - */ - list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($path); - $cache = $storage->getCache(); - - if (!$cache->inCache($internalPath)) { - $scanner = $storage->getScanner(); - $scanner->scan($internalPath, \OC\Files\Cache\Scanner::SCAN_SHALLOW); - } - - $files = $cache->getFolderContents($internalPath); //TODO: mimetype_filter - - //add a folder for any mountpoint in this directory and add the sizes of other mountpoints to the folders - $mountPoints = \OC\Files\Filesystem::getMountPoints($directory); - $dirLength = strlen($path); - foreach ($mountPoints as $mountPoint) { - $subStorage = \OC\Files\Filesystem::getStorage($mountPoint); - $subCache = $subStorage->getCache(); - $rootEntry = $subCache->get(''); - - $relativePath = trim(substr($mountPoint, $dirLength), '/'); - if ($pos = strpos($relativePath, '/')) { //mountpoint inside subfolder add size to the correct folder - $entryName = substr($relativePath, 0, $pos); - foreach ($files as &$entry) { - if ($entry['name'] === $entryName) { - $entry['size'] += $rootEntry['size']; - } - } - } else { //mountpoint in this folder, add an entry for it - $rootEntry['name'] = $relativePath; - $files[] = $rootEntry; - } - } - - usort($files, "fileCmp"); //TODO: remove this once ajax is merged - return $files; - } - public static function searchByMime($mimetype_filter) { $files = array(); $dirs_to_check = array(''); diff --git a/lib/files/cache/cache.php b/lib/files/cache/cache.php index 5ef49246ea..9b55666f95 100644 --- a/lib/files/cache/cache.php +++ b/lib/files/cache/cache.php @@ -8,6 +8,11 @@ namespace OC\Files\Cache; +/** + * Metadata cache for the filesystem + * + * don't use this class directly if you need to get metadata, use \OC\Files\Filesystem::getFileInfo instead + */ class Cache { const NOT_FOUND = 0; const PARTIAL = 1; //only partial data available, file not cached in the database diff --git a/lib/files/filesystem.php b/lib/files/filesystem.php index b7f8483fbf..d735cf8626 100644 --- a/lib/files/filesystem.php +++ b/lib/files/filesystem.php @@ -609,6 +609,33 @@ class Filesystem { } return $path; } + + /** + * get the filesystem info + * + * @param string $path + * @return array + * + * returns an associative array with the following keys: + * - size + * - mtime + * - mimetype + * - encrypted + * - versioned + */ + public static function getFileInfo($path) { + return self::$defaultInstance->getFileInfo($path); + } + + /** + * get the content of a directory + * + * @param string $directory path under datadirectory + * @return array + */ + public static function getDirectoryContent($directory, $mimetype_filter = '') { + return self::$defaultInstance->getDirectoryContent($directory, $mimetype_filter); + } } \OC_Hook::connect('OC_Filesystem', 'post_write', 'OC_Filesystem', 'removeETagHook'); diff --git a/lib/files/view.php b/lib/files/view.php index 18d9193035..aaca1618ac 100644 --- a/lib/files/view.php +++ b/lib/files/view.php @@ -645,4 +645,96 @@ class View { public function hasUpdated($path, $time) { return $this->basicOperation('hasUpdated', $path, array(), $time); } + + /** + * get the filesystem info + * + * @param string $path + * @return array + * + * returns an associative array with the following keys: + * - size + * - mtime + * - mimetype + * - encrypted + * - versioned + */ + public function getFileInfo($path) { + $path = \OC\Files\Filesystem::normalizePath($this->fakeRoot . '/' . $path); + /** + * @var \OC\Files\Storage\Storage $storage + * @var string $path + */ + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($path); + $cache = $storage->getCache(); + + if (!$cache->inCache($internalPath)) { + $scanner = $storage->getScanner(); + $scanner->scan($internalPath, \OC\Files\Cache\Scanner::SCAN_SHALLOW); + } + + $data = $cache->get($internalPath); + + if ($data['mimetype'] === 'httpd/unix-directory') { + //add the sizes of other mountpoints to the folder + $mountPoints = \OC\Files\Filesystem::getMountPoints($path); + foreach ($mountPoints as $mountPoint) { + $subStorage = \OC\Files\Filesystem::getStorage($mountPoint); + $subCache = $subStorage->getCache(); + $rootEntry = $subCache->get(''); + + $data['size'] += $rootEntry['size']; + } + } + + return $data; + } + + /** + * get the content of a directory + * + * @param string $directory path under datadirectory + * @return array + */ + public function getDirectoryContent($directory, $mimetype_filter = '') { + $path = \OC\Files\Filesystem::normalizePath($this->fakeRoot . '/' . $directory); + /** + * @var \OC\Files\Storage\Storage $storage + * @var string $path + */ + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($path); + $cache = $storage->getCache(); + + if (!$cache->inCache($internalPath)) { + $scanner = $storage->getScanner(); + $scanner->scan($internalPath, \OC\Files\Cache\Scanner::SCAN_SHALLOW); + } + + $files = $cache->getFolderContents($internalPath); //TODO: mimetype_filter + + //add a folder for any mountpoint in this directory and add the sizes of other mountpoints to the folders + $mountPoints = \OC\Files\Filesystem::getMountPoints($directory); + $dirLength = strlen($path); + foreach ($mountPoints as $mountPoint) { + $subStorage = \OC\Files\Filesystem::getStorage($mountPoint); + $subCache = $subStorage->getCache(); + $rootEntry = $subCache->get(''); + + $relativePath = trim(substr($mountPoint, $dirLength), '/'); + if ($pos = strpos($relativePath, '/')) { //mountpoint inside subfolder add size to the correct folder + $entryName = substr($relativePath, 0, $pos); + foreach ($files as &$entry) { + if ($entry['name'] === $entryName) { + $entry['size'] += $rootEntry['size']; + } + } + } else { //mountpoint in this folder, add an entry for it + $rootEntry['name'] = $relativePath; + $files[] = $rootEntry; + } + } + + usort($files, "fileCmp"); //TODO: remove this once ajax is merged + return $files; + } } diff --git a/tests/lib/files.php b/tests/lib/files/view.php similarity index 86% rename from tests/lib/files.php rename to tests/lib/files/view.php index d978ac3fd1..6e7608f596 100644 --- a/tests/lib/files.php +++ b/tests/lib/files/view.php @@ -5,9 +5,11 @@ * later. * See the COPYING-README file. */ +namespace Test\Files; + use \OC\Files\Filesystem as Filesystem; -class Test_Files extends PHPUnit_Framework_TestCase { +class View extends \PHPUnit_Framework_TestCase { /** * @var \OC\Files\Storage\Storage[] $storages; */ @@ -35,19 +37,21 @@ class Test_Files extends PHPUnit_Framework_TestCase { $imageSize = filesize(\OC::$SERVERROOT . '/core/img/logo.png'); $storageSize = $textSize * 2 + $imageSize; - $cachedData = OC_Files::getFileInfo('/foo.txt'); + $rootView = new \OC\Files\View(''); + + $cachedData = $rootView->getFileInfo('/foo.txt'); $this->assertEquals($textSize, $cachedData['size']); $this->assertEquals('text/plain', $cachedData['mimetype']); - $cachedData = OC_Files::getFileInfo('/'); + $cachedData = $rootView->getFileInfo('/'); $this->assertEquals($storageSize * 3, $cachedData['size']); $this->assertEquals('httpd/unix-directory', $cachedData['mimetype']); - $cachedData = OC_Files::getFileInfo('/folder'); + $cachedData = $rootView->getFileInfo('/folder'); $this->assertEquals($storageSize + $textSize, $cachedData['size']); $this->assertEquals('httpd/unix-directory', $cachedData['mimetype']); - $folderData = OC_Files::getDirectoryContent('/'); + $folderData = $rootView->getDirectoryContent('/'); /** * expected entries: * folder @@ -74,20 +78,21 @@ class Test_Files extends PHPUnit_Framework_TestCase { Filesystem::mount($storage2, array(), '/substorage'); $textSize = strlen("dummy file data\n"); $imageSize = filesize(\OC::$SERVERROOT . '/core/img/logo.png'); - $storageSize = $textSize * 2 + $imageSize; - $cachedData = \OC_Files::getFileInfo('/'); + $rootView = new \OC\Files\View(''); + + $cachedData = $rootView->getFileInfo('/'); $this->assertEquals('httpd/unix-directory', $cachedData['mimetype']); $this->assertEquals(-1, $cachedData['size']); - $folderData = \OC_Files::getDirectoryContent('/substorage/folder'); + $folderData = $rootView->getDirectoryContent('/substorage/folder'); $this->assertEquals('text/plain', $folderData[0]['mimetype']); $this->assertEquals($textSize, $folderData[0]['size']); } /** * @param bool $scan - * @return OC\Files\Storage\Storage + * @return \OC\Files\Storage\Storage */ private function getTestStorage($scan = true) { $storage = new \OC\Files\Storage\Temporary(array()); From e63e246c4894aa7f72feb550d98747890838af0b Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 26 Oct 2012 12:37:49 +0200 Subject: [PATCH 049/272] fix problem with normalizePath when there was a double leading slash --- lib/files/filesystem.php | 8 ++++---- tests/lib/files/view.php | 3 +++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/files/filesystem.php b/lib/files/filesystem.php index d735cf8626..2b51131b20 100644 --- a/lib/files/filesystem.php +++ b/lib/files/filesystem.php @@ -595,14 +595,14 @@ class Filesystem { if ($path[0] !== '/') { $path = '/' . $path; } -//remove trailing slash - if ($stripTrailingSlash and strlen($path) > 1 and substr($path, -1, 1) === '/') { - $path = substr($path, 0, -1); - } //remove duplicate slashes while (strpos($path, '//') !== false) { $path = str_replace('//', '/', $path); } +//remove trailing slash + if ($stripTrailingSlash and strlen($path) > 1 and substr($path, -1, 1) === '/') { + $path = substr($path, 0, -1); + } //normalize unicode if possible if (class_exists('Normalizer')) { $path = \Normalizer::normalize($path); diff --git a/tests/lib/files/view.php b/tests/lib/files/view.php index 6e7608f596..adbed5a18b 100644 --- a/tests/lib/files/view.php +++ b/tests/lib/files/view.php @@ -69,6 +69,9 @@ class View extends \PHPUnit_Framework_TestCase { $this->assertEquals($imageSize, $folderData[1]['size']); $this->assertEquals($textSize, $folderData[2]['size']); $this->assertEquals($storageSize, $folderData[3]['size']); + + $folderView = new \OC\Files\View('/folder'); + $this->assertEquals($rootView->getFileInfo('/folder'), $folderView->getFileInfo('/')); } public function testAutoScan() { From 7ef0ffe8ad8f95b023f95f80e7b91c2a3ee50c67 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 26 Oct 2012 12:43:23 +0200 Subject: [PATCH 050/272] add View->putFileInfo to the filecache api --- lib/files/view.php | 30 ++++++++++++++++++++++++++++-- tests/lib/files/view.php | 7 +++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/lib/files/view.php b/lib/files/view.php index aaca1618ac..094a7d92a4 100644 --- a/lib/files/view.php +++ b/lib/files/view.php @@ -663,7 +663,7 @@ class View { $path = \OC\Files\Filesystem::normalizePath($this->fakeRoot . '/' . $path); /** * @var \OC\Files\Storage\Storage $storage - * @var string $path + * @var string $internalPath */ list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($path); $cache = $storage->getCache(); @@ -700,7 +700,7 @@ class View { $path = \OC\Files\Filesystem::normalizePath($this->fakeRoot . '/' . $directory); /** * @var \OC\Files\Storage\Storage $storage - * @var string $path + * @var string $internalPath */ list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($path); $cache = $storage->getCache(); @@ -737,4 +737,30 @@ class View { usort($files, "fileCmp"); //TODO: remove this once ajax is merged return $files; } + + /** + * change file metadata + * + * @param string $path + * @param array $data + * @return int + * + * returns the fileid of the updated file + */ + public function putFileInfo($path, $data) { + $path = \OC\Files\Filesystem::normalizePath($this->fakeRoot . '/' . $path); + /** + * @var \OC\Files\Storage\Storage $storage + * @var string $internalPath + */ + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($path); + $cache = $storage->getCache(); + + if (!$cache->inCache($internalPath)) { + $scanner = $storage->getScanner(); + $scanner->scan($internalPath, \OC\Files\Cache\Scanner::SCAN_SHALLOW); + } + + return $cache->put($internalPath, $data); + } } diff --git a/tests/lib/files/view.php b/tests/lib/files/view.php index adbed5a18b..051ae25162 100644 --- a/tests/lib/files/view.php +++ b/tests/lib/files/view.php @@ -72,6 +72,13 @@ class View extends \PHPUnit_Framework_TestCase { $folderView = new \OC\Files\View('/folder'); $this->assertEquals($rootView->getFileInfo('/folder'), $folderView->getFileInfo('/')); + + $cachedData = $rootView->getFileInfo('/foo.txt'); + $this->assertFalse($cachedData['encrypted']); + $id = $rootView->putFileInfo('/foo.txt', array('encrypted' => true)); + $cachedData = $rootView->getFileInfo('/foo.txt'); + $this->assertTrue($cachedData['encrypted']); + $this->assertEquals($cachedData['fileid'], $id); } public function testAutoScan() { From 8bce661e4ddedb611d5873c578203dfda631ce7e Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 26 Oct 2012 13:23:15 +0200 Subject: [PATCH 051/272] add search to the filecache api --- lib/files/cache/cache.php | 21 ++++++++- lib/files/filesystem.php | 2 +- lib/files/view.php | 84 ++++++++++++++++++++++++--------- tests/lib/files/cache/cache.php | 20 ++++++++ tests/lib/files/view.php | 45 +++++++++++++++++- 5 files changed, 146 insertions(+), 26 deletions(-) diff --git a/lib/files/cache/cache.php b/lib/files/cache/cache.php index 9b55666f95..7e0c5cb21f 100644 --- a/lib/files/cache/cache.php +++ b/lib/files/cache/cache.php @@ -251,7 +251,7 @@ class Cache { */ public function getStatus($file) { $pathHash = md5($file); - $query = \OC_DB::prepare('SELECT * FROM `*PREFIX*filecache` WHERE `storage` = ? AND `path_hash` = ?'); + $query = \OC_DB::prepare('SELECT `size` FROM `*PREFIX*filecache` WHERE `storage` = ? AND `path_hash` = ?'); $result = $query->execute(array($this->storageId, $pathHash)); if ($row = $result->fetchRow()) { if ((int)$row['size'] === -1) { @@ -267,4 +267,23 @@ class Cache { } } } + + /** + * search for files matching $pattern + * + * @param string $pattern + * @return array of file data + */ + public function search($pattern) { + $query = \OC_DB::prepare(' + SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted` + FROM `*PREFIX*filecache` WHERE `name` LIKE ? AND `storage` = ?' + ); + $result = $query->execute(array($pattern, $this->storageId)); + $files = array(); + while ($row = $result->fetchRow()) { + $files[] = $row; + } + return $files; + } } diff --git a/lib/files/filesystem.php b/lib/files/filesystem.php index 2b51131b20..4031c0c5b8 100644 --- a/lib/files/filesystem.php +++ b/lib/files/filesystem.php @@ -548,7 +548,7 @@ class Filesystem { } static public function search($query) { - return Cache\Cache::search($query); + return self::$defaultInstance->search($query); } /** diff --git a/lib/files/view.php b/lib/files/view.php index 094a7d92a4..ee95cce0c1 100644 --- a/lib/files/view.php +++ b/lib/files/view.php @@ -110,7 +110,7 @@ class View { */ public function getLocalFile($path) { $parent = substr($path, 0, strrpos($path, '/')); - list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($path); + list($storage, $internalPath) = Filesystem::resolvePath($path); if (Filesystem::isValidPath($parent) and $storage) { return $storage->getLocalFile($internalPath); } else { @@ -124,7 +124,7 @@ class View { */ public function getLocalFolder($path) { $parent = substr($path, 0, strrpos($path, '/')); - list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($path); + list($storage, $internalPath) = Filesystem::resolvePath($path); if (Filesystem::isValidPath($parent) and $storage) { return $storage->getLocalFolder($internalPath); } else { @@ -150,7 +150,7 @@ class View { } public function readdir($handle) { - $fsLocal = new \OC\Files\Storage\Local(array('datadir' => '/')); + $fsLocal = new Storage\Local(array('datadir' => '/')); return $fsLocal->readdir($handle); } @@ -334,8 +334,8 @@ class View { $mp1 = $this->getMountPoint($path1 . $postFix1); $mp2 = $this->getMountPoint($path2 . $postFix2); if ($mp1 == $mp2) { - list($storage, $internalPath1) = \OC\Files\Filesystem::resolvePath($path1 . $postFix1); - list(, $internalPath2) = \OC\Files\Filesystem::resolvePath($path2 . $postFix2); + list($storage, $internalPath1) = Filesystem::resolvePath($path1 . $postFix1); + list(, $internalPath2) = Filesystem::resolvePath($path2 . $postFix2); if ($storage) { $result = $storage->rename($internalPath1, $internalPath2); } else { @@ -345,7 +345,7 @@ class View { $source = $this->fopen($path1 . $postFix1, 'r'); $target = $this->fopen($path2 . $postFix2, 'w'); $count = \OC_Helper::streamCopy($source, $target); - list($storage1, $internalPath1) = \OC\Files\Filesystem::resolvePath($path1 . $postFix1); + list($storage1, $internalPath1) = Filesystem::resolvePath($path1 . $postFix1); $storage1->unlink($internalPath1); $result = $count > 0; } @@ -417,8 +417,8 @@ class View { $mp1 = $this->getMountPoint($path1 . $postFix1); $mp2 = $this->getMountPoint($path2 . $postFix2); if ($mp1 == $mp2) { - list($storage, $internalPath1) = \OC\Files\Filesystem::resolvePath($path1 . $postFix1); - list(, $internalPath2) = \OC\Files\Filesystem::resolvePath($path2 . $postFix2); + list($storage, $internalPath1) = Filesystem::resolvePath($path1 . $postFix1); + list(, $internalPath2) = Filesystem::resolvePath($path2 . $postFix2); if ($storage) { $result = $storage->copy($internalPath1, $internalPath2); } else { @@ -553,7 +553,7 @@ class View { array(Filesystem::signal_param_path => $path) ); } - list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($path . $postFix); + list($storage, $internalPath) = Filesystem::resolvePath($path . $postFix); if ($storage) { $result = $storage->hash($type, $internalPath, $raw); $result = \OC_FileProxy::runPostProxies('hash', $absolutePath, $result); @@ -588,7 +588,7 @@ class View { return false; } $run = $this->runHooks($hooks, $path); - list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($path . $postFix); + list($storage, $internalPath) = Filesystem::resolvePath($path . $postFix); if ($run and $storage) { if (!is_null($extraParam)) { $result = $storage->$operation($internalPath, $extraParam); @@ -660,26 +660,26 @@ class View { * - versioned */ public function getFileInfo($path) { - $path = \OC\Files\Filesystem::normalizePath($this->fakeRoot . '/' . $path); + $path = Filesystem::normalizePath($this->fakeRoot . '/' . $path); /** * @var \OC\Files\Storage\Storage $storage * @var string $internalPath */ - list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($path); + list($storage, $internalPath) = Filesystem::resolvePath($path); $cache = $storage->getCache(); if (!$cache->inCache($internalPath)) { $scanner = $storage->getScanner(); - $scanner->scan($internalPath, \OC\Files\Cache\Scanner::SCAN_SHALLOW); + $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW); } $data = $cache->get($internalPath); if ($data['mimetype'] === 'httpd/unix-directory') { //add the sizes of other mountpoints to the folder - $mountPoints = \OC\Files\Filesystem::getMountPoints($path); + $mountPoints = Filesystem::getMountPoints($path); foreach ($mountPoints as $mountPoint) { - $subStorage = \OC\Files\Filesystem::getStorage($mountPoint); + $subStorage = Filesystem::getStorage($mountPoint); $subCache = $subStorage->getCache(); $rootEntry = $subCache->get(''); @@ -697,26 +697,26 @@ class View { * @return array */ public function getDirectoryContent($directory, $mimetype_filter = '') { - $path = \OC\Files\Filesystem::normalizePath($this->fakeRoot . '/' . $directory); + $path = Filesystem::normalizePath($this->fakeRoot . '/' . $directory); /** * @var \OC\Files\Storage\Storage $storage * @var string $internalPath */ - list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($path); + list($storage, $internalPath) = Filesystem::resolvePath($path); $cache = $storage->getCache(); if (!$cache->inCache($internalPath)) { $scanner = $storage->getScanner(); - $scanner->scan($internalPath, \OC\Files\Cache\Scanner::SCAN_SHALLOW); + $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW); } $files = $cache->getFolderContents($internalPath); //TODO: mimetype_filter //add a folder for any mountpoint in this directory and add the sizes of other mountpoints to the folders - $mountPoints = \OC\Files\Filesystem::getMountPoints($directory); + $mountPoints = Filesystem::getMountPoints($directory); $dirLength = strlen($path); foreach ($mountPoints as $mountPoint) { - $subStorage = \OC\Files\Filesystem::getStorage($mountPoint); + $subStorage = Filesystem::getStorage($mountPoint); $subCache = $subStorage->getCache(); $rootEntry = $subCache->get(''); @@ -748,19 +748,57 @@ class View { * returns the fileid of the updated file */ public function putFileInfo($path, $data) { - $path = \OC\Files\Filesystem::normalizePath($this->fakeRoot . '/' . $path); + $path = Filesystem::normalizePath($this->fakeRoot . '/' . $path); /** * @var \OC\Files\Storage\Storage $storage * @var string $internalPath */ - list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($path); + list($storage, $internalPath) = Filesystem::resolvePath($path); $cache = $storage->getCache(); if (!$cache->inCache($internalPath)) { $scanner = $storage->getScanner(); - $scanner->scan($internalPath, \OC\Files\Cache\Scanner::SCAN_SHALLOW); + $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW); } return $cache->put($internalPath, $data); } + + /** + * search for files with the name matching $query + * + * @param string $query + * @return array + */ + public function search($query) { + $files = array(); + $rootLength = strlen($this->fakeRoot); + + $mountPoint = Filesystem::getMountPoint($this->fakeRoot); + $storage = Filesystem::getStorage($mountPoint); + $cache = $storage->getCache(); + + $results = $cache->search('%' . $query . '%'); + foreach ($results as $result) { + if (substr($mountPoint . $result['path'], 0, $rootLength) === $this->fakeRoot) { + $result['path'] = substr($mountPoint . $result['path'], $rootLength); + $files[] = $result; + } + } + + $mountPoints = Filesystem::getMountPoints($this->fakeRoot); + foreach ($mountPoints as $mountPoint) { + $storage = Filesystem::getStorage($mountPoint); + $cache = $storage->getCache(); + + $relativeMountPoint = substr($mountPoint, $rootLength); + $results = $cache->search('%' . $query . '%'); + foreach ($results as $result) { + $result['path'] = $relativeMountPoint . $result['path']; + $files[] = $result; + } + } + + return $files; + } } diff --git a/tests/lib/files/cache/cache.php b/tests/lib/files/cache/cache.php index 177cf1c045..839e06e93f 100644 --- a/tests/lib/files/cache/cache.php +++ b/tests/lib/files/cache/cache.php @@ -111,6 +111,26 @@ class Cache extends \UnitTestCase { $this->assertEquals(\OC\Files\Cache\Cache::COMPLETE, $this->cache->getStatus('foo')); } + function testSearch() { + $file1 = 'folder'; + $file2 = 'folder/foobar'; + $file3 = 'folder/foo'; + $data1 = array('size' => 100, 'mtime' => 50, 'mimetype' => 'foo/folder'); + $fileData = array(); + $fileData['foobar'] = array('size' => 1000, 'mtime' => 20, 'mimetype' => 'foo/file'); + $fileData['foo'] = array('size' => 20, 'mtime' => 25, 'mimetype' => 'foo/file'); + + $this->cache->put($file1, $data1); + $this->cache->put($file2, $fileData['foobar']); + $this->cache->put($file3, $fileData['foo']); + + $this->assertEquals(2, count($this->cache->search('%foo%'))); + $this->assertEquals(1, count($this->cache->search('foo'))); + $this->assertEquals(1, count($this->cache->search('%folder%'))); + $this->assertEquals(1, count($this->cache->search('folder%'))); + $this->assertEquals(3, count($this->cache->search('%'))); + } + public function tearDown() { $this->cache->clear(); } diff --git a/tests/lib/files/view.php b/tests/lib/files/view.php index 051ae25162..fc872ea5e2 100644 --- a/tests/lib/files/view.php +++ b/tests/lib/files/view.php @@ -87,7 +87,6 @@ class View extends \PHPUnit_Framework_TestCase { Filesystem::mount($storage1, array(), '/'); Filesystem::mount($storage2, array(), '/substorage'); $textSize = strlen("dummy file data\n"); - $imageSize = filesize(\OC::$SERVERROOT . '/core/img/logo.png'); $rootView = new \OC\Files\View(''); @@ -100,6 +99,50 @@ class View extends \PHPUnit_Framework_TestCase { $this->assertEquals($textSize, $folderData[0]['size']); } + function testSearch() { + $storage1 = $this->getTestStorage(); + $storage2 = $this->getTestStorage(); + $storage3 = $this->getTestStorage(); + Filesystem::mount($storage1, array(), '/'); + Filesystem::mount($storage2, array(), '/substorage'); + Filesystem::mount($storage3, array(), '/folder/anotherstorage'); + + $rootView = new \OC\Files\View(''); + + $results = $rootView->search('foo'); + $this->assertEquals(6, count($results)); + $paths = array(); + foreach ($results as $result) { + $this->assertEquals($result['path'], Filesystem::normalizePath($result['path'])); + $paths[] = $result['path']; + } + $this->assertContains('/foo.txt', $paths); + $this->assertContains('/foo.png', $paths); + $this->assertContains('/substorage/foo.txt', $paths); + $this->assertContains('/substorage/foo.png', $paths); + $this->assertContains('/folder/anotherstorage/foo.txt', $paths); + $this->assertContains('/folder/anotherstorage/foo.png', $paths); + + $folderView = new \OC\Files\View('/folder'); + $results = $folderView->search('bar'); + $this->assertEquals(2, count($results)); + $paths = array(); + foreach ($results as $result) { + $paths[] = $result['path']; + } + $this->assertContains('/anotherstorage/folder/bar.txt', $paths); + $this->assertContains('/bar.txt', $paths); + + $results = $folderView->search('foo'); + $this->assertEquals(2, count($results)); + $paths = array(); + foreach ($results as $result) { + $paths[] = $result['path']; + } + $this->assertContains('/anotherstorage/foo.txt', $paths); + $this->assertContains('/anotherstorage/foo.png', $paths); + } + /** * @param bool $scan * @return \OC\Files\Storage\Storage From 7ad8bf3156a794e47ee223efd2e1cf9191cc8a97 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 26 Oct 2012 18:07:01 +0200 Subject: [PATCH 052/272] move filesystem test case --- tests/lib/files/filesystem.php | 110 +++++++++++++++++++++++++++++++++ tests/lib/files/view.php | 22 +++---- tests/lib/filesystem.php | 110 --------------------------------- 3 files changed, 120 insertions(+), 122 deletions(-) create mode 100644 tests/lib/files/filesystem.php delete mode 100644 tests/lib/filesystem.php diff --git a/tests/lib/files/filesystem.php b/tests/lib/files/filesystem.php new file mode 100644 index 0000000000..363426511b --- /dev/null +++ b/tests/lib/files/filesystem.php @@ -0,0 +1,110 @@ +. + * + */ + +namespace Test\Files; + +class Filesystem extends \PHPUnit_Framework_TestCase { + /** + * @var array tmpDirs + */ + private $tmpDirs=array(); + + /** + * @return array + */ + private function getStorageData() { + $dir = \OC_Helper::tmpFolder(); + $this->tmpDirs[] = $dir; + return array('datadir' => $dir); + } + + public function tearDown() { + foreach ($this->tmpDirs as $dir) { + \OC_Helper::rmdirr($dir); + } + } + + public function setUp() { + \OC\Files\Filesystem::clearMounts(); + } + + public function testMount() { + \OC\Files\Filesystem::mount('\OC\Files\Storage\Local',self::getStorageData(),'/'); + $this->assertEquals('/',\OC\Files\Filesystem::getMountPoint('/')); + $this->assertEquals('/',\OC\Files\Filesystem::getMountPoint('/some/folder')); + list( , $internalPath)=\OC\Files\Filesystem::resolvePath('/'); + $this->assertEquals('',$internalPath); + list( , $internalPath)=\OC\Files\Filesystem::resolvePath('/some/folder'); + $this->assertEquals('some/folder',$internalPath); + + \OC\Files\Filesystem::mount('\OC\Files\Storage\Local',self::getStorageData(),'/some'); + $this->assertEquals('/',\OC\Files\Filesystem::getMountPoint('/')); + $this->assertEquals('/some/',\OC\Files\Filesystem::getMountPoint('/some/folder')); + $this->assertEquals('/some/',\OC\Files\Filesystem::getMountPoint('/some/')); + $this->assertEquals('/',\OC\Files\Filesystem::getMountPoint('/some')); + list( , $internalPath)=\OC\Files\Filesystem::resolvePath('/some/folder'); + $this->assertEquals('folder',$internalPath); + } + + public function testNormalize() { + $this->assertEquals('/path', \OC\Files\Filesystem::normalizePath('/path/')); + $this->assertEquals('/path/', \OC\Files\Filesystem::normalizePath('/path/', false)); + $this->assertEquals('/path', \OC\Files\Filesystem::normalizePath('path')); + $this->assertEquals('/path', \OC\Files\Filesystem::normalizePath('\path')); + $this->assertEquals('/foo/bar', \OC\Files\Filesystem::normalizePath('/foo//bar/')); + $this->assertEquals('/foo/bar', \OC\Files\Filesystem::normalizePath('/foo////bar')); + if (class_exists('Normalizer')) { + $this->assertEquals("/foo/bar\xC3\xBC", \OC\Files\Filesystem::normalizePath("/foo/baru\xCC\x88")); + } + } + + public function testHooks() { + if(\OC\Files\Filesystem::getView()){ + $user = \OC_User::getUser(); + }else{ + $user=uniqid(); + \OC\Files\Filesystem::init('/'.$user.'/files'); + } + \OC_Hook::clear('OC_Filesystem'); + \OC_Hook::connect('OC_Filesystem', 'post_write', $this, 'dummyHook'); + + \OC\Files\Filesystem::mount('OC\Files\Storage\Temporary', array(), '/'); + + $rootView=new \OC\Files\View(''); + $rootView->mkdir('/'.$user); + $rootView->mkdir('/'.$user.'/files'); + + \OC\Files\Filesystem::file_put_contents('/foo', 'foo'); + \OC\Files\Filesystem::mkdir('/bar'); + \OC\Files\Filesystem::file_put_contents('/bar//foo', 'foo'); + + $tmpFile = \OC_Helper::tmpFile(); + file_put_contents($tmpFile, 'foo'); + $fh = fopen($tmpFile, 'r'); + \OC\Files\Filesystem::file_put_contents('/bar//foo', $fh); + } + + public function dummyHook($arguments) { + $path = $arguments['path']; + $this->assertEquals($path, \OC\Files\Filesystem::normalizePath($path)); //the path passed to the hook should already be normalized + } +} diff --git a/tests/lib/files/view.php b/tests/lib/files/view.php index fc872ea5e2..980c5298fb 100644 --- a/tests/lib/files/view.php +++ b/tests/lib/files/view.php @@ -7,8 +7,6 @@ namespace Test\Files; -use \OC\Files\Filesystem as Filesystem; - class View extends \PHPUnit_Framework_TestCase { /** * @var \OC\Files\Storage\Storage[] $storages; @@ -16,7 +14,7 @@ class View extends \PHPUnit_Framework_TestCase { private $storages = array(); public function setUp() { - Filesystem::clearMounts(); + \OC\Files\Filesystem::clearMounts(); } public function tearDown() { @@ -30,9 +28,9 @@ class View extends \PHPUnit_Framework_TestCase { $storage1 = $this->getTestStorage(); $storage2 = $this->getTestStorage(); $storage3 = $this->getTestStorage(); - Filesystem::mount($storage1, array(), '/'); - Filesystem::mount($storage2, array(), '/substorage'); - Filesystem::mount($storage3, array(), '/folder/anotherstorage'); + \OC\Files\Filesystem::mount($storage1, array(), '/'); + \OC\Files\Filesystem::mount($storage2, array(), '/substorage'); + \OC\Files\Filesystem::mount($storage3, array(), '/folder/anotherstorage'); $textSize = strlen("dummy file data\n"); $imageSize = filesize(\OC::$SERVERROOT . '/core/img/logo.png'); $storageSize = $textSize * 2 + $imageSize; @@ -84,8 +82,8 @@ class View extends \PHPUnit_Framework_TestCase { public function testAutoScan() { $storage1 = $this->getTestStorage(false); $storage2 = $this->getTestStorage(false); - Filesystem::mount($storage1, array(), '/'); - Filesystem::mount($storage2, array(), '/substorage'); + \OC\Files\Filesystem::mount($storage1, array(), '/'); + \OC\Files\Filesystem::mount($storage2, array(), '/substorage'); $textSize = strlen("dummy file data\n"); $rootView = new \OC\Files\View(''); @@ -103,9 +101,9 @@ class View extends \PHPUnit_Framework_TestCase { $storage1 = $this->getTestStorage(); $storage2 = $this->getTestStorage(); $storage3 = $this->getTestStorage(); - Filesystem::mount($storage1, array(), '/'); - Filesystem::mount($storage2, array(), '/substorage'); - Filesystem::mount($storage3, array(), '/folder/anotherstorage'); + \OC\Files\Filesystem::mount($storage1, array(), '/'); + \OC\Files\Filesystem::mount($storage2, array(), '/substorage'); + \OC\Files\Filesystem::mount($storage3, array(), '/folder/anotherstorage'); $rootView = new \OC\Files\View(''); @@ -113,7 +111,7 @@ class View extends \PHPUnit_Framework_TestCase { $this->assertEquals(6, count($results)); $paths = array(); foreach ($results as $result) { - $this->assertEquals($result['path'], Filesystem::normalizePath($result['path'])); + $this->assertEquals($result['path'], \OC\Files\Filesystem::normalizePath($result['path'])); $paths[] = $result['path']; } $this->assertContains('/foo.txt', $paths); diff --git a/tests/lib/filesystem.php b/tests/lib/filesystem.php deleted file mode 100644 index 6e7b4fb781..0000000000 --- a/tests/lib/filesystem.php +++ /dev/null @@ -1,110 +0,0 @@ -. - * - */ - -use \OC\Files\Filesystem as Filesystem; - -class Test_Filesystem extends UnitTestCase { - /** - * @var array tmpDirs - */ - private $tmpDirs=array(); - - /** - * @return array - */ - private function getStorageData() { - $dir = OC_Helper::tmpFolder(); - $this->tmpDirs[] = $dir; - return array('datadir' => $dir); - } - - public function tearDown() { - foreach ($this->tmpDirs as $dir) { - OC_Helper::rmdirr($dir); - } - } - - public function setUp() { - Filesystem::clearMounts(); - } - - public function testMount() { - Filesystem::mount('\OC\Files\Storage\Local',self::getStorageData(),'/'); - $this->assertEqual('/',Filesystem::getMountPoint('/')); - $this->assertEqual('/',Filesystem::getMountPoint('/some/folder')); - list( , $internalPath)=\OC\Files\Filesystem::resolvePath('/'); - $this->assertEqual('',$internalPath); - list( , $internalPath)=\OC\Files\Filesystem::resolvePath('/some/folder'); - $this->assertEqual('some/folder',$internalPath); - - Filesystem::mount('\OC\Files\Storage\Local',self::getStorageData(),'/some'); - $this->assertEqual('/',Filesystem::getMountPoint('/')); - $this->assertEqual('/some/',Filesystem::getMountPoint('/some/folder')); - $this->assertEqual('/some/',Filesystem::getMountPoint('/some/')); - $this->assertEqual('/',Filesystem::getMountPoint('/some')); - list( , $internalPath)=\OC\Files\Filesystem::resolvePath('/some/folder'); - $this->assertEqual('folder',$internalPath); - } - - public function testNormalize() { - $this->assertEqual('/path', Filesystem::normalizePath('/path/')); - $this->assertEqual('/path/', Filesystem::normalizePath('/path/', false)); - $this->assertEqual('/path', Filesystem::normalizePath('path')); - $this->assertEqual('/path', Filesystem::normalizePath('\path')); - $this->assertEqual('/foo/bar', Filesystem::normalizePath('/foo//bar/')); - $this->assertEqual('/foo/bar', Filesystem::normalizePath('/foo////bar')); - if (class_exists('Normalizer')) { - $this->assertEqual("/foo/bar\xC3\xBC", Filesystem::normalizePath("/foo/baru\xCC\x88")); - } - } - - public function testHooks() { - if(Filesystem::getView()){ - $user = OC_User::getUser(); - }else{ - $user=uniqid(); - Filesystem::init('/'.$user.'/files'); - } - OC_Hook::clear('OC_Filesystem'); - OC_Hook::connect('OC_Filesystem', 'post_write', $this, 'dummyHook'); - - Filesystem::mount('OC\Files\Storage\Temporary', array(), '/'); - - $rootView=new \OC\Files\View(''); - $rootView->mkdir('/'.$user); - $rootView->mkdir('/'.$user.'/files'); - - Filesystem::file_put_contents('/foo', 'foo'); - Filesystem::mkdir('/bar'); - Filesystem::file_put_contents('/bar//foo', 'foo'); - - $tmpFile = OC_Helper::tmpFile(); - file_put_contents($tmpFile, 'foo'); - $fh = fopen($tmpFile, 'r'); - Filesystem::file_put_contents('/bar//foo', $fh); - } - - public function dummyHook($arguments) { - $path = $arguments['path']; - $this->assertEqual($path, Filesystem::normalizePath($path)); //the path passed to the hook should already be normalized - } -} From c22a723785f80671548b89c543e9163c2fff9264 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 26 Oct 2012 19:07:29 +0200 Subject: [PATCH 053/272] add file permissions cache --- db_structure.xml | 47 +++++++++++++++ lib/files/cache/permissions.php | 85 +++++++++++++++++++++++++++ lib/util.php | 2 +- tests/lib/files/cache/permissions.php | 47 +++++++++++++++ 4 files changed, 180 insertions(+), 1 deletion(-) create mode 100644 lib/files/cache/permissions.php create mode 100644 tests/lib/files/cache/permissions.php diff --git a/db_structure.xml b/db_structure.xml index e0b9dc11e9..e420a9f0e4 100644 --- a/db_structure.xml +++ b/db_structure.xml @@ -175,6 +175,53 @@
+ + + *dbprefix*permissions + + + + + fileid + integer + 0 + true + 4 + + + + user + text + + true + 64 + + + + permissions + integer + 0 + true + 4 + + + + id_user_index + true + + fileid + ascending + + + user + ascending + + + + + +
+ *dbprefix*group_user diff --git a/lib/files/cache/permissions.php b/lib/files/cache/permissions.php new file mode 100644 index 0000000000..e3fa63c464 --- /dev/null +++ b/lib/files/cache/permissions.php @@ -0,0 +1,85 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Files\Cache; + +class Permissions { + /** + * get the permissions for a single file + * + * @param int $fileId + * @param string $user + * @return int (-1 if file no permissions set) + */ + static public function get($fileId, $user) { + $query = \OC_DB::prepare('SELECT `permissions` FROM `*PREFIX*permissions` WHERE `user` = ? AND `fileid` = ?'); + $result = $query->execute(array($user, $fileId)); + if ($row = $result->fetchRow()) { + return $row['permissions']; + } else { + return -1; + } + } + + /** + * set the permissions of a file + * + * @param int $fileId + * @param string $user + * @param int $permissions + */ + static public function set($fileId, $user, $permissions) { + if (self::get($fileId, $user) !== -1) { + $query = \OC_DB::prepare('UPDATE `*PREFIX*permissions` SET `permissions` = ? WHERE `user` = ? AND `fileid` = ?'); + } else { + $query = \OC_DB::prepare('INSERT INTO `*PREFIX*permissions`(`permissions`, `user`, `fileid`) VALUES(?, ?,? )'); + } + $query->execute(array($permissions, $user, $fileId)); + } + + /** + * get the permissions of multiply files + * + * @param int[] $fileIds + * @param string $user + * @return int[] + */ + static public function getMultiple($fileIds, $user) { + $params = $fileIds; + $params[] = $user; + $inPart = implode(', ', array_fill(0, count($fileIds), '?')); + + $query = \OC_DB::prepare('SELECT `fileid`, `permissions` FROM `*PREFIX*permissions` WHERE `fileid` IN (' . $inPart . ') AND `user` = ?'); + $result = $query->execute($params); + $filePermissions = array(); + while ($row = $result->fetchRow()) { + $filePermissions[$row['fileid']] = $row['permissions']; + } + return $filePermissions; + } + + /** + * remove the permissions for a file + * + * @param int $fileId + * @param string $user + */ + static public function remove($fileId, $user) { + $query = \OC_DB::prepare('DELETE FROM `*PREFIX*permissions` WHERE `fileid` = ? AND `user` = ?'); + $query->execute(array($fileId, $user)); + } + + static public function removeMultiple($fileIds, $user) { + $params = $fileIds; + $params[] = $user; + $inPart = implode(', ', array_fill(0, count($fileIds), '?')); + + $query = \OC_DB::prepare('DELETE FROM `*PREFIX*permissions` WHERE `fileid` IN (' . $inPart . ') AND `user` = ?'); + $query->execute($params); + } +} diff --git a/lib/util.php b/lib/util.php index 137766d012..a2e0422c97 100755 --- a/lib/util.php +++ b/lib/util.php @@ -86,7 +86,7 @@ class OC_Util { */ public static function getVersion() { // hint: We only can count up. So the internal version number of ownCloud 4.5 will be 4.90.0. This is not visible to the user - return array(4,91,00); + return array(4,91,01); } /** diff --git a/tests/lib/files/cache/permissions.php b/tests/lib/files/cache/permissions.php new file mode 100644 index 0000000000..4d47929a3e --- /dev/null +++ b/tests/lib/files/cache/permissions.php @@ -0,0 +1,47 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace Test\Files\Cache; + +class Permissions extends \PHPUnit_Framework_TestCase { + function testSimple() { + $ids = range(1, 10); + $user = uniqid(); + + $this->assertEquals(-1, \OC\Files\Cache\Permissions::get(1, $user)); + \OC\Files\Cache\Permissions::set(1, $user, 1); + $this->assertEquals(1, \OC\Files\Cache\Permissions::get(1, $user)); + $this->assertEquals(-1, \OC\Files\Cache\Permissions::get(2, $user)); + $this->assertEquals(-1, \OC\Files\Cache\Permissions::get(1, $user . '2')); + + \OC\Files\Cache\Permissions::set(1, $user, 2); + $this->assertEquals(2, \OC\Files\Cache\Permissions::get(1, $user)); + + \OC\Files\Cache\Permissions::set(2, $user, 1); + $this->assertEquals(1, \OC\Files\Cache\Permissions::get(2, $user)); + + \OC\Files\Cache\Permissions::remove(1, $user); + $this->assertEquals(-1, \OC\Files\Cache\Permissions::get(1, $user)); + \OC\Files\Cache\Permissions::remove(1, $user . '2'); + $this->assertEquals(1, \OC\Files\Cache\Permissions::get(2, $user)); + + $expected = array(); + foreach ($ids as $id) { + \OC\Files\Cache\Permissions::set($id, $user, 10 + $id); + $expected[$id] = 10 + $id; + } + $this->assertEquals($expected, \OC\Files\Cache\Permissions::getMultiple($ids, $user)); + + \OC\Files\Cache\Permissions::removeMultiple(array(10, 9), $user); + unset($expected[9]); + unset($expected[10]); + $this->assertEquals($expected, \OC\Files\Cache\Permissions::getMultiple($ids, $user)); + + \OC\Files\Cache\Permissions::removeMultiple($ids, $user); + } +} From 6db81afab9d68ccd50c1e164622252e47ae76c2f Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 26 Oct 2012 23:05:02 +0200 Subject: [PATCH 054/272] move some stuff to the new api --- apps/files/ajax/list.php | 2 +- apps/files/ajax/newfile.php | 12 ++++++------ apps/files/ajax/newfolder.php | 3 ++- apps/files/ajax/rawlist.php | 2 +- apps/files/ajax/upload.php | 2 +- apps/files/index.php | 2 +- apps/files/settings.php | 2 +- apps/files_encryption/lib/cryptstream.php | 2 +- apps/files_encryption/lib/proxy.php | 10 +++++----- apps/files_sharing/appinfo/update.php | 3 ++- lib/connector/sabre/directory.php | 6 +++--- lib/connector/sabre/node.php | 4 ++-- lib/fileproxy/quota.php | 12 ++++-------- lib/files.php | 2 +- lib/files/filesystem.php | 13 +++++++++++++ lib/files/view.php | 1 - lib/ocs.php | 4 ++-- lib/public/share.php | 15 +++++++++------ lib/search/provider/file.php | 2 +- settings/personal.php | 4 ++-- 20 files changed, 58 insertions(+), 45 deletions(-) diff --git a/apps/files/ajax/list.php b/apps/files/ajax/list.php index 568fe754c0..92091f4213 100644 --- a/apps/files/ajax/list.php +++ b/apps/files/ajax/list.php @@ -32,7 +32,7 @@ if($doBreadcrumb) { // make filelist $files = array(); -foreach( OC_Files::getdirectorycontent( $dir ) as $i ) { +foreach( \OC\Files\Filesystem::getDirectoryContent( $dir ) as $i ) { $i["date"] = OCP\Util::formatDate($i["mtime"] ); $files[] = $i; } diff --git a/apps/files/ajax/newfile.php b/apps/files/ajax/newfile.php index 5f3f3d88f2..38714f34a6 100644 --- a/apps/files/ajax/newfile.php +++ b/apps/files/ajax/newfile.php @@ -65,9 +65,9 @@ if($source) { $target=$dir.'/'.$filename; $result=\OC\Files\Filesystem::file_put_contents($target, $sourceStream); if($result) { - $meta = OC_FileCache::get($target); + $meta = \OC\Files\Filesystem::getFileInfo($target); $mime=$meta['mimetype']; - $id = OC_FileCache::getId($target); + $id = $meta['fileid']; $eventSource->send('success', array('mime'=>$mime, 'size'=>\OC\Files\Filesystem::filesize($target), 'id' => $id)); } else { $eventSource->send('error', "Error while downloading ".$source. ' to '.$target); @@ -77,14 +77,14 @@ if($source) { } else { if($content) { if(\OC\Files\Filesystem::file_put_contents($dir.'/'.$filename, $content)) { - $meta = OC_FileCache::get($dir.'/'.$filename); - $id = OC_FileCache::getId($dir.'/'.$filename); + $meta = \OC\Files\Filesystem::getFileInfo($dir.'/'.$filename); + $id = $meta['fileid']; OCP\JSON::success(array("data" => array('content'=>$content, 'id' => $id))); exit(); } }elseif(\OC\Files\Filesystem::touch($dir . '/' . $filename)) { - $meta = OC_FileCache::get($dir.'/'.$filename); - $id = OC_FileCache::getId($dir.'/'.$filename); + $meta = \OC\Files\Filesystem::getFileInfo($dir.'/'.$filename); + $id = $meta['fileid']; OCP\JSON::success(array("data" => array('content'=>$content, 'id' => $id))); exit(); } diff --git a/apps/files/ajax/newfolder.php b/apps/files/ajax/newfolder.php index c36c208455..e26e1238bc 100644 --- a/apps/files/ajax/newfolder.php +++ b/apps/files/ajax/newfolder.php @@ -25,7 +25,8 @@ if(\OC\Files\Filesystem::mkdir($dir . '/' . stripslashes($foldername))) { } else { $path = '/'.$foldername; } - $id = OC_FileCache::getId($path); + $meta = \OC\Files\Filesystem::getFileInfo($path); + $id = $meta['fileid']; OCP\JSON::success(array("data" => array('id'=>$id))); exit(); } diff --git a/apps/files/ajax/rawlist.php b/apps/files/ajax/rawlist.php index e0aa0bdac5..1cd2944483 100644 --- a/apps/files/ajax/rawlist.php +++ b/apps/files/ajax/rawlist.php @@ -15,7 +15,7 @@ $mimetype = isset($_GET['mimetype']) ? $_GET['mimetype'] : ''; // make filelist $files = array(); -foreach( OC_Files::getdirectorycontent( $dir, $mimetype ) as $i ) { +foreach( \OC\Files\Filesystem::getDirectoryContent( $dir, $mimetype ) as $i ) { $i["date"] = OCP\Util::formatDate($i["mtime"] ); $i['mimetype_icon'] = $i['type'] == 'dir' ? \mimetype_icon('dir'): \mimetype_icon($i['mimetype']); $files[] = $i; diff --git a/apps/files/ajax/upload.php b/apps/files/ajax/upload.php index b6b8c7f7b4..2a784d6169 100644 --- a/apps/files/ajax/upload.php +++ b/apps/files/ajax/upload.php @@ -49,7 +49,7 @@ if(strpos($dir, '..') === false) { for($i=0;$i<$fileCount;$i++) { $target = OCP\Files::buildNotExistingFileName(stripslashes($dir), $files['name'][$i]); if(is_uploaded_file($files['tmp_name'][$i]) and \OC\Files\Filesystem::fromTmpFile($files['tmp_name'][$i], $target)) { - $meta = \OC_Files::getFileInfo($target); + $meta = \OC\Files\Filesystem::getFileInfo($target); $id = $meta['fileid']; $result[]=array( "status" => "success", 'mime'=>$meta['mimetype'],'size'=>$meta['size'], 'id'=>$id, 'name'=>basename($target)); } diff --git a/apps/files/index.php b/apps/files/index.php index 192cd2696f..4676ebc602 100644 --- a/apps/files/index.php +++ b/apps/files/index.php @@ -44,7 +44,7 @@ if(!\OC\Files\Filesystem::is_dir($dir.'/')) { } $files = array(); -foreach( OC_Files::getdirectorycontent( $dir ) as $i ) { +foreach( \OC\Files\Filesystem::getDirectoryContent( $dir ) as $i ) { $i['date'] = OCP\Util::formatDate($i['mtime'] ); if($i['type']=='file') { $fileinfo=pathinfo($i['name']); diff --git a/apps/files/settings.php b/apps/files/settings.php index 52ec9fd0fe..30463210f7 100644 --- a/apps/files/settings.php +++ b/apps/files/settings.php @@ -36,7 +36,7 @@ OCP\Util::addscript( "files", "files" ); $dir = isset( $_GET['dir'] ) ? $_GET['dir'] : ''; $files = array(); -foreach( OC_Files::getdirectorycontent( $dir ) as $i ) { +foreach( \OC\Files\Filesystem::getDirectoryContent( $dir ) as $i ) { $i["date"] = date( $CONFIG_DATEFORMAT, $i["mtime"] ); $files[] = $i; } diff --git a/apps/files_encryption/lib/cryptstream.php b/apps/files_encryption/lib/cryptstream.php index e98013e1f4..c429838264 100644 --- a/apps/files_encryption/lib/cryptstream.php +++ b/apps/files_encryption/lib/cryptstream.php @@ -167,7 +167,7 @@ class OC_CryptStream{ public function stream_close() { $this->flush(); if($this->meta['mode']!='r' and $this->meta['mode']!='rb') { - OC_FileCache::put($this->path, array('encrypted'=>true,'size'=>$this->size),''); + \OC\Files\Filesystem::putFileInfo($this->path, array('encrypted'=>true,'size'=>$this->size),''); } return fclose($this->source); } diff --git a/apps/files_encryption/lib/proxy.php b/apps/files_encryption/lib/proxy.php index 7b7bafe73e..49fb30ac29 100644 --- a/apps/files_encryption/lib/proxy.php +++ b/apps/files_encryption/lib/proxy.php @@ -59,7 +59,7 @@ class OC_FileProxy_Encryption extends OC_FileProxy{ * @return bool */ private static function isEncrypted($path) { - $metadata=OC_FileCache_Cached::get($path,''); + $metadata=\OC\Files\Filesystem::getFileInfo($path,''); return isset($metadata['encrypted']) and (bool)$metadata['encrypted']; } @@ -68,14 +68,14 @@ class OC_FileProxy_Encryption extends OC_FileProxy{ if (!is_resource($data)) {//stream put contents should have been converter to fopen $size=strlen($data); $data=OC_Crypt::blockEncrypt($data); - OC_FileCache::put($path, array('encrypted'=>true,'size'=>$size),''); + \OC\Files\Filesystem::putFileInfo($path, array('encrypted'=>true,'size'=>$size),''); } } } public function postFile_get_contents($path,$data) { if(self::isEncrypted($path)) { - $cached=OC_FileCache_Cached::get($path,''); + $cached=\OC\Files\Filesystem::getFileInfo($path,''); $data=OC_Crypt::blockDecrypt($data,'',$cached['size']); } return $data; @@ -113,7 +113,7 @@ class OC_FileProxy_Encryption extends OC_FileProxy{ public function postStat($path,$data) { if(self::isEncrypted($path)) { - $cached=OC_FileCache_Cached::get($path,''); + $cached=\OC\Files\Filesystem::getFileInfo($path,''); $data['size']=$cached['size']; } return $data; @@ -121,7 +121,7 @@ class OC_FileProxy_Encryption extends OC_FileProxy{ public function postFileSize($path,$size) { if(self::isEncrypted($path)) { - $cached=OC_FileCache_Cached::get($path,''); + $cached=\OC\Files\Filesystem::getFileInfo($path,''); return $cached['size']; }else{ return $size; diff --git a/apps/files_sharing/appinfo/update.php b/apps/files_sharing/appinfo/update.php index e75c538b15..51048bd178 100644 --- a/apps/files_sharing/appinfo/update.php +++ b/apps/files_sharing/appinfo/update.php @@ -10,6 +10,7 @@ if (version_compare($installedVersion, '0.3', '<')) { OC_Group::useBackend(new OC_Group_Database()); OC_App::loadApps(array('authentication')); while ($row = $result->fetchRow()) { + $meta = \OC\Files\Filesystem::getId($path, ''); $itemSource = OC_FileCache::getId($row['source'], ''); if ($itemSource != -1) { $file = OC_FileCache::get($row['source'], ''); @@ -70,4 +71,4 @@ if (version_compare($installedVersion, '0.3.3', '<')) { foreach ($users as $user) { OC_FileCache::delete('Shared', '/'.$user.'/files/'); } -} \ No newline at end of file +} diff --git a/lib/connector/sabre/directory.php b/lib/connector/sabre/directory.php index 3df85b2bbb..d4f58527d2 100644 --- a/lib/connector/sabre/directory.php +++ b/lib/connector/sabre/directory.php @@ -93,7 +93,7 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa $path = $this->path . '/' . $name; if (is_null($info)) { - $info = OC_Files::getFileInfo($path); + $info = \OC\Files\Filesystem::getFileInfo($path); } if (!$info) { @@ -117,7 +117,7 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa */ public function getChildren() { - $folder_content = OC_Files::getDirectoryContent($this->path); + $folder_content = \OC\Files\Filesystem::getDirectoryContent($this->path); $paths = array(); foreach($folder_content as $info) { $paths[] = $this->path.'/'.$info['name']; @@ -178,7 +178,7 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa * @return array */ public function getQuotaInfo() { - $rootInfo=OC_FileCache_Cached::get(''); + $rootInfo=\OC\Files\Filesystem::getFileInfo(''); return array( $rootInfo['size'], \OC\Files\Filesystem::free_space() diff --git a/lib/connector/sabre/node.php b/lib/connector/sabre/node.php index e7e83507ea..2095c956e5 100644 --- a/lib/connector/sabre/node.php +++ b/lib/connector/sabre/node.php @@ -95,11 +95,11 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr } /** - * Make sure the fileinfo cache is filled. Uses OC_FileCache or a direct stat + * Make sure the fileinfo cache is filled. Uses the file cache or a direct stat */ protected function getFileinfoCache() { if (!isset($this->fileinfo_cache)) { - if ($fileinfo_cache = \OC\Files\Filesystem::get($this->path)) { + if ($fileinfo_cache = \OC\Files\Filesystem::getFileInfo($this->path)) { } else { $fileinfo_cache = \OC\Files\Filesystem::stat($this->path); } diff --git a/lib/fileproxy/quota.php b/lib/fileproxy/quota.php index d120f30999..cd9a2f4a19 100644 --- a/lib/fileproxy/quota.php +++ b/lib/fileproxy/quota.php @@ -57,7 +57,7 @@ class OC_FileProxy_Quota extends OC_FileProxy{ * @return int */ private function getFreeSpace($path) { - $storage=OC_Filesystem::getStorage($path); + list($storage,)=\OC\Files\Filesystem::resolvePath($path); $owner=$storage->getOwner($path); $totalSpace=$this->getQuota($owner); @@ -65,13 +65,9 @@ class OC_FileProxy_Quota extends OC_FileProxy{ return 0; } - $rootInfo=OC_FileCache::get('', "/".$owner."/files"); - // TODO Remove after merge of share_api - if (OC_FileCache::inCache('/Shared', "/".$owner."/files")) { - $sharedInfo=OC_FileCache::get('/Shared', "/".$owner."/files"); - } else { - $sharedInfo = null; - } + $view = new \OC\Files\View("/".$owner."/files"); + + $rootInfo=$view->getFileInfo('/'); $usedSpace=isset($rootInfo['size'])?$rootInfo['size']:0; $usedSpace=isset($sharedInfo['size'])?$usedSpace-$sharedInfo['size']:$usedSpace; return $totalSpace-$usedSpace; diff --git a/lib/files.php b/lib/files.php index 422e7f4ffe..6a063f216d 100644 --- a/lib/files.php +++ b/lib/files.php @@ -146,7 +146,7 @@ class OC_Files { $dirname = basename($dir); $zip->addEmptyDir($internalDir . $dirname); $internalDir .= $dirname .= '/'; - $files = OC_Files::getdirectorycontent($dir); + $files = \OC\Files\Filesystem::getDirectoryContent($dir); foreach ($files as $file) { $filename = $file['name']; $file = $dir . '/' . $filename; diff --git a/lib/files/filesystem.php b/lib/files/filesystem.php index 4031c0c5b8..b3c92f3855 100644 --- a/lib/files/filesystem.php +++ b/lib/files/filesystem.php @@ -627,6 +627,19 @@ class Filesystem { return self::$defaultInstance->getFileInfo($path); } + /** + * change file metadata + * + * @param string $path + * @param array $data + * @return int + * + * returns the fileid of the updated file + */ + public static function putFileInfo($path, $data) { + return self::$defaultInstance->putFileInfo($path, $data); + } + /** * get the content of a directory * diff --git a/lib/files/view.php b/lib/files/view.php index ee95cce0c1..e9b583f8ae 100644 --- a/lib/files/view.php +++ b/lib/files/view.php @@ -451,7 +451,6 @@ class View { array(Filesystem::signal_param_path => $path2) ); } else { // no real copy, file comes from somewhere else, e.g. version rollback -> just update the file cache and the webdav properties without all the other post_write actions -// OC_FileCache_Update::update($path2, $this->fakeRoot); Filesystem::removeETagHook(array("path" => $path2), $this->fakeRoot); } return $result; diff --git a/lib/ocs.php b/lib/ocs.php index 645380ddba..c6bcd2c06e 100644 --- a/lib/ocs.php +++ b/lib/ocs.php @@ -590,8 +590,8 @@ class OC_OCS { // calculate the disc space $user_dir = '/'.$user.'/files'; \OC\Files\Filesystem::init($user_dir); - $rootInfo=OC_FileCache::get(''); - $sharedInfo=OC_FileCache::get('/Shared'); + $rootInfo=\OC\Files\Filesystem::getFileInfo(''); + $sharedInfo=\OC\Files\Filesystem::getFileInfo('/Shared'); $used=$rootInfo['size']-$sharedInfo['size']; $free=\OC\Files\Filesystem::free_space(); $total=$free+$used; diff --git a/lib/public/share.php b/lib/public/share.php index 7a9a087d1b..dd6c841c99 100644 --- a/lib/public/share.php +++ b/lib/public/share.php @@ -285,10 +285,10 @@ class Share { // If the item is a folder, scan through the folder looking for equivalent item types if ($itemType == 'folder') { $parentFolder = self::put('folder', $itemSource, $shareType, $shareWith, $uidOwner, $permissions, true); - if ($parentFolder && $files = \OC_Files::getDirectoryContent($itemSource)) { + if ($parentFolder && $files = \OC\Files\Filesystem::getDirectoryContent($itemSource)) { for ($i = 0; $i < count($files); $i++) { $name = substr($files[$i]['name'], strpos($files[$i]['name'], $itemSource) - strlen($itemSource)); - if ($files[$i]['mimetype'] == 'httpd/unix-directory' && $children = \OC_Files::getDirectoryContent($name, '/')) { + if ($files[$i]['mimetype'] == 'httpd/unix-directory' && $children = \OC\Files\Filesystem::getDirectoryContent($name, '/')) { // Continue scanning into child folders array_push($files, $children); } else { @@ -768,9 +768,10 @@ class Share { if ($row['item_type'] == 'file' || $row['item_type'] == 'folder') { $childItem['file_source'] = $child['source']; } else { - $childItem['file_source'] = \OC_FileCache::getId($child['file_path']); + $meta = \OC\Files\Filesystem::getFileInfo($child['file_path']); + $childItem['file_source'] = $meta['fileid']; } - $childItem['file_target'] = \OC_Filesystem::normalizePath($child['file_path']); + $childItem['file_target'] = \OC\Files\Filesystem::normalizePath($child['file_path']); } if (isset($item)) { if ($childItem[$column] == $item) { @@ -881,7 +882,8 @@ class Share { if ($itemType == 'file' || $itemType == 'folder') { $fileSource = $itemSource; } else { - $fileSource = \OC_FileCache::getId($filePath); + $meta = \OC\Files\Filesystem::getFileInfo($filePath); + $fileSource = $meta['fileid']; } if ($fileSource == -1) { $message = 'Sharing '.$itemSource.' failed, because the file could not be found in the file cache'; @@ -1063,7 +1065,8 @@ class Share { } if ($item['uid_owner'] == $uidOwner) { if ($itemType == 'file' || $itemType == 'folder') { - if ($item['file_source'] == \OC_FileCache::getId($itemSource)) { + $meta = \OC\Files\Filesystem::getFileInfo($itemSource); + if ($item['file_source'] == $meta['fileid']) { return $target; } } else if ($item['item_source'] == $itemSource) { diff --git a/lib/search/provider/file.php b/lib/search/provider/file.php index 0d4b332b79..456e12b5ec 100644 --- a/lib/search/provider/file.php +++ b/lib/search/provider/file.php @@ -2,7 +2,7 @@ class OC_Search_Provider_File extends OC_Search_Provider{ function search($query) { - $files=OC_FileCache::search($query, true); + $files=\OC\Files\Filesystem::search($query, true); $results=array(); $l=OC_L10N::get('lib'); foreach($files as $fileData) { diff --git a/settings/personal.php b/settings/personal.php index c73a3dd370..5a267ec284 100644 --- a/settings/personal.php +++ b/settings/personal.php @@ -16,8 +16,8 @@ OC_Util::addStyle( '3rdparty', 'chosen' ); OC_App::setActiveNavigationEntry( 'personal' ); // calculate the disc space -$rootInfo=OC_FileCache::get(''); -$sharedInfo=OC_FileCache::get('/Shared'); +$rootInfo=\OC\Files\Filesystem::getFileInfo(''); +$sharedInfo=\OC\Files\Filesystem::getFileInfo('/Shared'); $used=$rootInfo['size']; if($used<0) $used=0; $free=\OC\Files\Filesystem::free_space(); From 7d6da68d532fe1b1299f3a724e70b8ec3960ec8d Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 26 Oct 2012 23:25:52 +0200 Subject: [PATCH 055/272] prove some compatibility with the old cache api --- lib/files/view.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/files/view.php b/lib/files/view.php index e9b583f8ae..04b7dca8af 100644 --- a/lib/files/view.php +++ b/lib/files/view.php @@ -712,7 +712,7 @@ class View { $files = $cache->getFolderContents($internalPath); //TODO: mimetype_filter //add a folder for any mountpoint in this directory and add the sizes of other mountpoints to the folders - $mountPoints = Filesystem::getMountPoints($directory); + $mountPoints = Filesystem::getMountPoints($path); $dirLength = strlen($path); foreach ($mountPoints as $mountPoint) { $subStorage = Filesystem::getStorage($mountPoint); @@ -733,6 +733,10 @@ class View { } } + foreach($files as $i => $file){ + $files[$i]['type'] = $file['mimetype'] === 'httpd/unix-directory' ? 'dir' : 'file'; + } + usort($files, "fileCmp"); //TODO: remove this once ajax is merged return $files; } From 99534271977ef5a4fd35ce119d6627bbcde45096 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 26 Oct 2012 23:26:12 +0200 Subject: [PATCH 056/272] also use new cache api here --- lib/fileproxy/fileoperations.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/fileproxy/fileoperations.php b/lib/fileproxy/fileoperations.php index 23fb63fcfb..47ccd8f8c2 100644 --- a/lib/fileproxy/fileoperations.php +++ b/lib/fileproxy/fileoperations.php @@ -29,9 +29,9 @@ class OC_FileProxy_FileOperations extends OC_FileProxy{ public function premkdir($path) { if(!self::$rootView){ - self::$rootView = new OC_FilesystemView(''); + self::$rootView = new \OC\Files\View(''); } return !self::$rootView->file_exists($path); } -} \ No newline at end of file +} From 695405dfeb4da3733df1b1be239a97100f2ce66f Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sat, 27 Oct 2012 10:01:20 +0200 Subject: [PATCH 057/272] add permissions data to the results of the cache api --- lib/files/cache/cache.php | 15 +++++++++++++++ lib/files/cache/scanner.php | 3 ++- lib/files/view.php | 12 +++++++++++- tests/lib/files/cache/scanner.php | 2 ++ 4 files changed, 30 insertions(+), 2 deletions(-) diff --git a/lib/files/cache/cache.php b/lib/files/cache/cache.php index 7e0c5cb21f..5efc7d67c4 100644 --- a/lib/files/cache/cache.php +++ b/lib/files/cache/cache.php @@ -286,4 +286,19 @@ class Cache { } return $files; } + + /** + * get all file ids on the files on the storage + * + * @return int[] + */ + public function getAll() { + $query = \OC_DB::prepare('SELECT `fileid` FROM `*PREFIX*filecache` WHERE `storage` = ?'); + $result = $query->execute(array($this->storageId)); + $ids = array(); + while ($row = $result->fetchRow()) { + $ids[] = $row['fileid']; + } + return $ids; + } } diff --git a/lib/files/cache/scanner.php b/lib/files/cache/scanner.php index e8f54c34be..0adde1d354 100644 --- a/lib/files/cache/scanner.php +++ b/lib/files/cache/scanner.php @@ -66,7 +66,8 @@ class Scanner { $this->scanFile($parent); } } - $this->cache->put($file, $data); + $id = $this->cache->put($file, $data); + Permissions::set($id, \OC_User::getUser(), $data['permissions']); return $data; } diff --git a/lib/files/view.php b/lib/files/view.php index 04b7dca8af..82455d582e 100644 --- a/lib/files/view.php +++ b/lib/files/view.php @@ -686,6 +686,8 @@ class View { } } + $data['permissions'] = Cache\Permissions::get($data['fileid'], \OC_User::getUser()); + return $data; } @@ -733,8 +735,16 @@ class View { } } - foreach($files as $i => $file){ + $ids = array(); + + foreach ($files as $i => $file) { $files[$i]['type'] = $file['mimetype'] === 'httpd/unix-directory' ? 'dir' : 'file'; + $ids[] = $file['fileid']; + } + + $permissions = Cache\Permissions::getMultiple($ids, \OC_User::getUser()); + foreach ($files as $i => $file) { + $files[$i]['permissions'] = $permissions[$file['fileid']]; } usort($files, "fileCmp"); //TODO: remove this once ajax is merged diff --git a/tests/lib/files/cache/scanner.php b/tests/lib/files/cache/scanner.php index e3f47047f5..34fefc784d 100644 --- a/tests/lib/files/cache/scanner.php +++ b/tests/lib/files/cache/scanner.php @@ -106,6 +106,8 @@ class Scanner extends \UnitTestCase { } function tearDown() { + $ids = $this->cache->getAll(); + \OC\Files\Cache\Permissions::removeMultiple($ids, \OC_User::getUser()); $this->cache->clear(); } } From fcc92a9f43e75ecdb15adb0e948d45eac119306f Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sat, 27 Oct 2012 10:34:25 +0200 Subject: [PATCH 058/272] add searchByMime to new cache api --- lib/files/cache/cache.php | 21 +++++++++++++++++++++ lib/files/filesystem.php | 4 ++++ lib/files/view.php | 25 ++++++++++++++++++++++--- tests/lib/files/cache/cache.php | 3 +++ tests/lib/files/view.php | 3 +++ 5 files changed, 53 insertions(+), 3 deletions(-) diff --git a/lib/files/cache/cache.php b/lib/files/cache/cache.php index 5efc7d67c4..6604525477 100644 --- a/lib/files/cache/cache.php +++ b/lib/files/cache/cache.php @@ -287,6 +287,27 @@ class Cache { return $files; } + /** + * search for files by mimetype + * + * @param string $part1 + * @param string $part2 + * @return array + */ + public function searchByMime($mimetype) { + if (strpos($mimetype, '/')) { + $where = '`mimetype` = ?'; + } else { + $where = '`mimepart` = ?'; + } + $query = \OC_DB::prepare(' + SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted` + FROM `*PREFIX*filecache` WHERE ' . $where . ' AND `storage` = ?' + ); + $result = $query->execute(array($mimetype, $this->storageId)); + return $result->fetchAll(); + } + /** * get all file ids on the files on the storage * diff --git a/lib/files/filesystem.php b/lib/files/filesystem.php index b3c92f3855..96660a8ce3 100644 --- a/lib/files/filesystem.php +++ b/lib/files/filesystem.php @@ -551,6 +551,10 @@ class Filesystem { return self::$defaultInstance->search($query); } + static public function searchByMime($query) { + return self::$defaultInstance->searchByMime($query); + } + /** * check if a file or folder has been updated since $time * diff --git a/lib/files/view.php b/lib/files/view.php index 82455d582e..0a5a6436c2 100644 --- a/lib/files/view.php +++ b/lib/files/view.php @@ -747,7 +747,7 @@ class View { $files[$i]['permissions'] = $permissions[$file['fileid']]; } - usort($files, "fileCmp"); //TODO: remove this once ajax is merged + usort($files, "fileCmp"); return $files; } @@ -784,6 +784,25 @@ class View { * @return array */ public function search($query) { + return $this->searchCommon('%' . $query . '%', 'search'); + } + + /** + * search for files by mimetype + * + * @param string $query + * @return array + */ + public function searchByMime($mimetype) { + return $this->searchCommon($mimetype, 'searchByMime'); + } + + /** + * @param string $query + * @param string $method + * @return array + */ + private function searchCommon($query, $method) { $files = array(); $rootLength = strlen($this->fakeRoot); @@ -791,7 +810,7 @@ class View { $storage = Filesystem::getStorage($mountPoint); $cache = $storage->getCache(); - $results = $cache->search('%' . $query . '%'); + $results = $cache->$method($query); foreach ($results as $result) { if (substr($mountPoint . $result['path'], 0, $rootLength) === $this->fakeRoot) { $result['path'] = substr($mountPoint . $result['path'], $rootLength); @@ -805,7 +824,7 @@ class View { $cache = $storage->getCache(); $relativeMountPoint = substr($mountPoint, $rootLength); - $results = $cache->search('%' . $query . '%'); + $results = $cache->$method($query); foreach ($results as $result) { $result['path'] = $relativeMountPoint . $result['path']; $files[] = $result; diff --git a/tests/lib/files/cache/cache.php b/tests/lib/files/cache/cache.php index 839e06e93f..5a8d79b902 100644 --- a/tests/lib/files/cache/cache.php +++ b/tests/lib/files/cache/cache.php @@ -129,6 +129,9 @@ class Cache extends \UnitTestCase { $this->assertEquals(1, count($this->cache->search('%folder%'))); $this->assertEquals(1, count($this->cache->search('folder%'))); $this->assertEquals(3, count($this->cache->search('%'))); + + $this->assertEquals(3, count($this->cache->searchByMime('foo'))); + $this->assertEquals(2, count($this->cache->searchByMime('foo/file'))); } public function tearDown() { diff --git a/tests/lib/files/view.php b/tests/lib/files/view.php index 980c5298fb..5144fb1caa 100644 --- a/tests/lib/files/view.php +++ b/tests/lib/files/view.php @@ -139,6 +139,9 @@ class View extends \PHPUnit_Framework_TestCase { } $this->assertContains('/anotherstorage/foo.txt', $paths); $this->assertContains('/anotherstorage/foo.png', $paths); + + $this->assertEquals(6, count($rootView->searchByMime('text'))); + $this->assertEquals(3, count($folderView->searchByMime('text'))); } /** From 3437071cf1776288d9239a89b1a6ceb9c5ddbce1 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sat, 27 Oct 2012 10:40:27 +0200 Subject: [PATCH 059/272] remove old searchByMime --- lib/files.php | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/lib/files.php b/lib/files.php index 6a063f216d..28df225580 100644 --- a/lib/files.php +++ b/lib/files.php @@ -28,24 +28,6 @@ class OC_Files { static $tmpFiles = array(); - public static function searchByMime($mimetype_filter) { - $files = array(); - $dirs_to_check = array(''); - while (!empty($dirs_to_check)) { - // get next subdir to check - $dir = array_pop($dirs_to_check); - $dir_content = self::getDirectoryContent($dir, $mimetype_filter); - foreach ($dir_content as $file) { - if ($file['type'] == 'file') { - $files[] = $dir . '/' . $file['name']; - } else { - $dirs_to_check[] = $dir . '/' . $file['name']; - } - } - } - return $files; - } - /** * return the content of a file or return a zip file containning multiply files * From 5bfff6c56e8d4d911c7a5265bb5462c7122ca495 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sat, 27 Oct 2012 12:17:35 +0200 Subject: [PATCH 060/272] use absolute paths when resolving mount points --- lib/files.php | 10 ---------- lib/files/cache/permissions.php | 3 +++ lib/files/view.php | 35 ++++++++++++++++++++++++--------- lib/public/share.php | 4 ++-- 4 files changed, 31 insertions(+), 21 deletions(-) diff --git a/lib/files.php b/lib/files.php index 28df225580..e6b324f062 100644 --- a/lib/files.php +++ b/lib/files.php @@ -249,13 +249,3 @@ class OC_Files { return false; } } - -function fileCmp($a, $b) { - if ($a['type'] == 'dir' and $b['type'] != 'dir') { - return -1; - } elseif ($a['type'] != 'dir' and $b['type'] == 'dir') { - return 1; - } else { - return strnatcasecmp($a['name'], $b['name']); - } -} diff --git a/lib/files/cache/permissions.php b/lib/files/cache/permissions.php index e3fa63c464..dd7233abc7 100644 --- a/lib/files/cache/permissions.php +++ b/lib/files/cache/permissions.php @@ -50,6 +50,9 @@ class Permissions { * @return int[] */ static public function getMultiple($fileIds, $user) { + if (count($fileIds) === 0) { + return array(); + } $params = $fileIds; $params[] = $user; $inPart = implode(', ', array_fill(0, count($fileIds), '?')); diff --git a/lib/files/view.php b/lib/files/view.php index 0a5a6436c2..0f9a5feb5d 100644 --- a/lib/files/view.php +++ b/lib/files/view.php @@ -110,6 +110,7 @@ class View { */ public function getLocalFile($path) { $parent = substr($path, 0, strrpos($path, '/')); + $path = $this->getAbsolutePath($path); list($storage, $internalPath) = Filesystem::resolvePath($path); if (Filesystem::isValidPath($parent) and $storage) { return $storage->getLocalFile($internalPath); @@ -124,6 +125,7 @@ class View { */ public function getLocalFolder($path) { $parent = substr($path, 0, strrpos($path, '/')); + $path = $this->getAbsolutePath($path); list($storage, $internalPath) = Filesystem::resolvePath($path); if (Filesystem::isValidPath($parent) and $storage) { return $storage->getLocalFolder($internalPath); @@ -334,8 +336,8 @@ class View { $mp1 = $this->getMountPoint($path1 . $postFix1); $mp2 = $this->getMountPoint($path2 . $postFix2); if ($mp1 == $mp2) { - list($storage, $internalPath1) = Filesystem::resolvePath($path1 . $postFix1); - list(, $internalPath2) = Filesystem::resolvePath($path2 . $postFix2); + list($storage, $internalPath1) = Filesystem::resolvePath($absolutePath1 . $postFix1); + list(, $internalPath2) = Filesystem::resolvePath($absolutePath2 . $postFix2); if ($storage) { $result = $storage->rename($internalPath1, $internalPath2); } else { @@ -345,7 +347,7 @@ class View { $source = $this->fopen($path1 . $postFix1, 'r'); $target = $this->fopen($path2 . $postFix2, 'w'); $count = \OC_Helper::streamCopy($source, $target); - list($storage1, $internalPath1) = Filesystem::resolvePath($path1 . $postFix1); + list($storage1, $internalPath1) = Filesystem::resolvePath($absolutePath1 . $postFix1); $storage1->unlink($internalPath1); $result = $count > 0; } @@ -417,8 +419,8 @@ class View { $mp1 = $this->getMountPoint($path1 . $postFix1); $mp2 = $this->getMountPoint($path2 . $postFix2); if ($mp1 == $mp2) { - list($storage, $internalPath1) = Filesystem::resolvePath($path1 . $postFix1); - list(, $internalPath2) = Filesystem::resolvePath($path2 . $postFix2); + list($storage, $internalPath1) = Filesystem::resolvePath($absolutePath1 . $postFix1); + list(, $internalPath2) = Filesystem::resolvePath($absolutePath2 . $postFix2); if ($storage) { $result = $storage->copy($internalPath1, $internalPath2); } else { @@ -552,7 +554,7 @@ class View { array(Filesystem::signal_param_path => $path) ); } - list($storage, $internalPath) = Filesystem::resolvePath($path . $postFix); + list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix); if ($storage) { $result = $storage->hash($type, $internalPath, $raw); $result = \OC_FileProxy::runPostProxies('hash', $absolutePath, $result); @@ -587,7 +589,7 @@ class View { return false; } $run = $this->runHooks($hooks, $path); - list($storage, $internalPath) = Filesystem::resolvePath($path . $postFix); + list($storage, $internalPath) = Filesystem::resolvePath($absolutePath . $postFix); if ($run and $storage) { if (!is_null($extraParam)) { $result = $storage->$operation($internalPath, $extraParam); @@ -747,8 +749,23 @@ class View { $files[$i]['permissions'] = $permissions[$file['fileid']]; } - usort($files, "fileCmp"); - return $files; + if ($mimetype_filter) { + foreach ($files as $file) { + if (strpos($mimetype_filter, '/')) { + if ($file['mimetype'] === $mimetype_filter) { + $result[] = $file; + } + } else { + if ($file['mimepart'] === $mimetype_filter) { + $result[] = $file; + } + } + } + } else { + $result = $files; + } + + return $result; } /** diff --git a/lib/public/share.php b/lib/public/share.php index 060f67c9e9..707d5abe44 100644 --- a/lib/public/share.php +++ b/lib/public/share.php @@ -534,7 +534,7 @@ class Share { $backend = self::getBackend($itemType); // Get filesystem root to add it to the file target and remove from the file source, match file_source with the file cache if ($itemType == 'file' || $itemType == 'folder') { - $root = \OC_Filesystem::getRoot(); + $root = \OC\Files\Filesystem::getRoot(); $where = 'INNER JOIN `*PREFIX*fscache` ON `file_source` = `*PREFIX*fscache`.`id`'; if (!isset($item)) { $where .= ' WHERE `file_target` IS NOT NULL'; @@ -621,7 +621,7 @@ class Share { } else { if ($itemType == 'file' || $itemType == 'folder') { $where .= ' `file_target` = ?'; - $item = \OC_Filesystem::normalizePath($item); + $item = \OC\Files\Filesystem::normalizePath($item); } else { $where .= ' `item_target` = ?'; } From dea0d20a9ad0270896e57a7459d6276798658d7c Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sat, 27 Oct 2012 12:18:01 +0200 Subject: [PATCH 061/272] fix delete and sort files --- apps/files/ajax/delete.php | 17 +++++++---------- apps/files/ajax/scan.php | 2 ++ apps/files/index.php | 12 ++++++++++++ apps/files_sharing/public.php | 4 ++-- 4 files changed, 23 insertions(+), 12 deletions(-) diff --git a/apps/files/ajax/delete.php b/apps/files/ajax/delete.php index ae6158a05a..fdd0c0c491 100644 --- a/apps/files/ajax/delete.php +++ b/apps/files/ajax/delete.php @@ -12,18 +12,15 @@ $files = isset($_POST["file"]) ? stripslashes($_POST["file"]) : stripslashes($_P $files = explode(';', $files); $filesWithError = ''; -if (OC_User::isLoggedIn()) { - $success = true; - //Now delete - foreach ($files as $file) { - if ($dir != '' || $file != 'Shared' && !\OC\Files\Filesystem::unlink($dir . '/' . $file)) { - $filesWithError .= $file . "\n"; - $success = false; - } +$success = true; + +//Now delete +foreach ($files as $file) { + if (($dir === '' && $file === 'Shared') || !\OC\Files\Filesystem::unlink($dir . '/' . $file)) { + $filesWithError .= $file . "\n"; + $success = false; } -} else { - $success = false; } if ($success) { diff --git a/apps/files/ajax/scan.php b/apps/files/ajax/scan.php index 5cd9572d7f..91b20fa836 100644 --- a/apps/files/ajax/scan.php +++ b/apps/files/ajax/scan.php @@ -1,5 +1,7 @@ assign('uidOwner', $uidOwner); $tmpl->assign('dir', basename($dir)); $tmpl->assign('filename', basename($path)); - $tmpl->assign('mimetype', OC_Filesystem::getMimeType($path)); + $tmpl->assign('mimetype', \OC\Files\Filesystem::getMimeType($path)); $tmpl->assign('allowZipDownload', intval(OCP\Config::getSystemValue('allowZipDownload', true))); if (isset($_GET['path'])) { $getPath = $_GET['path']; @@ -200,7 +200,7 @@ if (isset($_GET['file']) || isset($_GET['dir'])) { $tmpl->assign('uidOwner', $uidOwner); $tmpl->assign('dir', dirname($path)); $tmpl->assign('filename', basename($path)); - $tmpl->assign('mimetype', OC_Filesystem::getMimeType($path)); + $tmpl->assign('mimetype', \OC\Files\Filesystem::getMimeType($path)); if ($type == 'file') { $tmpl->assign('downloadURL', OCP\Util::linkToPublic('files').'&file='.urlencode($_GET['file']).'&download', false); } else { From 86be7e88721ada34e7b230a0e5dfb4df546bc38a Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sat, 27 Oct 2012 12:23:13 +0200 Subject: [PATCH 062/272] remove last uses of depricated OC_Filesystem --- apps/files_sharing/public.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/apps/files_sharing/public.php b/apps/files_sharing/public.php index 143edd23c4..35e7734e2d 100644 --- a/apps/files_sharing/public.php +++ b/apps/files_sharing/public.php @@ -24,7 +24,7 @@ if (isset($_GET['token'])) { function getID($path) { // use the share table from the db to find the item source if the file was reshared because shared files //are not stored in the file cache. - if (substr(OC_Filesystem::getMountPoint($path), -7, 6) == "Shared") { + if (substr(\OC\Files\Filesystem::getMountPoint($path), -7, 6) == "Shared") { $path_parts = explode('/', $path, 5); $user = $path_parts[1]; $intPath = '/'.$path_parts[4]; @@ -33,7 +33,9 @@ function getID($path) { $row = $result->fetchRow(); $fileSource = $row['item_source']; } else { - $fileSource = OC_Filecache::getId($path, ''); + $rootView = new \OC\Files\View(''); + $meta = $rootView->getFileInfo($path); + $fileSource = $meta['fileid']; } return $fileSource; @@ -102,7 +104,7 @@ if (isset($_GET['file']) || isset($_GET['dir'])) { if (isset($_GET['path'])) { $path .= $_GET['path']; $dir .= $_GET['path']; - if (!OC_Filesystem::file_exists($path)) { + if (!\OC\Files\Filesystem::file_exists($path)) { header('HTTP/1.0 404 Not Found'); $tmpl = new OCP\Template('', '404', 'guest'); $tmpl->printPage(); @@ -130,7 +132,7 @@ if (isset($_GET['file']) || isset($_GET['dir'])) { $tmpl = new OCP\Template('files_sharing', 'public', 'base'); $tmpl->assign('owner', $uidOwner); // Show file list - if (OC_Filesystem::is_dir($path)) { + if (\OC\Files\Filesystem::is_dir($path)) { OCP\Util::addStyle('files', 'files'); OCP\Util::addScript('files', 'files'); OCP\Util::addScript('files', 'filelist'); From 0fb707d2c90846c1231cdefac79e9b58c8351c81 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sat, 27 Oct 2012 14:25:15 +0200 Subject: [PATCH 063/272] some more porting of the sharing --- apps/files_sharing/appinfo/update.php | 9 +++++---- apps/files_sharing/public.php | 3 ++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/apps/files_sharing/appinfo/update.php b/apps/files_sharing/appinfo/update.php index 51048bd178..69c526eae0 100644 --- a/apps/files_sharing/appinfo/update.php +++ b/apps/files_sharing/appinfo/update.php @@ -9,11 +9,12 @@ if (version_compare($installedVersion, '0.3', '<')) { OC_User::useBackend(new OC_User_Database()); OC_Group::useBackend(new OC_Group_Database()); OC_App::loadApps(array('authentication')); + $rootView = new \OC\Files\View(''); while ($row = $result->fetchRow()) { - $meta = \OC\Files\Filesystem::getId($path, ''); - $itemSource = OC_FileCache::getId($row['source'], ''); + $meta = $rootView->getFileInfo($$row['source']); + $itemSource = $meta['fileid']; if ($itemSource != -1) { - $file = OC_FileCache::get($row['source'], ''); + $file = $meta; if ($file['mimetype'] == 'httpd/unix-directory') { $itemType = 'folder'; } else { @@ -69,6 +70,6 @@ if (version_compare($installedVersion, '0.3.3', '<')) { OC_App::loadApps(array('authentication')); $users = OC_User::getUsers(); foreach ($users as $user) { - OC_FileCache::delete('Shared', '/'.$user.'/files/'); +// OC_FileCache::delete('Shared', '/'.$user.'/files/'); } } diff --git a/apps/files_sharing/public.php b/apps/files_sharing/public.php index 35e7734e2d..1fe7311002 100644 --- a/apps/files_sharing/public.php +++ b/apps/files_sharing/public.php @@ -10,7 +10,8 @@ if (isset($_GET['token'])) { $qry = \OC_DB::prepare('SELECT `source` FROM `*PREFIX*sharing` WHERE `target` = ? LIMIT 1'); $filepath = $qry->execute(array($_GET['token']))->fetchOne(); if(isset($filepath)) { - $info = OC_FileCache_Cached::get($filepath, ''); + $rootView = new \OC\Files\View(''); + $info = $rootView->getFileInfo($filepath, ''); if(strtolower($info['mimetype']) == 'httpd/unix-directory') { $_GET['dir'] = $filepath; } else { From 7891add28655c7f39b388ac0a889b246b23ad934 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Sat, 27 Oct 2012 15:26:28 +0200 Subject: [PATCH 064/272] don't call $('#found_versions').chosen(); after opening the history drop-down since no version is selected at the beginning --- apps/files_versions/js/versions.js | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/files_versions/js/versions.js b/apps/files_versions/js/versions.js index 426d521df8..b9c5468981 100644 --- a/apps/files_versions/js/versions.js +++ b/apps/files_versions/js/versions.js @@ -73,7 +73,6 @@ function createVersionsDropdown(filename, files) { $.each( versions, function(index, row ) { addVersion( row ); }); - $('#found_versions').chosen(); } else { $('#found_versions').hide(); $('#makelink').hide(); From 72aebe3aa3ce51ef47ef6005b528c4d296e208c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Sat, 27 Oct 2012 15:24:01 +0200 Subject: [PATCH 065/272] fix broken paths in versions app --- apps/files_versions/lib/versions.php | 41 +++++++++++++--------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/apps/files_versions/lib/versions.php b/apps/files_versions/lib/versions.php index 47e514d9d7..e83310172a 100644 --- a/apps/files_versions/lib/versions.php +++ b/apps/files_versions/lib/versions.php @@ -58,10 +58,8 @@ class Storage { public function store($filename) { if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') { list($uid, $filename) = self::getUidAndFilename($filename); - - $userHome = \OC_User::getHome($uid); - $files_view = new \OC\Files\View($userHome.'/files'); - $users_view = new \OC\Files\View($userHome); + $files_view = new \OC\Files\View('/'.\OCP\User::getUser() .'/files'); + $users_view = new \OC\Files\View('/'.\OCP\User::getUser()); //check if source file already exist as version to avoid recursions. // todo does this check work? @@ -96,11 +94,12 @@ class Storage { // check mininterval if the file is being modified by the owner (all shared files should be versioned despite mininterval) if ($uid == \OCP\User::getUser()) { - $versions_fileview = new \OC\Files\View($userHome.'/files_versions'); - $versionsFolderName=\OCP\Config::getSystemValue('datadirectory'). $versions_fileview->getAbsolutePath(''); - $matches=glob($versionsFolderName.'/'.$filename.'.v*'); + $versions_fileview = new \OC\Files\View('/'.\OCP\User::getUser().'/files_versions'); + $versionsName=\OCP\Config::getSystemValue('datadirectory').$versions_fileview->getAbsolutePath($filename); + $versionsFolderName=\OCP\Config::getSystemValue('datadirectory').$versions_fileview->getAbsolutePath(''); + $matches=glob($versionsName.'.v*'); sort($matches); - $parts=explode('.v', end($matches)); + $parts=explode('.v',end($matches)); if((end($parts)+Storage::DEFAULTMININTERVAL)>time()) { return false; } @@ -110,7 +109,7 @@ class Storage { // create all parent folders $info=pathinfo($filename); if(!file_exists($versionsFolderName.'/'.$info['dirname'])) { - mkdir($versionsFolderName.'/'.$info['dirname'],0750, true); + mkdir($versionsFolderName.'/'.$info['dirname'],0750,true); } // store a new version of a file @@ -129,7 +128,7 @@ class Storage { if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') { list($uid, $filename) = self::getUidAndFilename($filename); - $users_view = new \OC\Files\View(\OC_User::getHome($uid)); + $users_view = new \OC\Files\View('/'.\OCP\User::getUser()); // rollback if( @$users_view->copy('files_versions'.$filename.'.v'.$revision, 'files'.$filename) ) { @@ -152,12 +151,12 @@ class Storage { public static function isversioned($filename) { if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') { list($uid, $filename) = self::getUidAndFilename($filename); - $versions_fileview = new \OC\Files\View(\OC_User::getHome($uid).'/files_versions'); + $versions_fileview = new \OC\Files\View('/'.\OCP\User::getUser().'/files_versions'); - $versionsFolderName=\OCP\Config::getSystemValue('datadirectory'). $versions_fileview->getAbsolutePath(''); + $versionsName=\OCP\Config::getSystemValue('datadirectory').$versions_fileview->getAbsolutePath($filename); // check for old versions - $matches=glob($versionsFolderName.$filename.'.v*'); + $matches=glob($versionsName.'.v*'); if(count($matches)>0) { return true; }else{ @@ -177,22 +176,20 @@ class Storage { * @returns array */ public static function getVersions( $filename, $count = 0 ) { - if( \OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true' ) { list($uid, $filename) = self::getUidAndFilename($filename); - $versions_fileview = new \OC\Files\View(\OC_User::getHome($uid).'/files_versions'); + $versions_fileview = new \OC\Files\View('/'.\OCP\User::getUser().'/files_versions'); - $versionsFolderName = \OCP\Config::getSystemValue('datadirectory'). $versions_fileview->getAbsolutePath(''); + $versionsName = \OCP\Config::getSystemValue('datadirectory').$versions_fileview->getAbsolutePath($filename); $versions = array(); - // fetch for old versions - $matches = glob( $versionsFolderName.'/'.$filename.'.v*' ); + $matches = glob( $versionsName.'.v*' ); sort( $matches ); $i = 0; - $files_view = new \OC\Files\View(\OC_User::getHome($uid).'/files'); + $files_view = new \OC\Files\View('/'.\OCP\User::getUser().'/files'); $local_file = $files_view->getLocalFile($filename); foreach( $matches as $ma ) { @@ -249,10 +246,10 @@ class Storage { list($uid, $filename) = self::getUidAndFilename($filename); $versions_fileview = new \OC\Files\View('/'.$uid.'/files_versions'); - $versionsFolderName=\OCP\Config::getSystemValue('datadirectory'). $versions_fileview->getAbsolutePath(''); + $versionsName=\OCP\Config::getSystemValue('datadirectory').$versions_fileview->getAbsolutePath($filename); // check for old versions - $matches = glob( $versionsFolderName.'/'.$filename.'.v*' ); + $matches = glob( $versionsName.'.v*' ); if( count( $matches ) > \OCP\Config::getSystemValue( 'files_versionmaxversions', Storage::DEFAULTMAXVERSIONS ) ) { @@ -263,7 +260,7 @@ class Storage { foreach( $deleteItems as $de ) { - unlink( $versionsFolderName.'/'.$filename.'.v'.$de ); + unlink( $versionsName.'.v'.$de ); } } From 0d3f68cc7a425410e498e9946ab9847348dcdc55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Sat, 27 Oct 2012 15:23:35 +0200 Subject: [PATCH 066/272] fix function documentation --- lib/user.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/user.php b/lib/user.php index 064fcbad96..869984a16e 100644 --- a/lib/user.php +++ b/lib/user.php @@ -369,8 +369,7 @@ class OC_User { * @param $password The password * @returns string * - * Check if the password is correct without logging in the user - * returns the user id or false + * returns the path to the users home directory */ public static function getHome($uid) { foreach(self::$_usedBackends as $backend) { From 2dc1b778b47c7a765b1c4b56aa928c74f7116946 Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Sat, 27 Oct 2012 16:24:24 +0200 Subject: [PATCH 067/272] Include copy of Symfony routing component, and don't use composer --- .gitignore | 4 - 3rdparty/bin/composer | Bin 631359 -> 0 bytes .../Component/Routing/Annotation/Route.php | 103 ++++++ .../Component/Routing/CompiledRoute.php | 134 ++++++++ .../Routing/Exception/ExceptionInterface.php | 23 ++ .../Exception/InvalidParameterException.php | 23 ++ .../Exception/MethodNotAllowedException.php | 38 +++ .../MissingMandatoryParametersException.php | 24 ++ .../Exception/ResourceNotFoundException.php | 25 ++ .../Exception/RouteNotFoundException.php | 23 ++ .../Generator/Dumper/GeneratorDumper.php | 39 +++ .../Dumper/GeneratorDumperInterface.php | 45 +++ .../Generator/Dumper/PhpGeneratorDumper.php | 150 +++++++++ .../Routing/Generator/UrlGenerator.php | 176 ++++++++++ .../Generator/UrlGeneratorInterface.php | 37 +++ .../routing/Symfony/Component/Routing/LICENSE | 19 ++ .../Routing/Loader/AnnotationClassLoader.php | 213 ++++++++++++ .../Loader/AnnotationDirectoryLoader.php | 77 +++++ .../Routing/Loader/AnnotationFileLoader.php | 125 +++++++ .../Routing/Loader/ClosureLoader.php | 54 +++ .../Routing/Loader/PhpFileLoader.php | 64 ++++ .../Routing/Loader/XmlFileLoader.php | 224 +++++++++++++ .../Routing/Loader/YamlFileLoader.php | 142 ++++++++ .../Loader/schema/routing/routing-1.0.xsd | 38 +++ .../Routing/Matcher/ApacheUrlMatcher.php | 76 +++++ .../Matcher/Dumper/ApacheMatcherDumper.php | 155 +++++++++ .../Routing/Matcher/Dumper/MatcherDumper.php | 44 +++ .../Matcher/Dumper/MatcherDumperInterface.php | 41 +++ .../Matcher/Dumper/PhpMatcherDumper.php | 293 ++++++++++++++++ .../Matcher/RedirectableUrlMatcher.php | 53 +++ .../RedirectableUrlMatcherInterface.php | 35 ++ .../Component/Routing/Matcher/UrlMatcher.php | 151 +++++++++ .../Routing/Matcher/UrlMatcherInterface.php | 38 +++ .../Symfony/Component/Routing/README.md | 32 ++ .../Component/Routing/RequestContext.php | 250 ++++++++++++++ .../Routing/RequestContextAwareInterface.php | 27 ++ .../Symfony/Component/Routing/Route.php | 312 ++++++++++++++++++ .../Component/Routing/RouteCollection.php | 259 +++++++++++++++ .../Component/Routing/RouteCompiler.php | 128 +++++++ .../Routing/RouteCompilerInterface.php | 29 ++ .../Symfony/Component/Routing/Router.php | 263 +++++++++++++++ .../Component/Routing/RouterInterface.php | 26 ++ .../Symfony/Component/Routing/composer.json | 29 ++ composer.json | 18 - 44 files changed, 4037 insertions(+), 22 deletions(-) delete mode 100755 3rdparty/bin/composer create mode 100644 3rdparty/symfony/routing/Symfony/Component/Routing/Annotation/Route.php create mode 100644 3rdparty/symfony/routing/Symfony/Component/Routing/CompiledRoute.php create mode 100644 3rdparty/symfony/routing/Symfony/Component/Routing/Exception/ExceptionInterface.php create mode 100644 3rdparty/symfony/routing/Symfony/Component/Routing/Exception/InvalidParameterException.php create mode 100644 3rdparty/symfony/routing/Symfony/Component/Routing/Exception/MethodNotAllowedException.php create mode 100644 3rdparty/symfony/routing/Symfony/Component/Routing/Exception/MissingMandatoryParametersException.php create mode 100644 3rdparty/symfony/routing/Symfony/Component/Routing/Exception/ResourceNotFoundException.php create mode 100644 3rdparty/symfony/routing/Symfony/Component/Routing/Exception/RouteNotFoundException.php create mode 100644 3rdparty/symfony/routing/Symfony/Component/Routing/Generator/Dumper/GeneratorDumper.php create mode 100644 3rdparty/symfony/routing/Symfony/Component/Routing/Generator/Dumper/GeneratorDumperInterface.php create mode 100644 3rdparty/symfony/routing/Symfony/Component/Routing/Generator/Dumper/PhpGeneratorDumper.php create mode 100644 3rdparty/symfony/routing/Symfony/Component/Routing/Generator/UrlGenerator.php create mode 100644 3rdparty/symfony/routing/Symfony/Component/Routing/Generator/UrlGeneratorInterface.php create mode 100644 3rdparty/symfony/routing/Symfony/Component/Routing/LICENSE create mode 100644 3rdparty/symfony/routing/Symfony/Component/Routing/Loader/AnnotationClassLoader.php create mode 100644 3rdparty/symfony/routing/Symfony/Component/Routing/Loader/AnnotationDirectoryLoader.php create mode 100644 3rdparty/symfony/routing/Symfony/Component/Routing/Loader/AnnotationFileLoader.php create mode 100644 3rdparty/symfony/routing/Symfony/Component/Routing/Loader/ClosureLoader.php create mode 100644 3rdparty/symfony/routing/Symfony/Component/Routing/Loader/PhpFileLoader.php create mode 100644 3rdparty/symfony/routing/Symfony/Component/Routing/Loader/XmlFileLoader.php create mode 100644 3rdparty/symfony/routing/Symfony/Component/Routing/Loader/YamlFileLoader.php create mode 100644 3rdparty/symfony/routing/Symfony/Component/Routing/Loader/schema/routing/routing-1.0.xsd create mode 100644 3rdparty/symfony/routing/Symfony/Component/Routing/Matcher/ApacheUrlMatcher.php create mode 100644 3rdparty/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/ApacheMatcherDumper.php create mode 100644 3rdparty/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/MatcherDumper.php create mode 100644 3rdparty/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/MatcherDumperInterface.php create mode 100644 3rdparty/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php create mode 100644 3rdparty/symfony/routing/Symfony/Component/Routing/Matcher/RedirectableUrlMatcher.php create mode 100644 3rdparty/symfony/routing/Symfony/Component/Routing/Matcher/RedirectableUrlMatcherInterface.php create mode 100644 3rdparty/symfony/routing/Symfony/Component/Routing/Matcher/UrlMatcher.php create mode 100644 3rdparty/symfony/routing/Symfony/Component/Routing/Matcher/UrlMatcherInterface.php create mode 100644 3rdparty/symfony/routing/Symfony/Component/Routing/README.md create mode 100644 3rdparty/symfony/routing/Symfony/Component/Routing/RequestContext.php create mode 100644 3rdparty/symfony/routing/Symfony/Component/Routing/RequestContextAwareInterface.php create mode 100644 3rdparty/symfony/routing/Symfony/Component/Routing/Route.php create mode 100644 3rdparty/symfony/routing/Symfony/Component/Routing/RouteCollection.php create mode 100644 3rdparty/symfony/routing/Symfony/Component/Routing/RouteCompiler.php create mode 100644 3rdparty/symfony/routing/Symfony/Component/Routing/RouteCompilerInterface.php create mode 100644 3rdparty/symfony/routing/Symfony/Component/Routing/Router.php create mode 100644 3rdparty/symfony/routing/Symfony/Component/Routing/RouterInterface.php create mode 100644 3rdparty/symfony/routing/Symfony/Component/Routing/composer.json delete mode 100644 composer.json diff --git a/.gitignore b/.gitignore index 4ae39ed7fa..4749dea19d 100644 --- a/.gitignore +++ b/.gitignore @@ -54,7 +54,3 @@ nbproject # WebFinger .well-known /.buildpath -3rdparty/autoload.php -3rdparty/composer/ -3rdparty/symfony/ -composer.lock diff --git a/3rdparty/bin/composer b/3rdparty/bin/composer deleted file mode 100755 index 7999bd4ff6875d721d50c01d28e4b371c78d21d6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 631359 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≧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

J{fW+SfXRq-eaET&qYo^DAbnH>1R3g` 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 diff --git a/3rdparty/symfony/routing/Symfony/Component/Routing/Annotation/Route.php b/3rdparty/symfony/routing/Symfony/Component/Routing/Annotation/Route.php new file mode 100644 index 0000000000..f60af463f2 --- /dev/null +++ b/3rdparty/symfony/routing/Symfony/Component/Routing/Annotation/Route.php @@ -0,0 +1,103 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Annotation; + +/** + * Annotation class for @Route(). + * + * @Annotation + * + * @author Fabien Potencier + */ +class Route +{ + private $pattern; + private $name; + private $requirements; + private $options; + private $defaults; + + /** + * Constructor. + * + * @param array $data An array of key/value parameters. + */ + public function __construct(array $data) + { + $this->requirements = array(); + $this->options = array(); + $this->defaults = array(); + + if (isset($data['value'])) { + $data['pattern'] = $data['value']; + unset($data['value']); + } + + foreach ($data as $key => $value) { + $method = 'set'.$key; + if (!method_exists($this, $method)) { + throw new \BadMethodCallException(sprintf("Unknown property '%s' on annotation '%s'.", $key, get_class($this))); + } + $this->$method($value); + } + } + + public function setPattern($pattern) + { + $this->pattern = $pattern; + } + + public function getPattern() + { + return $this->pattern; + } + + public function setName($name) + { + $this->name = $name; + } + + public function getName() + { + return $this->name; + } + + public function setRequirements($requirements) + { + $this->requirements = $requirements; + } + + public function getRequirements() + { + return $this->requirements; + } + + public function setOptions($options) + { + $this->options = $options; + } + + public function getOptions() + { + return $this->options; + } + + public function setDefaults($defaults) + { + $this->defaults = $defaults; + } + + public function getDefaults() + { + return $this->defaults; + } +} diff --git a/3rdparty/symfony/routing/Symfony/Component/Routing/CompiledRoute.php b/3rdparty/symfony/routing/Symfony/Component/Routing/CompiledRoute.php new file mode 100644 index 0000000000..c86c9eca5b --- /dev/null +++ b/3rdparty/symfony/routing/Symfony/Component/Routing/CompiledRoute.php @@ -0,0 +1,134 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing; + +/** + * CompiledRoutes are returned by the RouteCompiler class. + * + * @author Fabien Potencier + */ +class CompiledRoute +{ + private $route; + private $variables; + private $tokens; + private $staticPrefix; + private $regex; + + /** + * Constructor. + * + * @param Route $route A original Route instance + * @param string $staticPrefix The static prefix of the compiled route + * @param string $regex The regular expression to use to match this route + * @param array $tokens An array of tokens to use to generate URL for this route + * @param array $variables An array of variables + */ + public function __construct(Route $route, $staticPrefix, $regex, array $tokens, array $variables) + { + $this->route = $route; + $this->staticPrefix = $staticPrefix; + $this->regex = $regex; + $this->tokens = $tokens; + $this->variables = $variables; + } + + /** + * Returns the Route instance. + * + * @return Route A Route instance + */ + public function getRoute() + { + return $this->route; + } + + /** + * Returns the static prefix. + * + * @return string The static prefix + */ + public function getStaticPrefix() + { + return $this->staticPrefix; + } + + /** + * Returns the regex. + * + * @return string The regex + */ + public function getRegex() + { + return $this->regex; + } + + /** + * Returns the tokens. + * + * @return array The tokens + */ + public function getTokens() + { + return $this->tokens; + } + + /** + * Returns the variables. + * + * @return array The variables + */ + public function getVariables() + { + return $this->variables; + } + + /** + * Returns the pattern. + * + * @return string The pattern + */ + public function getPattern() + { + return $this->route->getPattern(); + } + + /** + * Returns the options. + * + * @return array The options + */ + public function getOptions() + { + return $this->route->getOptions(); + } + + /** + * Returns the defaults. + * + * @return array The defaults + */ + public function getDefaults() + { + return $this->route->getDefaults(); + } + + /** + * Returns the requirements. + * + * @return array The requirements + */ + public function getRequirements() + { + return $this->route->getRequirements(); + } +} diff --git a/3rdparty/symfony/routing/Symfony/Component/Routing/Exception/ExceptionInterface.php b/3rdparty/symfony/routing/Symfony/Component/Routing/Exception/ExceptionInterface.php new file mode 100644 index 0000000000..5289f525fc --- /dev/null +++ b/3rdparty/symfony/routing/Symfony/Component/Routing/Exception/ExceptionInterface.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Exception; + +/** + * ExceptionInterface + * + * @author Alexandre Salomé + * + * @api + */ +interface ExceptionInterface +{ +} diff --git a/3rdparty/symfony/routing/Symfony/Component/Routing/Exception/InvalidParameterException.php b/3rdparty/symfony/routing/Symfony/Component/Routing/Exception/InvalidParameterException.php new file mode 100644 index 0000000000..4f12469529 --- /dev/null +++ b/3rdparty/symfony/routing/Symfony/Component/Routing/Exception/InvalidParameterException.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Exception; + +/** + * Exception thrown when a parameter is not valid + * + * @author Alexandre Salomé + * + * @api + */ +class InvalidParameterException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/3rdparty/symfony/routing/Symfony/Component/Routing/Exception/MethodNotAllowedException.php b/3rdparty/symfony/routing/Symfony/Component/Routing/Exception/MethodNotAllowedException.php new file mode 100644 index 0000000000..470ce52216 --- /dev/null +++ b/3rdparty/symfony/routing/Symfony/Component/Routing/Exception/MethodNotAllowedException.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Exception; + +/** + * The resource was found but the request method is not allowed. + * + * This exception should trigger an HTTP 405 response in your application code. + * + * @author Kris Wallsmith + * + * @api + */ +class MethodNotAllowedException extends \RuntimeException implements ExceptionInterface +{ + protected $allowedMethods; + + public function __construct(array $allowedMethods, $message = null, $code = 0, \Exception $previous = null) + { + $this->allowedMethods = array_map('strtoupper', $allowedMethods); + + parent::__construct($message, $code, $previous); + } + + public function getAllowedMethods() + { + return $this->allowedMethods; + } +} diff --git a/3rdparty/symfony/routing/Symfony/Component/Routing/Exception/MissingMandatoryParametersException.php b/3rdparty/symfony/routing/Symfony/Component/Routing/Exception/MissingMandatoryParametersException.php new file mode 100644 index 0000000000..5a523fa559 --- /dev/null +++ b/3rdparty/symfony/routing/Symfony/Component/Routing/Exception/MissingMandatoryParametersException.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Exception; + +/** + * Exception thrown when a route cannot be generated because of missing + * mandatory parameters. + * + * @author Alexandre Salomé + * + * @api + */ +class MissingMandatoryParametersException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/3rdparty/symfony/routing/Symfony/Component/Routing/Exception/ResourceNotFoundException.php b/3rdparty/symfony/routing/Symfony/Component/Routing/Exception/ResourceNotFoundException.php new file mode 100644 index 0000000000..362a0d61f3 --- /dev/null +++ b/3rdparty/symfony/routing/Symfony/Component/Routing/Exception/ResourceNotFoundException.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Exception; + +/** + * The resource was not found. + * + * This exception should trigger an HTTP 404 response in your application code. + * + * @author Kris Wallsmith + * + * @api + */ +class ResourceNotFoundException extends \RuntimeException implements ExceptionInterface +{ +} diff --git a/3rdparty/symfony/routing/Symfony/Component/Routing/Exception/RouteNotFoundException.php b/3rdparty/symfony/routing/Symfony/Component/Routing/Exception/RouteNotFoundException.php new file mode 100644 index 0000000000..4d5f288e7e --- /dev/null +++ b/3rdparty/symfony/routing/Symfony/Component/Routing/Exception/RouteNotFoundException.php @@ -0,0 +1,23 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Exception; + +/** + * Exception thrown when a route does not exists + * + * @author Alexandre Salomé + * + * @api + */ +class RouteNotFoundException extends \InvalidArgumentException implements ExceptionInterface +{ +} diff --git a/3rdparty/symfony/routing/Symfony/Component/Routing/Generator/Dumper/GeneratorDumper.php b/3rdparty/symfony/routing/Symfony/Component/Routing/Generator/Dumper/GeneratorDumper.php new file mode 100644 index 0000000000..1291bd12d0 --- /dev/null +++ b/3rdparty/symfony/routing/Symfony/Component/Routing/Generator/Dumper/GeneratorDumper.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Generator\Dumper; + +use Symfony\Component\Routing\RouteCollection; + +/** + * GeneratorDumper is the base class for all built-in generator dumpers. + * + * @author Fabien Potencier + */ +abstract class GeneratorDumper implements GeneratorDumperInterface +{ + private $routes; + + /** + * Constructor. + * + * @param RouteCollection $routes The RouteCollection to dump + */ + public function __construct(RouteCollection $routes) + { + $this->routes = $routes; + } + + public function getRoutes() + { + return $this->routes; + } +} diff --git a/3rdparty/symfony/routing/Symfony/Component/Routing/Generator/Dumper/GeneratorDumperInterface.php b/3rdparty/symfony/routing/Symfony/Component/Routing/Generator/Dumper/GeneratorDumperInterface.php new file mode 100644 index 0000000000..6f5353caf2 --- /dev/null +++ b/3rdparty/symfony/routing/Symfony/Component/Routing/Generator/Dumper/GeneratorDumperInterface.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Generator\Dumper; + +use Symfony\Component\Routing\RouteCollection; + +/** + * GeneratorDumperInterface is the interface that all generator dumper classes must implement. + * + * @author Fabien Potencier + * + * @api + */ +interface GeneratorDumperInterface +{ + /** + * Dumps a set of routes to a PHP class. + * + * Available options: + * + * * class: The class name + * * base_class: The base class name + * + * @param array $options An array of options + * + * @return string A PHP class representing the generator class + */ + public function dump(array $options = array()); + + /** + * Gets the routes to dump. + * + * @return RouteCollection A RouteCollection instance + */ + public function getRoutes(); +} diff --git a/3rdparty/symfony/routing/Symfony/Component/Routing/Generator/Dumper/PhpGeneratorDumper.php b/3rdparty/symfony/routing/Symfony/Component/Routing/Generator/Dumper/PhpGeneratorDumper.php new file mode 100644 index 0000000000..080dd3a253 --- /dev/null +++ b/3rdparty/symfony/routing/Symfony/Component/Routing/Generator/Dumper/PhpGeneratorDumper.php @@ -0,0 +1,150 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Generator\Dumper; + +use Symfony\Component\Routing\Route; + +/** + * PhpGeneratorDumper creates a PHP class able to generate URLs for a given set of routes. + * + * @author Fabien Potencier + * + * @api + */ +class PhpGeneratorDumper extends GeneratorDumper +{ + /** + * Dumps a set of routes to a PHP class. + * + * Available options: + * + * * class: The class name + * * base_class: The base class name + * + * @param array $options An array of options + * + * @return string A PHP class representing the generator class + * + * @api + */ + public function dump(array $options = array()) + { + $options = array_merge(array( + 'class' => 'ProjectUrlGenerator', + 'base_class' => 'Symfony\\Component\\Routing\\Generator\\UrlGenerator', + ), $options); + + return + $this->startClass($options['class'], $options['base_class']). + $this->addConstructor(). + $this->addGenerator(). + $this->endClass() + ; + } + + private function addGenerator() + { + $methods = array(); + foreach ($this->getRoutes()->all() as $name => $route) { + $compiledRoute = $route->compile(); + + $variables = str_replace("\n", '', var_export($compiledRoute->getVariables(), true)); + $defaults = str_replace("\n", '', var_export($compiledRoute->getDefaults(), true)); + $requirements = str_replace("\n", '', var_export($compiledRoute->getRequirements(), true)); + $tokens = str_replace("\n", '', var_export($compiledRoute->getTokens(), true)); + + $escapedName = str_replace('.', '__', $name); + + $methods[] = <<{'get'.\$escapedName.'RouteInfo'}(); + + return \$this->doGenerate(\$variables, \$defaults, \$requirements, \$tokens, \$parameters, \$name, \$absolute); + } + +$methods +EOF; + } + + private function startClass($class, $baseClass) + { + $routes = array(); + foreach ($this->getRoutes()->all() as $name => $route) { + $routes[] = " '$name' => true,"; + } + $routes = implode("\n", $routes); + + return <<context = \$context; + } + +EOF; + } + + private function endClass() + { + return << + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Generator; + +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Routing\RequestContext; +use Symfony\Component\Routing\Exception\InvalidParameterException; +use Symfony\Component\Routing\Exception\RouteNotFoundException; +use Symfony\Component\Routing\Exception\MissingMandatoryParametersException; + +/** + * UrlGenerator generates URL based on a set of routes. + * + * @author Fabien Potencier + * + * @api + */ +class UrlGenerator implements UrlGeneratorInterface +{ + protected $context; + protected $decodedChars = array( + // %2F is not valid in a URL, so we don't encode it (which is fine as the requirements explicitly allowed it) + '%2F' => '/', + ); + + protected $routes; + protected $cache; + + /** + * Constructor. + * + * @param RouteCollection $routes A RouteCollection instance + * @param RequestContext $context The context + * + * @api + */ + public function __construct(RouteCollection $routes, RequestContext $context) + { + $this->routes = $routes; + $this->context = $context; + $this->cache = array(); + } + + /** + * Sets the request context. + * + * @param RequestContext $context The context + * + * @api + */ + public function setContext(RequestContext $context) + { + $this->context = $context; + } + + /** + * Gets the request context. + * + * @return RequestContext The context + */ + public function getContext() + { + return $this->context; + } + + /** + * Generates a URL from the given parameters. + * + * @param string $name The name of the route + * @param mixed $parameters An array of parameters + * @param Boolean $absolute Whether to generate an absolute URL + * + * @return string The generated URL + * + * @throws Symfony\Component\Routing\Exception\RouteNotFoundException When route doesn't exist + * + * @api + */ + public function generate($name, $parameters = array(), $absolute = false) + { + if (null === $route = $this->routes->get($name)) { + throw new RouteNotFoundException(sprintf('Route "%s" does not exist.', $name)); + } + + if (!isset($this->cache[$name])) { + $this->cache[$name] = $route->compile(); + } + + return $this->doGenerate($this->cache[$name]->getVariables(), $route->getDefaults(), $route->getRequirements(), $this->cache[$name]->getTokens(), $parameters, $name, $absolute); + } + + /** + * @throws Symfony\Component\Routing\Exception\MissingMandatoryParametersException When route has some missing mandatory parameters + * @throws Symfony\Component\Routing\Exception\InvalidParameterException When a parameter value is not correct + */ + protected function doGenerate($variables, $defaults, $requirements, $tokens, $parameters, $name, $absolute) + { + $variables = array_flip($variables); + + $originParameters = $parameters; + $parameters = array_replace($this->context->getParameters(), $parameters); + $tparams = array_replace($defaults, $parameters); + + // all params must be given + if ($diff = array_diff_key($variables, $tparams)) { + throw new MissingMandatoryParametersException(sprintf('The "%s" route has some missing mandatory parameters ("%s").', $name, implode('", "', array_keys($diff)))); + } + + $url = ''; + $optional = true; + foreach ($tokens as $token) { + if ('variable' === $token[0]) { + if (false === $optional || !array_key_exists($token[3], $defaults) || (isset($parameters[$token[3]]) && (string) $parameters[$token[3]] != (string) $defaults[$token[3]])) { + if (!$isEmpty = in_array($tparams[$token[3]], array(null, '', false), true)) { + // check requirement + if ($tparams[$token[3]] && !preg_match('#^'.$token[2].'$#', $tparams[$token[3]])) { + throw new InvalidParameterException(sprintf('Parameter "%s" for route "%s" must match "%s" ("%s" given).', $token[3], $name, $token[2], $tparams[$token[3]])); + } + } + + if (!$isEmpty || !$optional) { + $url = $token[1].strtr(rawurlencode($tparams[$token[3]]), $this->decodedChars).$url; + } + + $optional = false; + } + } elseif ('text' === $token[0]) { + $url = $token[1].$url; + $optional = false; + } + } + + if (!$url) { + $url = '/'; + } + + // add a query string if needed + $extra = array_diff_key($originParameters, $variables, $defaults); + if ($extra && $query = http_build_query($extra, '', '&')) { + $url .= '?'.$query; + } + + $url = $this->context->getBaseUrl().$url; + + if ($this->context->getHost()) { + $scheme = $this->context->getScheme(); + if (isset($requirements['_scheme']) && ($req = strtolower($requirements['_scheme'])) && $scheme != $req) { + $absolute = true; + $scheme = $req; + } + + if ($absolute) { + $port = ''; + if ('http' === $scheme && 80 != $this->context->getHttpPort()) { + $port = ':'.$this->context->getHttpPort(); + } elseif ('https' === $scheme && 443 != $this->context->getHttpsPort()) { + $port = ':'.$this->context->getHttpsPort(); + } + + $url = $scheme.'://'.$this->context->getHost().$port.$url; + } + } + + return $url; + } +} diff --git a/3rdparty/symfony/routing/Symfony/Component/Routing/Generator/UrlGeneratorInterface.php b/3rdparty/symfony/routing/Symfony/Component/Routing/Generator/UrlGeneratorInterface.php new file mode 100644 index 0000000000..6f2800c27c --- /dev/null +++ b/3rdparty/symfony/routing/Symfony/Component/Routing/Generator/UrlGeneratorInterface.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Generator; + +use Symfony\Component\Routing\RequestContextAwareInterface; + +/** + * UrlGeneratorInterface is the interface that all URL generator classes must implements. + * + * @author Fabien Potencier + * + * @api + */ +interface UrlGeneratorInterface extends RequestContextAwareInterface +{ + /** + * Generates a URL from the given parameters. + * + * @param string $name The name of the route + * @param mixed $parameters An array of parameters + * @param Boolean $absolute Whether to generate an absolute URL + * + * @return string The generated URL + * + * @api + */ + public function generate($name, $parameters = array(), $absolute = false); +} diff --git a/3rdparty/symfony/routing/Symfony/Component/Routing/LICENSE b/3rdparty/symfony/routing/Symfony/Component/Routing/LICENSE new file mode 100644 index 0000000000..cdffe7aebc --- /dev/null +++ b/3rdparty/symfony/routing/Symfony/Component/Routing/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2004-2012 Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/3rdparty/symfony/routing/Symfony/Component/Routing/Loader/AnnotationClassLoader.php b/3rdparty/symfony/routing/Symfony/Component/Routing/Loader/AnnotationClassLoader.php new file mode 100644 index 0000000000..5f292d4589 --- /dev/null +++ b/3rdparty/symfony/routing/Symfony/Component/Routing/Loader/AnnotationClassLoader.php @@ -0,0 +1,213 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Loader; + +use Doctrine\Common\Annotations\Reader; +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Config\Loader\LoaderInterface; +use Symfony\Component\Config\Loader\LoaderResolver; + +/** + * AnnotationClassLoader loads routing information from a PHP class and its methods. + * + * You need to define an implementation for the getRouteDefaults() method. Most of the + * time, this method should define some PHP callable to be called for the route + * (a controller in MVC speak). + * + * The @Route annotation can be set on the class (for global parameters), + * and on each method. + * + * The @Route annotation main value is the route pattern. The annotation also + * recognizes three parameters: requirements, options, and name. The name parameter + * is mandatory. Here is an example of how you should be able to use it: + * + * /** + * * @Route("/Blog") + * * / + * class Blog + * { + * /** + * * @Route("/", name="blog_index") + * * / + * public function index() + * { + * } + * + * /** + * * @Route("/{id}", name="blog_post", requirements = {"id" = "\d+"}) + * * / + * public function show() + * { + * } + * } + * + * @author Fabien Potencier + */ +abstract class AnnotationClassLoader implements LoaderInterface +{ + protected $reader; + protected $routeAnnotationClass = 'Symfony\\Component\\Routing\\Annotation\\Route'; + protected $defaultRouteIndex; + + /** + * Constructor. + * + * @param Reader $reader + */ + public function __construct(Reader $reader) + { + $this->reader = $reader; + } + + /** + * Sets the annotation class to read route properties from. + * + * @param string $class A fully-qualified class name + */ + public function setRouteAnnotationClass($class) + { + $this->routeAnnotationClass = $class; + } + + /** + * Loads from annotations from a class. + * + * @param string $class A class name + * @param string $type The resource type + * + * @return RouteCollection A RouteCollection instance + * + * @throws \InvalidArgumentException When route can't be parsed + */ + public function load($class, $type = null) + { + if (!class_exists($class)) { + throw new \InvalidArgumentException(sprintf('Class "%s" does not exist.', $class)); + } + + $globals = array( + 'pattern' => '', + 'requirements' => array(), + 'options' => array(), + 'defaults' => array(), + ); + + $class = new \ReflectionClass($class); + if ($class->isAbstract()) { + throw new \InvalidArgumentException(sprintf('Annotations from class "%s" cannot be read as it is abstract.', $class)); + } + + if ($annot = $this->reader->getClassAnnotation($class, $this->routeAnnotationClass)) { + if (null !== $annot->getPattern()) { + $globals['pattern'] = $annot->getPattern(); + } + + if (null !== $annot->getRequirements()) { + $globals['requirements'] = $annot->getRequirements(); + } + + if (null !== $annot->getOptions()) { + $globals['options'] = $annot->getOptions(); + } + + if (null !== $annot->getDefaults()) { + $globals['defaults'] = $annot->getDefaults(); + } + } + + $collection = new RouteCollection(); + $collection->addResource(new FileResource($class->getFileName())); + + foreach ($class->getMethods() as $method) { + $this->defaultRouteIndex = 0; + foreach ($this->reader->getMethodAnnotations($method) as $annot) { + if ($annot instanceof $this->routeAnnotationClass) { + $this->addRoute($collection, $annot, $globals, $class, $method); + } + } + } + + return $collection; + } + + protected function addRoute(RouteCollection $collection, $annot, $globals, \ReflectionClass $class, \ReflectionMethod $method) + { + $name = $annot->getName(); + if (null === $name) { + $name = $this->getDefaultRouteName($class, $method); + } + + $defaults = array_merge($globals['defaults'], $annot->getDefaults()); + $requirements = array_merge($globals['requirements'], $annot->getRequirements()); + $options = array_merge($globals['options'], $annot->getOptions()); + + $route = new Route($globals['pattern'].$annot->getPattern(), $defaults, $requirements, $options); + + $this->configureRoute($route, $class, $method, $annot); + + $collection->add($name, $route); + } + + /** + * Returns true if this class supports the given resource. + * + * @param mixed $resource A resource + * @param string $type The resource type + * + * @return Boolean True if this class supports the given resource, false otherwise + */ + public function supports($resource, $type = null) + { + return is_string($resource) && preg_match('/^(?:\\\\?[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)+$/', $resource) && (!$type || 'annotation' === $type); + } + + /** + * Sets the loader resolver. + * + * @param LoaderResolver $resolver A LoaderResolver instance + */ + public function setResolver(LoaderResolver $resolver) + { + } + + /** + * Gets the loader resolver. + * + * @return LoaderResolver A LoaderResolver instance + */ + public function getResolver() + { + } + + /** + * Gets the default route name for a class method. + * + * @param \ReflectionClass $class + * @param \ReflectionMethod $method + * + * @return string + */ + protected function getDefaultRouteName(\ReflectionClass $class, \ReflectionMethod $method) + { + $name = strtolower(str_replace('\\', '_', $class->name).'_'.$method->name); + if ($this->defaultRouteIndex > 0) { + $name .= '_'.$this->defaultRouteIndex; + } + $this->defaultRouteIndex++; + + return $name; + } + + abstract protected function configureRoute(Route $route, \ReflectionClass $class, \ReflectionMethod $method, $annot); +} diff --git a/3rdparty/symfony/routing/Symfony/Component/Routing/Loader/AnnotationDirectoryLoader.php b/3rdparty/symfony/routing/Symfony/Component/Routing/Loader/AnnotationDirectoryLoader.php new file mode 100644 index 0000000000..8097cd67f9 --- /dev/null +++ b/3rdparty/symfony/routing/Symfony/Component/Routing/Loader/AnnotationDirectoryLoader.php @@ -0,0 +1,77 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Loader; + +use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Config\Resource\DirectoryResource; + +/** + * AnnotationDirectoryLoader loads routing information from annotations set + * on PHP classes and methods. + * + * @author Fabien Potencier + */ +class AnnotationDirectoryLoader extends AnnotationFileLoader +{ + /** + * Loads from annotations from a directory. + * + * @param string $path A directory path + * @param string $type The resource type + * + * @return RouteCollection A RouteCollection instance + * + * @throws \InvalidArgumentException When the directory does not exist or its routes cannot be parsed + */ + public function load($path, $type = null) + { + $dir = $this->locator->locate($path); + + $collection = new RouteCollection(); + $collection->addResource(new DirectoryResource($dir, '/\.php$/')); + foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dir), \RecursiveIteratorIterator::LEAVES_ONLY) as $file) { + if (!$file->isFile() || '.php' !== substr($file->getFilename(), -4)) { + continue; + } + + if ($class = $this->findClass($file)) { + $refl = new \ReflectionClass($class); + if ($refl->isAbstract()) { + continue; + } + + $collection->addCollection($this->loader->load($class, $type)); + } + } + + return $collection; + } + + /** + * Returns true if this class supports the given resource. + * + * @param mixed $resource A resource + * @param string $type The resource type + * + * @return Boolean True if this class supports the given resource, false otherwise + */ + public function supports($resource, $type = null) + { + try { + $path = $this->locator->locate($resource); + } catch (\Exception $e) { + return false; + } + + return is_string($resource) && is_dir($path) && (!$type || 'annotation' === $type); + } +} diff --git a/3rdparty/symfony/routing/Symfony/Component/Routing/Loader/AnnotationFileLoader.php b/3rdparty/symfony/routing/Symfony/Component/Routing/Loader/AnnotationFileLoader.php new file mode 100644 index 0000000000..49e1cb2f77 --- /dev/null +++ b/3rdparty/symfony/routing/Symfony/Component/Routing/Loader/AnnotationFileLoader.php @@ -0,0 +1,125 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Loader; + +use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Config\Loader\FileLoader; +use Symfony\Component\Config\FileLocator; + +/** + * AnnotationFileLoader loads routing information from annotations set + * on a PHP class and its methods. + * + * @author Fabien Potencier + */ +class AnnotationFileLoader extends FileLoader +{ + protected $loader; + + /** + * Constructor. + * + * @param FileLocator $locator A FileLocator instance + * @param AnnotationClassLoader $loader An AnnotationClassLoader instance + * @param string|array $paths A path or an array of paths where to look for resources + */ + public function __construct(FileLocator $locator, AnnotationClassLoader $loader, $paths = array()) + { + if (!function_exists('token_get_all')) { + throw new \RuntimeException('The Tokenizer extension is required for the routing annotation loaders.'); + } + + parent::__construct($locator, $paths); + + $this->loader = $loader; + } + + /** + * Loads from annotations from a file. + * + * @param string $file A PHP file path + * @param string $type The resource type + * + * @return RouteCollection A RouteCollection instance + * + * @throws \InvalidArgumentException When the file does not exist or its routes cannot be parsed + */ + public function load($file, $type = null) + { + $path = $this->locator->locate($file); + + $collection = new RouteCollection(); + if ($class = $this->findClass($path)) { + $collection->addResource(new FileResource($path)); + $collection->addCollection($this->loader->load($class, $type)); + } + + return $collection; + } + + /** + * Returns true if this class supports the given resource. + * + * @param mixed $resource A resource + * @param string $type The resource type + * + * @return Boolean True if this class supports the given resource, false otherwise + */ + public function supports($resource, $type = null) + { + return is_string($resource) && 'php' === pathinfo($resource, PATHINFO_EXTENSION) && (!$type || 'annotation' === $type); + } + + /** + * Returns the full class name for the first class in the file. + * + * @param string $file A PHP file path + * + * @return string|false Full class name if found, false otherwise + */ + protected function findClass($file) + { + $class = false; + $namespace = false; + $tokens = token_get_all(file_get_contents($file)); + for ($i = 0, $count = count($tokens); $i < $count; $i++) { + $token = $tokens[$i]; + + if (!is_array($token)) { + continue; + } + + if (true === $class && T_STRING === $token[0]) { + return $namespace.'\\'.$token[1]; + } + + if (true === $namespace && T_STRING === $token[0]) { + $namespace = ''; + do { + $namespace .= $token[1]; + $token = $tokens[++$i]; + } while ($i < $count && is_array($token) && in_array($token[0], array(T_NS_SEPARATOR, T_STRING))); + } + + if (T_CLASS === $token[0]) { + $class = true; + } + + if (T_NAMESPACE === $token[0]) { + $namespace = true; + } + } + + return false; + } +} diff --git a/3rdparty/symfony/routing/Symfony/Component/Routing/Loader/ClosureLoader.php b/3rdparty/symfony/routing/Symfony/Component/Routing/Loader/ClosureLoader.php new file mode 100644 index 0000000000..ca49c8fa35 --- /dev/null +++ b/3rdparty/symfony/routing/Symfony/Component/Routing/Loader/ClosureLoader.php @@ -0,0 +1,54 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Loader; + +use Symfony\Component\Config\Loader\Loader; + +/** + * ClosureLoader loads routes from a PHP closure. + * + * The Closure must return a RouteCollection instance. + * + * @author Fabien Potencier + * + * @api + */ +class ClosureLoader extends Loader +{ + /** + * Loads a Closure. + * + * @param \Closure $closure A Closure + * @param string $type The resource type + * + * @api + */ + public function load($closure, $type = null) + { + return call_user_func($closure); + } + + /** + * Returns true if this class supports the given resource. + * + * @param mixed $resource A resource + * @param string $type The resource type + * + * @return Boolean True if this class supports the given resource, false otherwise + * + * @api + */ + public function supports($resource, $type = null) + { + return $resource instanceof \Closure && (!$type || 'closure' === $type); + } +} diff --git a/3rdparty/symfony/routing/Symfony/Component/Routing/Loader/PhpFileLoader.php b/3rdparty/symfony/routing/Symfony/Component/Routing/Loader/PhpFileLoader.php new file mode 100644 index 0000000000..ffd31f9444 --- /dev/null +++ b/3rdparty/symfony/routing/Symfony/Component/Routing/Loader/PhpFileLoader.php @@ -0,0 +1,64 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Loader; + +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Config\Loader\FileLoader; + +/** + * PhpFileLoader loads routes from a PHP file. + * + * The file must return a RouteCollection instance. + * + * @author Fabien Potencier + * + * @api + */ +class PhpFileLoader extends FileLoader +{ + /** + * Loads a PHP file. + * + * @param mixed $file A PHP file path + * @param string $type The resource type + * + * @api + */ + public function load($file, $type = null) + { + // the loader variable is exposed to the included file below + $loader = $this; + + $path = $this->locator->locate($file); + $this->setCurrentDir(dirname($path)); + + $collection = include $path; + $collection->addResource(new FileResource($path)); + + return $collection; + } + + /** + * Returns true if this class supports the given resource. + * + * @param mixed $resource A resource + * @param string $type The resource type + * + * @return Boolean True if this class supports the given resource, false otherwise + * + * @api + */ + public function supports($resource, $type = null) + { + return is_string($resource) && 'php' === pathinfo($resource, PATHINFO_EXTENSION) && (!$type || 'php' === $type); + } +} diff --git a/3rdparty/symfony/routing/Symfony/Component/Routing/Loader/XmlFileLoader.php b/3rdparty/symfony/routing/Symfony/Component/Routing/Loader/XmlFileLoader.php new file mode 100644 index 0000000000..5dad9db3fa --- /dev/null +++ b/3rdparty/symfony/routing/Symfony/Component/Routing/Loader/XmlFileLoader.php @@ -0,0 +1,224 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Loader; + +use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Routing\Route; +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Config\Loader\FileLoader; + +/** + * XmlFileLoader loads XML routing files. + * + * @author Fabien Potencier + * + * @api + */ +class XmlFileLoader extends FileLoader +{ + /** + * Loads an XML file. + * + * @param string $file An XML file path + * @param string $type The resource type + * + * @return RouteCollection A RouteCollection instance + * + * @throws \InvalidArgumentException When a tag can't be parsed + * + * @api + */ + public function load($file, $type = null) + { + $path = $this->locator->locate($file); + + $xml = $this->loadFile($path); + + $collection = new RouteCollection(); + $collection->addResource(new FileResource($path)); + + // process routes and imports + foreach ($xml->documentElement->childNodes as $node) { + if (!$node instanceof \DOMElement) { + continue; + } + + $this->parseNode($collection, $node, $path, $file); + } + + return $collection; + } + + /** + * Parses a node from a loaded XML file. + * + * @param RouteCollection $collection the collection to associate with the node + * @param DOMElement $node the node to parse + * @param string $path the path of the XML file being processed + * @param string $file + */ + protected function parseNode(RouteCollection $collection, \DOMElement $node, $path, $file) + { + switch ($node->tagName) { + case 'route': + $this->parseRoute($collection, $node, $path); + break; + case 'import': + $resource = (string) $node->getAttribute('resource'); + $type = (string) $node->getAttribute('type'); + $prefix = (string) $node->getAttribute('prefix'); + $this->setCurrentDir(dirname($path)); + $collection->addCollection($this->import($resource, ('' !== $type ? $type : null), false, $file), $prefix); + break; + default: + throw new \InvalidArgumentException(sprintf('Unable to parse tag "%s"', $node->tagName)); + } + } + + /** + * Returns true if this class supports the given resource. + * + * @param mixed $resource A resource + * @param string $type The resource type + * + * @return Boolean True if this class supports the given resource, false otherwise + * + * @api + */ + public function supports($resource, $type = null) + { + return is_string($resource) && 'xml' === pathinfo($resource, PATHINFO_EXTENSION) && (!$type || 'xml' === $type); + } + + /** + * Parses a route and adds it to the RouteCollection. + * + * @param RouteCollection $collection A RouteCollection instance + * @param \DOMElement $definition Route definition + * @param string $file An XML file path + * + * @throws \InvalidArgumentException When the definition cannot be parsed + */ + protected function parseRoute(RouteCollection $collection, \DOMElement $definition, $file) + { + $defaults = array(); + $requirements = array(); + $options = array(); + + foreach ($definition->childNodes as $node) { + if (!$node instanceof \DOMElement) { + continue; + } + + switch ($node->tagName) { + case 'default': + $defaults[(string) $node->getAttribute('key')] = trim((string) $node->nodeValue); + break; + case 'option': + $options[(string) $node->getAttribute('key')] = trim((string) $node->nodeValue); + break; + case 'requirement': + $requirements[(string) $node->getAttribute('key')] = trim((string) $node->nodeValue); + break; + default: + throw new \InvalidArgumentException(sprintf('Unable to parse tag "%s"', $node->tagName)); + } + } + + $route = new Route((string) $definition->getAttribute('pattern'), $defaults, $requirements, $options); + + $collection->add((string) $definition->getAttribute('id'), $route); + } + + /** + * Loads an XML file. + * + * @param string $file An XML file path + * + * @return \DOMDocument + * + * @throws \InvalidArgumentException When loading of XML file returns error + */ + protected function loadFile($file) + { + $internalErrors = libxml_use_internal_errors(true); + $disableEntities = libxml_disable_entity_loader(true); + libxml_clear_errors(); + + $dom = new \DOMDocument(); + $dom->validateOnParse = true; + if (!$dom->loadXML(file_get_contents($file), LIBXML_NONET | (defined('LIBXML_COMPACT') ? LIBXML_COMPACT : 0))) { + libxml_disable_entity_loader($disableEntities); + + throw new \InvalidArgumentException(implode("\n", $this->getXmlErrors($internalErrors))); + } + $dom->normalizeDocument(); + + libxml_use_internal_errors($internalErrors); + libxml_disable_entity_loader($disableEntities); + + foreach ($dom->childNodes as $child) { + if ($child->nodeType === XML_DOCUMENT_TYPE_NODE) { + throw new \InvalidArgumentException('Document types are not allowed.'); + } + } + + $this->validate($dom); + + return $dom; + } + + /** + * Validates a loaded XML file. + * + * @param \DOMDocument $dom A loaded XML file + * + * @throws \InvalidArgumentException When XML doesn't validate its XSD schema + */ + protected function validate(\DOMDocument $dom) + { + $location = __DIR__.'/schema/routing/routing-1.0.xsd'; + + $current = libxml_use_internal_errors(true); + libxml_clear_errors(); + + if (!$dom->schemaValidate($location)) { + throw new \InvalidArgumentException(implode("\n", $this->getXmlErrors($current))); + } + libxml_use_internal_errors($current); + } + + /** + * Retrieves libxml errors and clears them. + * + * @return array An array of libxml error strings + */ + private function getXmlErrors($internalErrors) + { + $errors = array(); + foreach (libxml_get_errors() as $error) { + $errors[] = sprintf('[%s %s] %s (in %s - line %d, column %d)', + LIBXML_ERR_WARNING == $error->level ? 'WARNING' : 'ERROR', + $error->code, + trim($error->message), + $error->file ? $error->file : 'n/a', + $error->line, + $error->column + ); + } + + libxml_clear_errors(); + libxml_use_internal_errors($internalErrors); + + return $errors; + } +} diff --git a/3rdparty/symfony/routing/Symfony/Component/Routing/Loader/YamlFileLoader.php b/3rdparty/symfony/routing/Symfony/Component/Routing/Loader/YamlFileLoader.php new file mode 100644 index 0000000000..ee72383435 --- /dev/null +++ b/3rdparty/symfony/routing/Symfony/Component/Routing/Loader/YamlFileLoader.php @@ -0,0 +1,142 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Loader; + +use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Routing\Route; +use Symfony\Component\Config\Resource\FileResource; +use Symfony\Component\Yaml\Yaml; +use Symfony\Component\Config\Loader\FileLoader; + +/** + * YamlFileLoader loads Yaml routing files. + * + * @author Fabien Potencier + * + * @api + */ +class YamlFileLoader extends FileLoader +{ + private static $availableKeys = array( + 'type', 'resource', 'prefix', 'pattern', 'options', 'defaults', 'requirements' + ); + + /** + * Loads a Yaml file. + * + * @param string $file A Yaml file path + * @param string $type The resource type + * + * @return RouteCollection A RouteCollection instance + * + * @throws \InvalidArgumentException When route can't be parsed + * + * @api + */ + public function load($file, $type = null) + { + $path = $this->locator->locate($file); + + $config = Yaml::parse($path); + + $collection = new RouteCollection(); + $collection->addResource(new FileResource($path)); + + // empty file + if (null === $config) { + $config = array(); + } + + // not an array + if (!is_array($config)) { + throw new \InvalidArgumentException(sprintf('The file "%s" must contain a YAML array.', $file)); + } + + foreach ($config as $name => $config) { + $config = $this->normalizeRouteConfig($config); + + if (isset($config['resource'])) { + $type = isset($config['type']) ? $config['type'] : null; + $prefix = isset($config['prefix']) ? $config['prefix'] : null; + $this->setCurrentDir(dirname($path)); + $collection->addCollection($this->import($config['resource'], $type, false, $file), $prefix); + } else { + $this->parseRoute($collection, $name, $config, $path); + } + } + + return $collection; + } + + /** + * Returns true if this class supports the given resource. + * + * @param mixed $resource A resource + * @param string $type The resource type + * + * @return Boolean True if this class supports the given resource, false otherwise + * + * @api + */ + public function supports($resource, $type = null) + { + return is_string($resource) && 'yml' === pathinfo($resource, PATHINFO_EXTENSION) && (!$type || 'yaml' === $type); + } + + /** + * Parses a route and adds it to the RouteCollection. + * + * @param RouteCollection $collection A RouteCollection instance + * @param string $name Route name + * @param array $config Route definition + * @param string $file A Yaml file path + * + * @throws \InvalidArgumentException When config pattern is not defined for the given route + */ + protected function parseRoute(RouteCollection $collection, $name, $config, $file) + { + $defaults = isset($config['defaults']) ? $config['defaults'] : array(); + $requirements = isset($config['requirements']) ? $config['requirements'] : array(); + $options = isset($config['options']) ? $config['options'] : array(); + + if (!isset($config['pattern'])) { + throw new \InvalidArgumentException(sprintf('You must define a "pattern" for the "%s" route.', $name)); + } + + $route = new Route($config['pattern'], $defaults, $requirements, $options); + + $collection->add($name, $route); + } + + /** + * Normalize route configuration. + * + * @param array $config A resource config + * + * @return array + * + * @throws InvalidArgumentException if one of the provided config keys is not supported + */ + private function normalizeRouteConfig(array $config) + { + foreach ($config as $key => $value) { + if (!in_array($key, self::$availableKeys)) { + throw new \InvalidArgumentException(sprintf( + 'Yaml routing loader does not support given key: "%s". Expected one of the (%s).', + $key, implode(', ', self::$availableKeys) + )); + } + } + + return $config; + } +} diff --git a/3rdparty/symfony/routing/Symfony/Component/Routing/Loader/schema/routing/routing-1.0.xsd b/3rdparty/symfony/routing/Symfony/Component/Routing/Loader/schema/routing/routing-1.0.xsd new file mode 100644 index 0000000000..a9554e64df --- /dev/null +++ b/3rdparty/symfony/routing/Symfony/Component/Routing/Loader/schema/routing/routing-1.0.xsd @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/3rdparty/symfony/routing/Symfony/Component/Routing/Matcher/ApacheUrlMatcher.php b/3rdparty/symfony/routing/Symfony/Component/Routing/Matcher/ApacheUrlMatcher.php new file mode 100644 index 0000000000..3003dfdebb --- /dev/null +++ b/3rdparty/symfony/routing/Symfony/Component/Routing/Matcher/ApacheUrlMatcher.php @@ -0,0 +1,76 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Matcher; + +use Symfony\Component\Routing\Exception\MethodNotAllowedException; + +/** + * ApacheUrlMatcher matches URL based on Apache mod_rewrite matching (see ApacheMatcherDumper). + * + * @author Fabien Potencier + */ +class ApacheUrlMatcher extends UrlMatcher +{ + /** + * Tries to match a URL based on Apache mod_rewrite matching. + * + * Returns false if no route matches the URL. + * + * @param string $pathinfo The pathinfo to be parsed + * + * @return array An array of parameters + * + * @throws MethodNotAllowedException If the current method is not allowed + */ + public function match($pathinfo) + { + $parameters = array(); + $defaults = array(); + $allow = array(); + $match = false; + + foreach ($_SERVER as $key => $value) { + $name = $key; + + if (0 === strpos($name, 'REDIRECT_')) { + $name = substr($name, 9); + } + + if (0 === strpos($name, '_ROUTING_DEFAULTS_')) { + $name = substr($name, 18); + $defaults[$name] = $value; + } elseif (0 === strpos($name, '_ROUTING_')) { + $name = substr($name, 9); + if ('_route' == $name) { + $match = true; + $parameters[$name] = $value; + } elseif (0 === strpos($name, '_allow_')) { + $allow[] = substr($name, 7); + } else { + $parameters[$name] = $value; + } + } else { + continue; + } + + unset($_SERVER[$key]); + } + + if ($match) { + return $this->mergeDefaults($parameters, $defaults); + } elseif (0 < count($allow)) { + throw new MethodNotAllowedException($allow); + } else { + return parent::match($pathinfo); + } + } +} diff --git a/3rdparty/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/ApacheMatcherDumper.php b/3rdparty/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/ApacheMatcherDumper.php new file mode 100644 index 0000000000..4f03b8d393 --- /dev/null +++ b/3rdparty/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/ApacheMatcherDumper.php @@ -0,0 +1,155 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Matcher\Dumper; + +/** + * Dumps a set of Apache mod_rewrite rules. + * + * @author Fabien Potencier + * @author Kris Wallsmith + */ +class ApacheMatcherDumper extends MatcherDumper +{ + /** + * Dumps a set of Apache mod_rewrite rules. + * + * Available options: + * + * * script_name: The script name (app.php by default) + * * base_uri: The base URI ("" by default) + * + * @param array $options An array of options + * + * @return string A string to be used as Apache rewrite rules + * + * @throws \LogicException When the route regex is invalid + */ + public function dump(array $options = array()) + { + $options = array_merge(array( + 'script_name' => 'app.php', + 'base_uri' => '', + ), $options); + + $options['script_name'] = self::escape($options['script_name'], ' ', '\\'); + + $rules = array("# skip \"real\" requests\nRewriteCond %{REQUEST_FILENAME} -f\nRewriteRule .* - [QSA,L]"); + $methodVars = array(); + + foreach ($this->getRoutes()->all() as $name => $route) { + $compiledRoute = $route->compile(); + + // prepare the apache regex + $regex = $compiledRoute->getRegex(); + $delimiter = $regex[0]; + $regexPatternEnd = strrpos($regex, $delimiter); + if (strlen($regex) < 2 || 0 === $regexPatternEnd) { + throw new \LogicException('The "%s" route regex "%s" is invalid', $name, $regex); + } + $regex = preg_replace('/\?P<.+?>/', '', substr($regex, 1, $regexPatternEnd - 1)); + $regex = '^'.self::escape(preg_quote($options['base_uri']).substr($regex, 1), ' ', '\\'); + + $hasTrailingSlash = '/$' == substr($regex, -2) && '^/$' != $regex; + + $variables = array('E=_ROUTING__route:'.$name); + foreach ($compiledRoute->getVariables() as $i => $variable) { + $variables[] = 'E=_ROUTING_'.$variable.':%'.($i + 1); + } + foreach ($route->getDefaults() as $key => $value) { + $variables[] = 'E=_ROUTING_DEFAULTS_'.$key.':'.strtr($value, array( + ':' => '\\:', + '=' => '\\=', + '\\' => '\\\\', + ' ' => '\\ ', + )); + } + $variables = implode(',', $variables); + + $rule = array("# $name"); + + // method mismatch + if ($req = $route->getRequirement('_method')) { + $methods = explode('|', strtoupper($req)); + // GET and HEAD are equivalent + if (in_array('GET', $methods) && !in_array('HEAD', $methods)) { + $methods[] = 'HEAD'; + } + $allow = array(); + foreach ($methods as $method) { + $methodVars[] = $method; + $allow[] = 'E=_ROUTING__allow_'.$method.':1'; + } + + $rule[] = "RewriteCond %{REQUEST_URI} $regex"; + $rule[] = sprintf("RewriteCond %%{REQUEST_METHOD} !^(%s)$ [NC]", implode('|', $methods)); + $rule[] = sprintf('RewriteRule .* - [S=%d,%s]', $hasTrailingSlash ? 2 : 1, implode(',', $allow)); + } + + // redirect with trailing slash appended + if ($hasTrailingSlash) { + $rule[] = 'RewriteCond %{REQUEST_URI} '.substr($regex, 0, -2).'$'; + $rule[] = 'RewriteRule .* $0/ [QSA,L,R=301]'; + } + + // the main rule + $rule[] = "RewriteCond %{REQUEST_URI} $regex"; + $rule[] = "RewriteRule .* {$options['script_name']} [QSA,L,$variables]"; + + $rules[] = implode("\n", $rule); + } + + if (0 < count($methodVars)) { + $rule = array('# 405 Method Not Allowed'); + $methodVars = array_values(array_unique($methodVars)); + foreach ($methodVars as $i => $methodVar) { + $rule[] = sprintf('RewriteCond %%{_ROUTING__allow_%s} !-z%s', $methodVar, isset($methodVars[$i + 1]) ? ' [OR]' : ''); + } + $rule[] = sprintf('RewriteRule .* %s [QSA,L]', $options['script_name']); + + $rules[] = implode("\n", $rule); + } + + return implode("\n\n", $rules)."\n"; + } + + /** + * Escapes a string. + * + * @param string $string The string to be escaped + * @param string $char The character to be escaped + * @param string $with The character to be used for escaping + * + * @return string The escaped string + */ + private static function escape($string, $char, $with) + { + $escaped = false; + $output = ''; + foreach (str_split($string) as $symbol) { + if ($escaped) { + $output .= $symbol; + $escaped = false; + continue; + } + if ($symbol === $char) { + $output .= $with.$char; + continue; + } + if ($symbol === $with) { + $escaped = true; + } + $output .= $symbol; + } + + return $output; + } +} diff --git a/3rdparty/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/MatcherDumper.php b/3rdparty/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/MatcherDumper.php new file mode 100644 index 0000000000..423368b57e --- /dev/null +++ b/3rdparty/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/MatcherDumper.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Matcher\Dumper; + +use Symfony\Component\Routing\RouteCollection; + +/** + * MatcherDumper is the abstract class for all built-in matcher dumpers. + * + * @author Fabien Potencier + */ +abstract class MatcherDumper implements MatcherDumperInterface +{ + private $routes; + + /** + * Constructor. + * + * @param RouteCollection $routes The RouteCollection to dump + */ + public function __construct(RouteCollection $routes) + { + $this->routes = $routes; + } + + /** + * Gets the routes to dump. + * + * @return RouteCollection A RouteCollection instance + */ + public function getRoutes() + { + return $this->routes; + } +} diff --git a/3rdparty/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/MatcherDumperInterface.php b/3rdparty/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/MatcherDumperInterface.php new file mode 100644 index 0000000000..fe09e067d7 --- /dev/null +++ b/3rdparty/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/MatcherDumperInterface.php @@ -0,0 +1,41 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Matcher\Dumper; + +/** + * MatcherDumperInterface is the interface that all matcher dumper classes must implement. + * + * @author Fabien Potencier + */ +interface MatcherDumperInterface +{ + /** + * Dumps a set of routes to a PHP class. + * + * Available options: + * + * * class: The class name + * * base_class: The base class name + * + * @param array $options An array of options + * + * @return string A PHP class representing the matcher class + */ + public function dump(array $options = array()); + + /** + * Gets the routes to match. + * + * @return RouteCollection A RouteCollection instance + */ + public function getRoutes(); +} diff --git a/3rdparty/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php b/3rdparty/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php new file mode 100644 index 0000000000..fdaad513a1 --- /dev/null +++ b/3rdparty/symfony/routing/Symfony/Component/Routing/Matcher/Dumper/PhpMatcherDumper.php @@ -0,0 +1,293 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Matcher\Dumper; + +use Symfony\Component\Routing\Route; +use Symfony\Component\Routing\RouteCollection; + +/** + * PhpMatcherDumper creates a PHP class able to match URLs for a given set of routes. + * + * @author Fabien Potencier + */ +class PhpMatcherDumper extends MatcherDumper +{ + /** + * Dumps a set of routes to a PHP class. + * + * Available options: + * + * * class: The class name + * * base_class: The base class name + * + * @param array $options An array of options + * + * @return string A PHP class representing the matcher class + */ + public function dump(array $options = array()) + { + $options = array_merge(array( + 'class' => 'ProjectUrlMatcher', + 'base_class' => 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher', + ), $options); + + // trailing slash support is only enabled if we know how to redirect the user + $interfaces = class_implements($options['base_class']); + $supportsRedirections = isset($interfaces['Symfony\Component\Routing\Matcher\RedirectableUrlMatcherInterface']); + + return + $this->startClass($options['class'], $options['base_class']). + $this->addConstructor(). + $this->addMatcher($supportsRedirections). + $this->endClass() + ; + } + + private function addMatcher($supportsRedirections) + { + // we need to deep clone the routes as we will modify the structure to optimize the dump + $code = implode("\n", $this->compileRoutes(clone $this->getRoutes(), $supportsRedirections)); + + return <<getIterator(); + $keys = array_keys($routeIterator->getArrayCopy()); + $keysCount = count($keys); + + $i = 0; + foreach ($routeIterator as $name => $route) { + $i++; + + if ($route instanceof RouteCollection) { + $prefix = $route->getPrefix(); + $optimizable = $prefix && count($route->all()) > 1 && false === strpos($route->getPrefix(), '{'); + $indent = ''; + if ($optimizable) { + for ($j = $i; $j < $keysCount; $j++) { + if ($keys[$j] === null) { + continue; + } + + $testRoute = $routeIterator->offsetGet($keys[$j]); + $isCollection = ($testRoute instanceof RouteCollection); + + $testPrefix = $isCollection ? $testRoute->getPrefix() : $testRoute->getPattern(); + + if (0 === strpos($testPrefix, $prefix)) { + $routeIterator->offsetUnset($keys[$j]); + + if ($isCollection) { + $route->addCollection($testRoute); + } else { + $route->add($keys[$j], $testRoute); + } + + $i++; + $keys[$j] = null; + } + } + + if ($prefix !== $parentPrefix) { + $code[] = sprintf(" if (0 === strpos(\$pathinfo, %s)) {", var_export($prefix, true)); + $indent = ' '; + } + } + + foreach ($this->compileRoutes($route, $supportsRedirections, $prefix) as $line) { + foreach (explode("\n", $line) as $l) { + if ($l) { + $code[] = $indent.$l; + } else { + $code[] = $l; + } + } + } + + if ($optimizable && $prefix !== $parentPrefix) { + $code[] = " }\n"; + } + } else { + foreach ($this->compileRoute($route, $name, $supportsRedirections, $parentPrefix) as $line) { + $code[] = $line; + } + } + } + + return $code; + } + + private function compileRoute(Route $route, $name, $supportsRedirections, $parentPrefix = null) + { + $code = array(); + $compiledRoute = $route->compile(); + $conditions = array(); + $hasTrailingSlash = false; + $matches = false; + if (!count($compiledRoute->getVariables()) && false !== preg_match('#^(.)\^(?P.*?)\$\1#', $compiledRoute->getRegex(), $m)) { + if ($supportsRedirections && substr($m['url'], -1) === '/') { + $conditions[] = sprintf("rtrim(\$pathinfo, '/') === %s", var_export(rtrim(str_replace('\\', '', $m['url']), '/'), true)); + $hasTrailingSlash = true; + } else { + $conditions[] = sprintf("\$pathinfo === %s", var_export(str_replace('\\', '', $m['url']), true)); + } + } else { + if ($compiledRoute->getStaticPrefix() && $compiledRoute->getStaticPrefix() != $parentPrefix) { + $conditions[] = sprintf("0 === strpos(\$pathinfo, %s)", var_export($compiledRoute->getStaticPrefix(), true)); + } + + $regex = $compiledRoute->getRegex(); + if ($supportsRedirections && $pos = strpos($regex, '/$')) { + $regex = substr($regex, 0, $pos).'/?$'.substr($regex, $pos + 2); + $hasTrailingSlash = true; + } + $conditions[] = sprintf("preg_match(%s, \$pathinfo, \$matches)", var_export($regex, true)); + + $matches = true; + } + + $conditions = implode(' && ', $conditions); + + $gotoname = 'not_'.preg_replace('/[^A-Za-z0-9_]/', '', $name); + + $code[] = <<getRequirement('_method')) { + $methods = explode('|', strtoupper($req)); + // GET and HEAD are equivalent + if (in_array('GET', $methods) && !in_array('HEAD', $methods)) { + $methods[] = 'HEAD'; + } + if (1 === count($methods)) { + $code[] = <<context->getMethod() != '$methods[0]') { + \$allow[] = '$methods[0]'; + goto $gotoname; + } +EOF; + } else { + $methods = implode('\', \'', $methods); + $code[] = <<context->getMethod(), array('$methods'))) { + \$allow = array_merge(\$allow, array('$methods')); + goto $gotoname; + } +EOF; + } + } + + if ($hasTrailingSlash) { + $code[] = sprintf(<<redirect(\$pathinfo.'/', '%s'); + } +EOF + , $name); + } + + if ($scheme = $route->getRequirement('_scheme')) { + if (!$supportsRedirections) { + throw new \LogicException('The "_scheme" requirement is only supported for route dumper that implements RedirectableUrlMatcherInterface.'); + } + + $code[] = sprintf(<<context->getScheme() !== '$scheme') { + return \$this->redirect(\$pathinfo, '%s', '$scheme'); + } +EOF + , $name); + } + + // optimize parameters array + if (true === $matches && $compiledRoute->getDefaults()) { + $code[] = sprintf(" return array_merge(\$this->mergeDefaults(\$matches, %s), array('_route' => '%s'));" + , str_replace("\n", '', var_export($compiledRoute->getDefaults(), true)), $name); + } elseif (true === $matches) { + $code[] = sprintf(" \$matches['_route'] = '%s';", $name); + $code[] = sprintf(" return \$matches;", $name); + } elseif ($compiledRoute->getDefaults()) { + $code[] = sprintf(' return %s;', str_replace("\n", '', var_export(array_merge($compiledRoute->getDefaults(), array('_route' => $name)), true))); + } else { + $code[] = sprintf(" return array('_route' => '%s');", $name); + } + $code[] = " }"; + + if ($req) { + $code[] = " $gotoname:"; + } + + $code[] = ''; + + return $code; + } + + private function startClass($class, $baseClass) + { + return <<context = \$context; + } + +EOF; + } + + private function endClass() + { + return << + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Matcher; + +use Symfony\Component\Routing\Exception\ResourceNotFoundException; + +/** + * @author Fabien Potencier + * + * @api + */ +abstract class RedirectableUrlMatcher extends UrlMatcher implements RedirectableUrlMatcherInterface +{ + private $trailingSlashTest = false; + + /** + * @see UrlMatcher::match() + * + * @api + */ + public function match($pathinfo) + { + try { + $parameters = parent::match($pathinfo); + } catch (ResourceNotFoundException $e) { + if ('/' === substr($pathinfo, -1)) { + throw $e; + } + + // try with a / at the end + $this->trailingSlashTest = true; + + return $this->match($pathinfo.'/'); + } + + if ($this->trailingSlashTest) { + $this->trailingSlashTest = false; + + return $this->redirect($pathinfo, null); + } + + return $parameters; + } +} diff --git a/3rdparty/symfony/routing/Symfony/Component/Routing/Matcher/RedirectableUrlMatcherInterface.php b/3rdparty/symfony/routing/Symfony/Component/Routing/Matcher/RedirectableUrlMatcherInterface.php new file mode 100644 index 0000000000..929ae9cc78 --- /dev/null +++ b/3rdparty/symfony/routing/Symfony/Component/Routing/Matcher/RedirectableUrlMatcherInterface.php @@ -0,0 +1,35 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Matcher; + +/** + * RedirectableUrlMatcherInterface knows how to redirect the user. + * + * @author Fabien Potencier + * + * @api + */ +interface RedirectableUrlMatcherInterface +{ + /** + * Redirects the user to another URL. + * + * @param string $path The path info to redirect to. + * @param string $route The route that matched + * @param string $scheme The URL scheme (null to keep the current one) + * + * @return array An array of parameters + * + * @api + */ + public function redirect($path, $route, $scheme = null); +} diff --git a/3rdparty/symfony/routing/Symfony/Component/Routing/Matcher/UrlMatcher.php b/3rdparty/symfony/routing/Symfony/Component/Routing/Matcher/UrlMatcher.php new file mode 100644 index 0000000000..5ff8070ea0 --- /dev/null +++ b/3rdparty/symfony/routing/Symfony/Component/Routing/Matcher/UrlMatcher.php @@ -0,0 +1,151 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Matcher; + +use Symfony\Component\Routing\Exception\MethodNotAllowedException; +use Symfony\Component\Routing\Exception\ResourceNotFoundException; +use Symfony\Component\Routing\RouteCollection; +use Symfony\Component\Routing\RequestContext; + +/** + * UrlMatcher matches URL based on a set of routes. + * + * @author Fabien Potencier + * + * @api + */ +class UrlMatcher implements UrlMatcherInterface +{ + protected $context; + protected $allow; + + private $routes; + + /** + * Constructor. + * + * @param RouteCollection $routes A RouteCollection instance + * @param RequestContext $context The context + * + * @api + */ + public function __construct(RouteCollection $routes, RequestContext $context) + { + $this->routes = $routes; + $this->context = $context; + } + + /** + * Sets the request context. + * + * @param RequestContext $context The context + * + * @api + */ + public function setContext(RequestContext $context) + { + $this->context = $context; + } + + /** + * Gets the request context. + * + * @return RequestContext The context + */ + public function getContext() + { + return $this->context; + } + + /** + * Tries to match a URL with a set of routes. + * + * @param string $pathinfo The path info to be parsed + * + * @return array An array of parameters + * + * @throws ResourceNotFoundException If the resource could not be found + * @throws MethodNotAllowedException If the resource was found but the request method is not allowed + * + * @api + */ + public function match($pathinfo) + { + $this->allow = array(); + + if ($ret = $this->matchCollection($pathinfo, $this->routes)) { + return $ret; + } + + throw 0 < count($this->allow) + ? new MethodNotAllowedException(array_unique(array_map('strtoupper', $this->allow))) + : new ResourceNotFoundException(); + } + + protected function matchCollection($pathinfo, RouteCollection $routes) + { + $pathinfo = urldecode($pathinfo); + + foreach ($routes as $name => $route) { + if ($route instanceof RouteCollection) { + if (false === strpos($route->getPrefix(), '{') && $route->getPrefix() !== substr($pathinfo, 0, strlen($route->getPrefix()))) { + continue; + } + + if (!$ret = $this->matchCollection($pathinfo, $route)) { + continue; + } + + return $ret; + } + + $compiledRoute = $route->compile(); + + // check the static prefix of the URL first. Only use the more expensive preg_match when it matches + if ('' !== $compiledRoute->getStaticPrefix() && 0 !== strpos($pathinfo, $compiledRoute->getStaticPrefix())) { + continue; + } + + if (!preg_match($compiledRoute->getRegex(), $pathinfo, $matches)) { + continue; + } + + // check HTTP method requirement + if ($req = $route->getRequirement('_method')) { + // HEAD and GET are equivalent as per RFC + if ('HEAD' === $method = $this->context->getMethod()) { + $method = 'GET'; + } + + if (!in_array($method, $req = explode('|', strtoupper($req)))) { + $this->allow = array_merge($this->allow, $req); + + continue; + } + } + + return array_merge($this->mergeDefaults($matches, $route->getDefaults()), array('_route' => $name)); + } + } + + protected function mergeDefaults($params, $defaults) + { + $parameters = $defaults; + foreach ($params as $key => $value) { + if (!is_int($key)) { + $parameters[$key] = rawurldecode($value); + } + } + + return $parameters; + } +} diff --git a/3rdparty/symfony/routing/Symfony/Component/Routing/Matcher/UrlMatcherInterface.php b/3rdparty/symfony/routing/Symfony/Component/Routing/Matcher/UrlMatcherInterface.php new file mode 100644 index 0000000000..5823d3201b --- /dev/null +++ b/3rdparty/symfony/routing/Symfony/Component/Routing/Matcher/UrlMatcherInterface.php @@ -0,0 +1,38 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing\Matcher; + +use Symfony\Component\Routing\RequestContextAwareInterface; + +/** + * UrlMatcherInterface is the interface that all URL matcher classes must implement. + * + * @author Fabien Potencier + * + * @api + */ +interface UrlMatcherInterface extends RequestContextAwareInterface +{ + /** + * Tries to match a URL with a set of routes. + * + * @param string $pathinfo The path info to be parsed + * + * @return array An array of parameters + * + * @throws ResourceNotFoundException If the resource could not be found + * @throws MethodNotAllowedException If the resource was found but the request method is not allowed + * + * @api + */ + public function match($pathinfo); +} diff --git a/3rdparty/symfony/routing/Symfony/Component/Routing/README.md b/3rdparty/symfony/routing/Symfony/Component/Routing/README.md new file mode 100644 index 0000000000..eb72334d2e --- /dev/null +++ b/3rdparty/symfony/routing/Symfony/Component/Routing/README.md @@ -0,0 +1,32 @@ +Routing Component +================= + +Routing associates a request with the code that will convert it to a response. + +The example below demonstrates how you can set up a fully working routing +system: + + use Symfony\Component\HttpFoundation\Request; + use Symfony\Component\Routing\Matcher\UrlMatcher; + use Symfony\Component\Routing\RequestContext; + use Symfony\Component\Routing\RouteCollection; + use Symfony\Component\Routing\Route; + + $routes = new RouteCollection(); + $routes->add('hello', new Route('/hello', array('controller' => 'foo'))); + + $context = new RequestContext(); + + // this is optional and can be done without a Request instance + $context->fromRequest(Request::createFromGlobals()); + + $matcher = new UrlMatcher($routes, $context); + + $parameters = $matcher->match('/hello'); + +Resources +--------- + +Unit tests: + +https://github.com/symfony/symfony/tree/master/tests/Symfony/Tests/Component/Routing diff --git a/3rdparty/symfony/routing/Symfony/Component/Routing/RequestContext.php b/3rdparty/symfony/routing/Symfony/Component/Routing/RequestContext.php new file mode 100644 index 0000000000..fef85b6bb3 --- /dev/null +++ b/3rdparty/symfony/routing/Symfony/Component/Routing/RequestContext.php @@ -0,0 +1,250 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing; + +/** + * Holds information about the current request. + * + * @author Fabien Potencier + * + * @api + */ +class RequestContext +{ + private $baseUrl; + private $method; + private $host; + private $scheme; + private $httpPort; + private $httpsPort; + private $parameters; + + /** + * Constructor. + * + * @param string $baseUrl The base URL + * @param string $method The HTTP method + * @param string $host The HTTP host name + * @param string $scheme The HTTP scheme + * @param integer $httpPort The HTTP port + * @param integer $httpsPort The HTTPS port + * + * @api + */ + public function __construct($baseUrl = '', $method = 'GET', $host = 'localhost', $scheme = 'http', $httpPort = 80, $httpsPort = 443) + { + $this->baseUrl = $baseUrl; + $this->method = strtoupper($method); + $this->host = $host; + $this->scheme = strtolower($scheme); + $this->httpPort = $httpPort; + $this->httpsPort = $httpsPort; + $this->parameters = array(); + } + + /** + * Gets the base URL. + * + * @return string The base URL + */ + public function getBaseUrl() + { + return $this->baseUrl; + } + + /** + * Sets the base URL. + * + * @param string $baseUrl The base URL + * + * @api + */ + public function setBaseUrl($baseUrl) + { + $this->baseUrl = $baseUrl; + } + + /** + * Gets the HTTP method. + * + * The method is always an uppercased string. + * + * @return string The HTTP method + */ + public function getMethod() + { + return $this->method; + } + + /** + * Sets the HTTP method. + * + * @param string $method The HTTP method + * + * @api + */ + public function setMethod($method) + { + $this->method = strtoupper($method); + } + + /** + * Gets the HTTP host. + * + * @return string The HTTP host + */ + public function getHost() + { + return $this->host; + } + + /** + * Sets the HTTP host. + * + * @param string $host The HTTP host + * + * @api + */ + public function setHost($host) + { + $this->host = $host; + } + + /** + * Gets the HTTP scheme. + * + * @return string The HTTP scheme + */ + public function getScheme() + { + return $this->scheme; + } + + /** + * Sets the HTTP scheme. + * + * @param string $scheme The HTTP scheme + * + * @api + */ + public function setScheme($scheme) + { + $this->scheme = strtolower($scheme); + } + + /** + * Gets the HTTP port. + * + * @return string The HTTP port + */ + public function getHttpPort() + { + return $this->httpPort; + } + + /** + * Sets the HTTP port. + * + * @param string $httpPort The HTTP port + * + * @api + */ + public function setHttpPort($httpPort) + { + $this->httpPort = $httpPort; + } + + /** + * Gets the HTTPS port. + * + * @return string The HTTPS port + */ + public function getHttpsPort() + { + return $this->httpsPort; + } + + /** + * Sets the HTTPS port. + * + * @param string $httpsPort The HTTPS port + * + * @api + */ + public function setHttpsPort($httpsPort) + { + $this->httpsPort = $httpsPort; + } + + /** + * Returns the parameters. + * + * @return array The parameters + */ + public function getParameters() + { + return $this->parameters; + } + + /** + * Sets the parameters. + * + * This method implements a fluent interface. + * + * @param array $parameters The parameters + * + * @return Route The current Route instance + */ + public function setParameters(array $parameters) + { + $this->parameters = $parameters; + + return $this; + } + + /** + * Gets a parameter value. + * + * @param string $name A parameter name + * + * @return mixed The parameter value + */ + public function getParameter($name) + { + return isset($this->parameters[$name]) ? $this->parameters[$name] : null; + } + + /** + * Checks if a parameter value is set for the given parameter. + * + * @param string $name A parameter name + * + * @return Boolean true if the parameter value is set, false otherwise + */ + public function hasParameter($name) + { + return array_key_exists($name, $this->parameters); + } + + /** + * Sets a parameter value. + * + * @param string $name A parameter name + * @param mixed $parameter The parameter value + * + * @api + */ + public function setParameter($name, $parameter) + { + $this->parameters[$name] = $parameter; + } +} diff --git a/3rdparty/symfony/routing/Symfony/Component/Routing/RequestContextAwareInterface.php b/3rdparty/symfony/routing/Symfony/Component/Routing/RequestContextAwareInterface.php new file mode 100644 index 0000000000..38443a88b7 --- /dev/null +++ b/3rdparty/symfony/routing/Symfony/Component/Routing/RequestContextAwareInterface.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing; + +/** + * @api + */ +interface RequestContextAwareInterface +{ + /** + * Sets the request context. + * + * @param RequestContext $context The context + * + * @api + */ + public function setContext(RequestContext $context); +} diff --git a/3rdparty/symfony/routing/Symfony/Component/Routing/Route.php b/3rdparty/symfony/routing/Symfony/Component/Routing/Route.php new file mode 100644 index 0000000000..548568334d --- /dev/null +++ b/3rdparty/symfony/routing/Symfony/Component/Routing/Route.php @@ -0,0 +1,312 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing; + +/** + * A Route describes a route and its parameters. + * + * @author Fabien Potencier + * + * @api + */ +class Route +{ + private $pattern; + private $defaults; + private $requirements; + private $options; + private $compiled; + + private static $compilers = array(); + + /** + * Constructor. + * + * Available options: + * + * * compiler_class: A class name able to compile this route instance (RouteCompiler by default) + * + * @param string $pattern The pattern to match + * @param array $defaults An array of default parameter values + * @param array $requirements An array of requirements for parameters (regexes) + * @param array $options An array of options + * + * @api + */ + public function __construct($pattern, array $defaults = array(), array $requirements = array(), array $options = array()) + { + $this->setPattern($pattern); + $this->setDefaults($defaults); + $this->setRequirements($requirements); + $this->setOptions($options); + } + + public function __clone() + { + $this->compiled = null; + } + + /** + * Returns the pattern. + * + * @return string The pattern + */ + public function getPattern() + { + return $this->pattern; + } + + /** + * Sets the pattern. + * + * This method implements a fluent interface. + * + * @param string $pattern The pattern + * + * @return Route The current Route instance + */ + public function setPattern($pattern) + { + $this->pattern = trim($pattern); + + // a route must start with a slash + if (empty($this->pattern) || '/' !== $this->pattern[0]) { + $this->pattern = '/'.$this->pattern; + } + + return $this; + } + + /** + * Returns the options. + * + * @return array The options + */ + public function getOptions() + { + return $this->options; + } + + /** + * Sets the options. + * + * This method implements a fluent interface. + * + * @param array $options The options + * + * @return Route The current Route instance + */ + public function setOptions(array $options) + { + $this->options = array_merge(array( + 'compiler_class' => 'Symfony\\Component\\Routing\\RouteCompiler', + ), $options); + + return $this; + } + + /** + * Sets an option value. + * + * This method implements a fluent interface. + * + * @param string $name An option name + * @param mixed $value The option value + * + * @return Route The current Route instance + * + * @api + */ + public function setOption($name, $value) + { + $this->options[$name] = $value; + + return $this; + } + + /** + * Get an option value. + * + * @param string $name An option name + * + * @return mixed The option value + */ + public function getOption($name) + { + return isset($this->options[$name]) ? $this->options[$name] : null; + } + + /** + * Returns the defaults. + * + * @return array The defaults + */ + public function getDefaults() + { + return $this->defaults; + } + + /** + * Sets the defaults. + * + * This method implements a fluent interface. + * + * @param array $defaults The defaults + * + * @return Route The current Route instance + */ + public function setDefaults(array $defaults) + { + $this->defaults = array(); + foreach ($defaults as $name => $default) { + $this->defaults[(string) $name] = $default; + } + + return $this; + } + + /** + * Gets a default value. + * + * @param string $name A variable name + * + * @return mixed The default value + */ + public function getDefault($name) + { + return isset($this->defaults[$name]) ? $this->defaults[$name] : null; + } + + /** + * Checks if a default value is set for the given variable. + * + * @param string $name A variable name + * + * @return Boolean true if the default value is set, false otherwise + */ + public function hasDefault($name) + { + return array_key_exists($name, $this->defaults); + } + + /** + * Sets a default value. + * + * @param string $name A variable name + * @param mixed $default The default value + * + * @return Route The current Route instance + * + * @api + */ + public function setDefault($name, $default) + { + $this->defaults[(string) $name] = $default; + + return $this; + } + + /** + * Returns the requirements. + * + * @return array The requirements + */ + public function getRequirements() + { + return $this->requirements; + } + + /** + * Sets the requirements. + * + * This method implements a fluent interface. + * + * @param array $requirements The requirements + * + * @return Route The current Route instance + */ + public function setRequirements(array $requirements) + { + $this->requirements = array(); + foreach ($requirements as $key => $regex) { + $this->requirements[$key] = $this->sanitizeRequirement($key, $regex); + } + + return $this; + } + + /** + * Returns the requirement for the given key. + * + * @param string $key The key + * + * @return string The regex + */ + public function getRequirement($key) + { + return isset($this->requirements[$key]) ? $this->requirements[$key] : null; + } + + /** + * Sets a requirement for the given key. + * + * @param string $key The key + * @param string $regex The regex + * + * @return Route The current Route instance + * + * @api + */ + public function setRequirement($key, $regex) + { + $this->requirements[$key] = $this->sanitizeRequirement($key, $regex); + + return $this; + } + + /** + * Compiles the route. + * + * @return CompiledRoute A CompiledRoute instance + */ + public function compile() + { + if (null !== $this->compiled) { + return $this->compiled; + } + + $class = $this->getOption('compiler_class'); + + if (!isset(self::$compilers[$class])) { + self::$compilers[$class] = new $class; + } + + return $this->compiled = self::$compilers[$class]->compile($this); + } + + private function sanitizeRequirement($key, $regex) + { + if (is_array($regex)) { + throw new \InvalidArgumentException(sprintf('Routing requirements must be a string, array given for "%s"', $key)); + } + + if ('^' == $regex[0]) { + $regex = substr($regex, 1); + } + + if ('$' == substr($regex, -1)) { + $regex = substr($regex, 0, -1); + } + + return $regex; + } +} diff --git a/3rdparty/symfony/routing/Symfony/Component/Routing/RouteCollection.php b/3rdparty/symfony/routing/Symfony/Component/Routing/RouteCollection.php new file mode 100644 index 0000000000..b3289d337c --- /dev/null +++ b/3rdparty/symfony/routing/Symfony/Component/Routing/RouteCollection.php @@ -0,0 +1,259 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing; + +use Symfony\Component\Config\Resource\ResourceInterface; + +/** + * A RouteCollection represents a set of Route instances. + * + * When adding a route, it overrides existing routes with the + * same name defined in the instance or its children and parents. + * + * @author Fabien Potencier + * + * @api + */ +class RouteCollection implements \IteratorAggregate +{ + private $routes; + private $resources; + private $prefix; + private $parent; + + /** + * Constructor. + * + * @api + */ + public function __construct() + { + $this->routes = array(); + $this->resources = array(); + $this->prefix = ''; + } + + public function __clone() + { + foreach ($this->routes as $name => $route) { + $this->routes[$name] = clone $route; + if ($route instanceof RouteCollection) { + $this->routes[$name]->setParent($this); + } + } + } + + /** + * Gets the parent RouteCollection. + * + * @return RouteCollection The parent RouteCollection + */ + public function getParent() + { + return $this->parent; + } + + /** + * Sets the parent RouteCollection. + * + * @param RouteCollection $parent The parent RouteCollection + */ + public function setParent(RouteCollection $parent) + { + $this->parent = $parent; + } + + /** + * Gets the current RouteCollection as an Iterator. + * + * @return \ArrayIterator An \ArrayIterator interface + */ + public function getIterator() + { + return new \ArrayIterator($this->routes); + } + + /** + * Adds a route. + * + * @param string $name The route name + * @param Route $route A Route instance + * + * @throws \InvalidArgumentException When route name contains non valid characters + * + * @api + */ + public function add($name, Route $route) + { + if (!preg_match('/^[a-z0-9A-Z_.]+$/', $name)) { + throw new \InvalidArgumentException(sprintf('The provided route name "%s" contains non valid characters. A route name must only contain digits (0-9), letters (a-z and A-Z), underscores (_) and dots (.).', $name)); + } + + $parent = $this; + while ($parent->getParent()) { + $parent = $parent->getParent(); + } + + if ($parent) { + $parent->remove($name); + } + + $this->routes[$name] = $route; + } + + /** + * Returns the array of routes. + * + * @return array An array of routes + */ + public function all() + { + $routes = array(); + foreach ($this->routes as $name => $route) { + if ($route instanceof RouteCollection) { + $routes = array_merge($routes, $route->all()); + } else { + $routes[$name] = $route; + } + } + + return $routes; + } + + /** + * Gets a route by name. + * + * @param string $name The route name + * + * @return Route $route A Route instance + */ + public function get($name) + { + // get the latest defined route + foreach (array_reverse($this->routes) as $routes) { + if (!$routes instanceof RouteCollection) { + continue; + } + + if (null !== $route = $routes->get($name)) { + return $route; + } + } + + if (isset($this->routes[$name])) { + return $this->routes[$name]; + } + } + + /** + * Removes a route by name. + * + * @param string $name The route name + */ + public function remove($name) + { + if (isset($this->routes[$name])) { + unset($this->routes[$name]); + } + + foreach ($this->routes as $routes) { + if ($routes instanceof RouteCollection) { + $routes->remove($name); + } + } + } + + /** + * Adds a route collection to the current set of routes (at the end of the current set). + * + * @param RouteCollection $collection A RouteCollection instance + * @param string $prefix An optional prefix to add before each pattern of the route collection + * + * @api + */ + public function addCollection(RouteCollection $collection, $prefix = '') + { + $collection->setParent($this); + $collection->addPrefix($prefix); + + // remove all routes with the same name in all existing collections + foreach (array_keys($collection->all()) as $name) { + $this->remove($name); + } + + $this->routes[] = $collection; + } + + /** + * Adds a prefix to all routes in the current set. + * + * @param string $prefix An optional prefix to add before each pattern of the route collection + * + * @api + */ + public function addPrefix($prefix) + { + // a prefix must not end with a slash + $prefix = rtrim($prefix, '/'); + + if (!$prefix) { + return; + } + + // a prefix must start with a slash + if ('/' !== $prefix[0]) { + $prefix = '/'.$prefix; + } + + $this->prefix = $prefix.$this->prefix; + + foreach ($this->routes as $name => $route) { + if ($route instanceof RouteCollection) { + $route->addPrefix($prefix); + } else { + $route->setPattern($prefix.$route->getPattern()); + } + } + } + + public function getPrefix() + { + return $this->prefix; + } + + /** + * Returns an array of resources loaded to build this collection. + * + * @return ResourceInterface[] An array of resources + */ + public function getResources() + { + $resources = $this->resources; + foreach ($this as $routes) { + if ($routes instanceof RouteCollection) { + $resources = array_merge($resources, $routes->getResources()); + } + } + + return array_unique($resources); + } + + /** + * Adds a resource for this collection. + * + * @param ResourceInterface $resource A resource instance + */ + public function addResource(ResourceInterface $resource) + { + $this->resources[] = $resource; + } +} diff --git a/3rdparty/symfony/routing/Symfony/Component/Routing/RouteCompiler.php b/3rdparty/symfony/routing/Symfony/Component/Routing/RouteCompiler.php new file mode 100644 index 0000000000..72ececc5ca --- /dev/null +++ b/3rdparty/symfony/routing/Symfony/Component/Routing/RouteCompiler.php @@ -0,0 +1,128 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing; + +/** + * RouteCompiler compiles Route instances to CompiledRoute instances. + * + * @author Fabien Potencier + */ +class RouteCompiler implements RouteCompilerInterface +{ + /** + * Compiles the current route instance. + * + * @param Route $route A Route instance + * + * @return CompiledRoute A CompiledRoute instance + */ + public function compile(Route $route) + { + $pattern = $route->getPattern(); + $len = strlen($pattern); + $tokens = array(); + $variables = array(); + $pos = 0; + preg_match_all('#.\{([\w\d_]+)\}#', $pattern, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER); + foreach ($matches as $match) { + if ($text = substr($pattern, $pos, $match[0][1] - $pos)) { + $tokens[] = array('text', $text); + } + $seps = array($pattern[$pos]); + $pos = $match[0][1] + strlen($match[0][0]); + $var = $match[1][0]; + + if ($req = $route->getRequirement($var)) { + $regexp = $req; + } else { + if ($pos !== $len) { + $seps[] = $pattern[$pos]; + } + $regexp = sprintf('[^%s]+?', preg_quote(implode('', array_unique($seps)), '#')); + } + + $tokens[] = array('variable', $match[0][0][0], $regexp, $var); + + if (in_array($var, $variables)) { + throw new \LogicException(sprintf('Route pattern "%s" cannot reference variable name "%s" more than once.', $route->getPattern(), $var)); + } + + $variables[] = $var; + } + + if ($pos < $len) { + $tokens[] = array('text', substr($pattern, $pos)); + } + + // find the first optional token + $firstOptional = INF; + for ($i = count($tokens) - 1; $i >= 0; $i--) { + $token = $tokens[$i]; + if ('variable' === $token[0] && $route->hasDefault($token[3])) { + $firstOptional = $i; + } else { + break; + } + } + + // compute the matching regexp + $regexp = ''; + for ($i = 0, $nbToken = count($tokens); $i < $nbToken; $i++) { + $regexp .= $this->computeRegexp($tokens, $i, $firstOptional); + } + + return new CompiledRoute( + $route, + 'text' === $tokens[0][0] ? $tokens[0][1] : '', + sprintf("#^%s$#s", $regexp), + array_reverse($tokens), + $variables + ); + } + + /** + * Computes the regexp used to match the token. + * + * @param array $tokens The route tokens + * @param integer $index The index of the current token + * @param integer $firstOptional The index of the first optional token + * + * @return string The regexp + */ + private function computeRegexp(array $tokens, $index, $firstOptional) + { + $token = $tokens[$index]; + if ('text' === $token[0]) { + // Text tokens + return preg_quote($token[1], '#'); + } else { + // Variable tokens + if (0 === $index && 0 === $firstOptional && 1 == count($tokens)) { + // When the only token is an optional variable token, the separator is required + return sprintf('%s(?P<%s>%s)?', preg_quote($token[1], '#'), $token[3], $token[2]); + } else { + $nbTokens = count($tokens); + $regexp = sprintf('%s(?P<%s>%s)', preg_quote($token[1], '#'), $token[3], $token[2]); + if ($index >= $firstOptional) { + // Enclose each optional tokens in a subpattern to make it optional + $regexp = "(?:$regexp"; + if ($nbTokens - 1 == $index) { + // Close the optional subpatterns + $regexp .= str_repeat(")?", $nbTokens - $firstOptional); + } + } + + return $regexp; + } + } + } +} diff --git a/3rdparty/symfony/routing/Symfony/Component/Routing/RouteCompilerInterface.php b/3rdparty/symfony/routing/Symfony/Component/Routing/RouteCompilerInterface.php new file mode 100644 index 0000000000..5c988adafb --- /dev/null +++ b/3rdparty/symfony/routing/Symfony/Component/Routing/RouteCompilerInterface.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing; + +/** + * RouteCompilerInterface is the interface that all RouteCompiler classes must implements. + * + * @author Fabien Potencier + */ +interface RouteCompilerInterface +{ + /** + * Compiles the current route instance. + * + * @param Route $route A Route instance + * + * @return CompiledRoute A CompiledRoute instance + */ + public function compile(Route $route); +} diff --git a/3rdparty/symfony/routing/Symfony/Component/Routing/Router.php b/3rdparty/symfony/routing/Symfony/Component/Routing/Router.php new file mode 100644 index 0000000000..eadb2231e1 --- /dev/null +++ b/3rdparty/symfony/routing/Symfony/Component/Routing/Router.php @@ -0,0 +1,263 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing; + +use Symfony\Component\Config\Loader\LoaderInterface; +use Symfony\Component\Config\ConfigCache; + +/** + * The Router class is an example of the integration of all pieces of the + * routing system for easier use. + * + * @author Fabien Potencier + */ +class Router implements RouterInterface +{ + protected $matcher; + protected $generator; + protected $defaults; + protected $context; + protected $loader; + protected $collection; + protected $resource; + protected $options; + + /** + * Constructor. + * + * @param LoaderInterface $loader A LoaderInterface instance + * @param mixed $resource The main resource to load + * @param array $options An array of options + * @param RequestContext $context The context + * @param array $defaults The default values + */ + public function __construct(LoaderInterface $loader, $resource, array $options = array(), RequestContext $context = null, array $defaults = array()) + { + $this->loader = $loader; + $this->resource = $resource; + $this->context = null === $context ? new RequestContext() : $context; + $this->defaults = $defaults; + $this->setOptions($options); + } + + /** + * Sets options. + * + * Available options: + * + * * cache_dir: The cache directory (or null to disable caching) + * * debug: Whether to enable debugging or not (false by default) + * * resource_type: Type hint for the main resource (optional) + * + * @param array $options An array of options + * + * @throws \InvalidArgumentException When unsupported option is provided + */ + public function setOptions(array $options) + { + $this->options = array( + 'cache_dir' => null, + 'debug' => false, + 'generator_class' => 'Symfony\\Component\\Routing\\Generator\\UrlGenerator', + 'generator_base_class' => 'Symfony\\Component\\Routing\\Generator\\UrlGenerator', + 'generator_dumper_class' => 'Symfony\\Component\\Routing\\Generator\\Dumper\\PhpGeneratorDumper', + 'generator_cache_class' => 'ProjectUrlGenerator', + 'matcher_class' => 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher', + 'matcher_base_class' => 'Symfony\\Component\\Routing\\Matcher\\UrlMatcher', + 'matcher_dumper_class' => 'Symfony\\Component\\Routing\\Matcher\\Dumper\\PhpMatcherDumper', + 'matcher_cache_class' => 'ProjectUrlMatcher', + 'resource_type' => null, + ); + + // check option names and live merge, if errors are encountered Exception will be thrown + $invalid = array(); + $isInvalid = false; + foreach ($options as $key => $value) { + if (array_key_exists($key, $this->options)) { + $this->options[$key] = $value; + } else { + $isInvalid = true; + $invalid[] = $key; + } + } + + if ($isInvalid) { + throw new \InvalidArgumentException(sprintf('The Router does not support the following options: "%s".', implode('\', \'', $invalid))); + } + } + + /** + * Sets an option. + * + * @param string $key The key + * @param mixed $value The value + * + * @throws \InvalidArgumentException + */ + public function setOption($key, $value) + { + if (!array_key_exists($key, $this->options)) { + throw new \InvalidArgumentException(sprintf('The Router does not support the "%s" option.', $key)); + } + + $this->options[$key] = $value; + } + + /** + * Gets an option value. + * + * @param string $key The key + * + * @return mixed The value + * + * @throws \InvalidArgumentException + */ + public function getOption($key) + { + if (!array_key_exists($key, $this->options)) { + throw new \InvalidArgumentException(sprintf('The Router does not support the "%s" option.', $key)); + } + + return $this->options[$key]; + } + + /** + * Gets the RouteCollection instance associated with this Router. + * + * @return RouteCollection A RouteCollection instance + */ + public function getRouteCollection() + { + if (null === $this->collection) { + $this->collection = $this->loader->load($this->resource, $this->options['resource_type']); + } + + return $this->collection; + } + + /** + * Sets the request context. + * + * @param RequestContext $context The context + */ + public function setContext(RequestContext $context) + { + $this->context = $context; + + $this->getMatcher()->setContext($context); + $this->getGenerator()->setContext($context); + } + + /** + * Gets the request context. + * + * @return RequestContext The context + */ + public function getContext() + { + return $this->context; + } + + /** + * Generates a URL from the given parameters. + * + * @param string $name The name of the route + * @param mixed $parameters An array of parameters + * @param Boolean $absolute Whether to generate an absolute URL + * + * @return string The generated URL + */ + public function generate($name, $parameters = array(), $absolute = false) + { + return $this->getGenerator()->generate($name, $parameters, $absolute); + } + + /** + * Tries to match a URL with a set of routes. + * + * Returns false if no route matches the URL. + * + * @param string $url URL to be parsed + * + * @return array|false An array of parameters or false if no route matches + */ + public function match($url) + { + return $this->getMatcher()->match($url); + } + + /** + * Gets the UrlMatcher instance associated with this Router. + * + * @return UrlMatcherInterface A UrlMatcherInterface instance + */ + public function getMatcher() + { + if (null !== $this->matcher) { + return $this->matcher; + } + + if (null === $this->options['cache_dir'] || null === $this->options['matcher_cache_class']) { + return $this->matcher = new $this->options['matcher_class']($this->getRouteCollection(), $this->context, $this->defaults); + } + + $class = $this->options['matcher_cache_class']; + $cache = new ConfigCache($this->options['cache_dir'].'/'.$class.'.php', $this->options['debug']); + if (!$cache->isFresh($class)) { + $dumper = new $this->options['matcher_dumper_class']($this->getRouteCollection()); + + $options = array( + 'class' => $class, + 'base_class' => $this->options['matcher_base_class'], + ); + + $cache->write($dumper->dump($options), $this->getRouteCollection()->getResources()); + } + + require_once $cache; + + return $this->matcher = new $class($this->context, $this->defaults); + } + + /** + * Gets the UrlGenerator instance associated with this Router. + * + * @return UrlGeneratorInterface A UrlGeneratorInterface instance + */ + public function getGenerator() + { + if (null !== $this->generator) { + return $this->generator; + } + + if (null === $this->options['cache_dir'] || null === $this->options['generator_cache_class']) { + return $this->generator = new $this->options['generator_class']($this->getRouteCollection(), $this->context, $this->defaults); + } + + $class = $this->options['generator_cache_class']; + $cache = new ConfigCache($this->options['cache_dir'].'/'.$class.'.php', $this->options['debug']); + if (!$cache->isFresh($class)) { + $dumper = new $this->options['generator_dumper_class']($this->getRouteCollection()); + + $options = array( + 'class' => $class, + 'base_class' => $this->options['generator_base_class'], + ); + + $cache->write($dumper->dump($options), $this->getRouteCollection()->getResources()); + } + + require_once $cache; + + return $this->generator = new $class($this->context, $this->defaults); + } +} diff --git a/3rdparty/symfony/routing/Symfony/Component/Routing/RouterInterface.php b/3rdparty/symfony/routing/Symfony/Component/Routing/RouterInterface.php new file mode 100644 index 0000000000..961342bf09 --- /dev/null +++ b/3rdparty/symfony/routing/Symfony/Component/Routing/RouterInterface.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Routing; + +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +use Symfony\Component\Routing\Matcher\UrlMatcherInterface; + +/** + * RouterInterface is the interface that all Router classes must implements. + * + * This interface is the concatenation of UrlMatcherInterface and UrlGeneratorInterface. + * + * @author Fabien Potencier + */ +interface RouterInterface extends UrlMatcherInterface, UrlGeneratorInterface +{ +} diff --git a/3rdparty/symfony/routing/Symfony/Component/Routing/composer.json b/3rdparty/symfony/routing/Symfony/Component/Routing/composer.json new file mode 100644 index 0000000000..8d29398b24 --- /dev/null +++ b/3rdparty/symfony/routing/Symfony/Component/Routing/composer.json @@ -0,0 +1,29 @@ +{ + "name": "symfony/routing", + "type": "library", + "description": "Symfony Routing Component", + "keywords": [], + "homepage": "http://symfony.com", + "license": "MIT", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "require": { + "php": ">=5.3.2" + }, + "suggest": { + "symfony/config": "self.version", + "symfony/yaml": "self.version" + }, + "autoload": { + "psr-0": { "Symfony\\Component\\Routing": "" } + }, + "target-dir": "Symfony/Component/Routing" +} diff --git a/composer.json b/composer.json deleted file mode 100644 index 7916b15cb9..0000000000 --- a/composer.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "description": "ownCloud gives you universal access to your files/contacts/calendar through a web interface or WebDAV.", - "homepage": "http://owncloud.org", - "license": "AGPL-3.0+", - "support": { - "email": "owncloud@kde.org", - "irc": "irc://irc.freenode.org/owncloud", - "forum": "http://forum.owncloud.org/", - "issues": "https://github.com/owncloud/core/issues" - }, - "require": { - "php": ">=5.3.2", - "symfony/routing": "2.0.*" - }, - "config": { - "vendor-dir": "3rdparty" - } -} From 56e9ce44c3ac18d6183a8959c690c6e3269bc79e Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sat, 27 Oct 2012 17:02:05 +0200 Subject: [PATCH 068/272] add a way to recalucale the size of a folder in the cache --- lib/files/cache/cache.php | 25 +++++++++++++++++++++++++ tests/lib/files/cache/cache.php | 11 +++++++++++ 2 files changed, 36 insertions(+) diff --git a/lib/files/cache/cache.php b/lib/files/cache/cache.php index 6604525477..734890329a 100644 --- a/lib/files/cache/cache.php +++ b/lib/files/cache/cache.php @@ -308,6 +308,31 @@ class Cache { return $result->fetchAll(); } + /** + * get the size of a folder and set it in the cache + * + * @param string $path + * @return int + */ + public function calculateFolderSize($path) { + $id = $this->getId($path); + $query = \OC_DB::prepare('SELECT `size` FROM `*PREFIX*filecache` WHERE `parent` = ? AND `storage` = ?'); + $result = $query->execute(array($id, $this->storageId)); + $totalSize = 0; + while ($row = $result->fetchRow()) { + $size = (int)$row['size']; + if ($size === -1) { + $totalSize = -1; + break; + } else { + $totalSize += $size; + } + } + + $this->update($id, array('size' => $totalSize)); + return $totalSize; + } + /** * get all file ids on the files on the storage * diff --git a/tests/lib/files/cache/cache.php b/tests/lib/files/cache/cache.php index 5a8d79b902..57a154d295 100644 --- a/tests/lib/files/cache/cache.php +++ b/tests/lib/files/cache/cache.php @@ -99,6 +99,17 @@ class Cache extends \UnitTestCase { $this->assertEqual($value, $cachedData[$name]); } } + + $file4 = 'folder/unkownSize'; + $fileData['unkownSize'] = array('size' => -1, 'mtime' => 25, 'mimetype' => 'foo/file'); + $this->cache->put($file4, $fileData['unkownSize']); + + $this->assertEquals(-1, $this->cache->calculateFolderSize($file1)); + + $fileData['unkownSize'] = array('size' => 5, 'mtime' => 25, 'mimetype' => 'foo/file'); + $this->cache->put($file4, $fileData['unkownSize']); + + $this->assertEquals(1025, $this->cache->calculateFolderSize($file1)); } function testStatus() { From 79d0ac21ccc65d12b6643ab525d45977644873e8 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sat, 27 Oct 2012 18:05:40 +0200 Subject: [PATCH 069/272] delete child entries when a folder gets removed from cache --- lib/files/cache/cache.php | 12 +++++++++--- lib/files/cache/scanner.php | 16 ++++++++++++++++ tests/lib/files/cache/cache.php | 6 +++++- tests/lib/files/cache/scanner.php | 9 +++++++-- 4 files changed, 37 insertions(+), 6 deletions(-) diff --git a/lib/files/cache/cache.php b/lib/files/cache/cache.php index 734890329a..cba48e4dbe 100644 --- a/lib/files/cache/cache.php +++ b/lib/files/cache/cache.php @@ -231,9 +231,15 @@ class Cache { * @param string $file */ public function remove($file) { - $pathHash = md5($file); - $query = \OC_DB::prepare('DELETE FROM `*PREFIX*filecache` WHERE `storage` = ? AND `path_hash` = ?'); - $query->execute(array($this->storageId, $pathHash)); + $entry = $this->get($file); + if ($entry['mimetype'] === 'httpd/unix-directory') { + $children = $this->getFolderContents($file); + foreach($children as $child){ + $this->remove($child['path']); + } + } + $query = \OC_DB::prepare('DELETE FROM `*PREFIX*filecache` WHERE `fileid` = ?'); + $query->execute(array($entry['fileid'])); } /** diff --git a/lib/files/cache/scanner.php b/lib/files/cache/scanner.php index 0adde1d354..7062888d74 100644 --- a/lib/files/cache/scanner.php +++ b/lib/files/cache/scanner.php @@ -107,4 +107,20 @@ class Scanner { } return $size; } + + /** + * update the folder size and the size of all parent folders + * + * @param $path + */ + public function correctFolderSize($path) { + $this->cache->calculateFolderSize($path); + if ($path !== '') { + $parent = dirname($path); + if ($parent === '.') { + $parent = ''; + } + $this->correctFolderSize($parent); + } + } } diff --git a/tests/lib/files/cache/cache.php b/tests/lib/files/cache/cache.php index 57a154d295..4f22e9bd1d 100644 --- a/tests/lib/files/cache/cache.php +++ b/tests/lib/files/cache/cache.php @@ -82,7 +82,7 @@ class Cache extends \UnitTestCase { $file1 = 'folder'; $file2 = 'folder/bar'; $file3 = 'folder/foo'; - $data1 = array('size' => 100, 'mtime' => 50, 'mimetype' => 'foo/folder'); + $data1 = array('size' => 100, 'mtime' => 50, 'mimetype' => 'httpd/unix-directory'); $fileData = array(); $fileData['bar'] = array('size' => 1000, 'mtime' => 20, 'mimetype' => 'foo/file'); $fileData['foo'] = array('size' => 20, 'mtime' => 25, 'mimetype' => 'foo/file'); @@ -110,6 +110,10 @@ class Cache extends \UnitTestCase { $this->cache->put($file4, $fileData['unkownSize']); $this->assertEquals(1025, $this->cache->calculateFolderSize($file1)); + + $this->cache->remove('folder'); + $this->assertFalse($this->cache->inCache('folder/foo')); + $this->assertFalse($this->cache->inCache('folder/bar')); } function testStatus() { diff --git a/tests/lib/files/cache/scanner.php b/tests/lib/files/cache/scanner.php index 34fefc784d..34d38c4273 100644 --- a/tests/lib/files/cache/scanner.php +++ b/tests/lib/files/cache/scanner.php @@ -89,14 +89,19 @@ class Scanner extends \UnitTestCase { $cachedDataFolder = $this->cache->get(''); $cachedDataFolder2 = $this->cache->get('folder'); - $this->assertEqual($cachedDataFolder['size'], -1); - $this->assertEqual($cachedDataFolder2['size'], -1); + $this->assertEqual(-1, $cachedDataFolder['size']); + $this->assertEqual(-1, $cachedDataFolder2['size']); $this->scanner->scan('folder', \OC\Files\Cache\Scanner::SCAN_SHALLOW); $cachedDataFolder2 = $this->cache->get('folder'); $this->assertNotEqual($cachedDataFolder2['size'], -1); + + $this->scanner->correctFolderSize('folder'); + + $cachedDataFolder = $this->cache->get(''); + $this->assertNotEqual($cachedDataFolder['size'], -1); } function setUp() { From fba7be1194fd31da2cb71c56d0cd8e6ab1c1ff49 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sun, 28 Oct 2012 11:26:31 +0100 Subject: [PATCH 070/272] add filesystem watcher to detect updates --- lib/files/cache/cache.php | 8 ++- lib/files/cache/watcher.php | 70 +++++++++++++++++++++++++ tests/lib/files/cache/watcher.php | 86 +++++++++++++++++++++++++++++++ tests/lib/files/view.php | 2 + 4 files changed, 164 insertions(+), 2 deletions(-) create mode 100644 lib/files/cache/watcher.php create mode 100644 tests/lib/files/cache/watcher.php diff --git a/lib/files/cache/cache.php b/lib/files/cache/cache.php index cba48e4dbe..138a5e6e63 100644 --- a/lib/files/cache/cache.php +++ b/lib/files/cache/cache.php @@ -234,7 +234,7 @@ class Cache { $entry = $this->get($file); if ($entry['mimetype'] === 'httpd/unix-directory') { $children = $this->getFolderContents($file); - foreach($children as $child){ + foreach ($children as $child) { $this->remove($child['path']); } } @@ -325,7 +325,9 @@ class Cache { $query = \OC_DB::prepare('SELECT `size` FROM `*PREFIX*filecache` WHERE `parent` = ? AND `storage` = ?'); $result = $query->execute(array($id, $this->storageId)); $totalSize = 0; + $hasChilds = 0; while ($row = $result->fetchRow()) { + $hasChilds = true; $size = (int)$row['size']; if ($size === -1) { $totalSize = -1; @@ -335,7 +337,9 @@ class Cache { } } - $this->update($id, array('size' => $totalSize)); + if ($hasChilds) { + $this->update($id, array('size' => $totalSize)); + } return $totalSize; } diff --git a/lib/files/cache/watcher.php b/lib/files/cache/watcher.php new file mode 100644 index 0000000000..f04ca9b465 --- /dev/null +++ b/lib/files/cache/watcher.php @@ -0,0 +1,70 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Files\Cache; + +/** + * check the storage backends for updates and change the cache accordingly + */ +class Watcher { + /** + * @var \OC\Files\Storage\Storage $storage + */ + private $storage; + + /** + * @var Cache $cache + */ + private $cache; + + /** + * @var Scanner $scanner; + */ + private $scanner; + + /** + * @param \OC\Files\Storage\Storage $storage + */ + public function __construct(\OC\Files\Storage\Storage $storage) { + $this->storage = $storage; + $this->cache = $storage->getCache(); + $this->scanner = $storage->getScanner(); + } + + /** + * check $path for updates + * + * @param string $path + */ + public function checkUpdate($path) { + $cachedEntry = $this->cache->get($path); + if ($this->storage->hasUpdated($path, $cachedEntry['mtime'])) { + if ($cachedEntry['mimetype'] === 'httpd/unix-directory') { + $this->scanner->scan($path, Scanner::SCAN_SHALLOW); + $this->cleanFolder($path); + } else { + $this->scanner->scanFile($path); + } + $this->scanner->correctFolderSize($path); + } + } + + /** + * remove deleted files in $path from the cache + * + * @param string $path + */ + public function cleanFolder($path) { + $cachedContent = $this->cache->getFolderContents($path); + foreach ($cachedContent as $entry) { + if (!$this->storage->file_exists($entry['path'])) { + $this->cache->remove($entry['path']); + } + } + } +} diff --git a/tests/lib/files/cache/watcher.php b/tests/lib/files/cache/watcher.php new file mode 100644 index 0000000000..a7076d9b0b --- /dev/null +++ b/tests/lib/files/cache/watcher.php @@ -0,0 +1,86 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace Test\Files\Cache; + +class Watcher extends \PHPUnit_Framework_TestCase { + + /** + * @var \OC\Files\Storage\Storage[] $storages; + */ + private $storages = array(); + + public function setUp() { + \OC\Files\Filesystem::clearMounts(); + } + + public function tearDown() { + foreach ($this->storages as $storage) { + $cache = $storage->getCache(); + $ids = $cache->getAll(); + \OC\Files\Cache\Permissions::removeMultiple($ids, \OC_User::getUser()); + $cache->clear(); + } + } + + function testWatcher() { + $storage = $this->getTestStorage(); + $cache = $storage->getCache(); + $updater = new \OC\Files\Cache\Watcher($storage); + + //set the mtime to the past so it can detect an mtime change + $cache->put('', array('mtime' => 10)); + + $this->assertTrue($cache->inCache('folder/bar.txt')); + $this->assertTrue($cache->inCache('folder/bar2.txt')); + + $this->assertFalse($cache->inCache('bar.test')); + $storage->file_put_contents('bar.test', 'foo'); + $updater->checkUpdate(''); + $this->assertTrue($cache->inCache('bar.test')); + $cachedData = $cache->get('bar.test'); + $this->assertEquals(3, $cachedData['size']); + + $cache->put('bar.test', array('mtime' => 10)); + $storage->file_put_contents('bar.test', 'test data'); + + $updater->checkUpdate('bar.test'); + $cachedData = $cache->get('bar.test'); + $this->assertEquals(9, $cachedData['size']); + + $cache->put('folder', array('mtime' => 10)); + + $storage->unlink('folder/bar2.txt'); + $updater->checkUpdate('folder'); + + $this->assertTrue($cache->inCache('folder/bar.txt')); + $this->assertFalse($cache->inCache('folder/bar2.txt')); + } + + /** + * @param bool $scan + * @return \OC\Files\Storage\Storage + */ + private function getTestStorage($scan = true) { + $storage = new \OC\Files\Storage\Temporary(array()); + $textData = "dummy file data\n"; + $imgData = file_get_contents(\OC::$SERVERROOT . '/core/img/logo.png'); + $storage->mkdir('folder'); + $storage->file_put_contents('foo.txt', $textData); + $storage->file_put_contents('foo.png', $imgData); + $storage->file_put_contents('folder/bar.txt', $textData); + $storage->file_put_contents('folder/bar2.txt', $textData); + + if ($scan) { + $scanner = $storage->getScanner(); + $scanner->scan(''); + } + $this->storages[] = $storage; + return $storage; + } +} diff --git a/tests/lib/files/view.php b/tests/lib/files/view.php index 5144fb1caa..8ff03963e4 100644 --- a/tests/lib/files/view.php +++ b/tests/lib/files/view.php @@ -20,6 +20,8 @@ class View extends \PHPUnit_Framework_TestCase { public function tearDown() { foreach ($this->storages as $storage) { $cache = $storage->getCache(); + $ids = $cache->getAll(); + \OC\Files\Cache\Permissions::removeMultiple($ids, \OC_User::getUser()); $cache->clear(); } } From b07672821bcdc1ce31e169d691cf9a94ad5fad21 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sun, 28 Oct 2012 11:43:45 +0100 Subject: [PATCH 071/272] check for changes when using the cache api --- lib/files/view.php | 6 ++++++ tests/lib/files/view.php | 17 +++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/lib/files/view.php b/lib/files/view.php index 0f9a5feb5d..c5e470b438 100644 --- a/lib/files/view.php +++ b/lib/files/view.php @@ -672,6 +672,9 @@ class View { if (!$cache->inCache($internalPath)) { $scanner = $storage->getScanner(); $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW); + } else { + $watcher = new \OC\Files\Cache\Watcher($storage); + $watcher->checkUpdate($internalPath); } $data = $cache->get($internalPath); @@ -711,6 +714,9 @@ class View { if (!$cache->inCache($internalPath)) { $scanner = $storage->getScanner(); $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW); + } else { + $watcher = new \OC\Files\Cache\Watcher($storage); + $watcher->checkUpdate($internalPath); } $files = $cache->getFolderContents($internalPath); //TODO: mimetype_filter diff --git a/tests/lib/files/view.php b/tests/lib/files/view.php index 8ff03963e4..fa562cb15c 100644 --- a/tests/lib/files/view.php +++ b/tests/lib/files/view.php @@ -146,6 +146,23 @@ class View extends \PHPUnit_Framework_TestCase { $this->assertEquals(3, count($folderView->searchByMime('text'))); } + function testWatcher() { + $storage1 = $this->getTestStorage(); + \OC\Files\Filesystem::mount($storage1, array(), '/'); + + $rootView = new \OC\Files\View(''); + + $cachedData = $rootView->getFileInfo('foo.txt'); + $this->assertEquals(16, $cachedData['size']); + + $rootView->putFileInfo('foo.txt', array('mtime' => 10)); + $storage1->file_put_contents('foo.txt', 'foo'); + clearstatcache(); + + $cachedData = $rootView->getFileInfo('foo.txt'); + $this->assertEquals(3, $cachedData['size']); + } + /** * @param bool $scan * @return \OC\Files\Storage\Storage From 8a6bb7965d96b1c4297da8f5dbc9644fec7aeb0f Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 2 Nov 2012 22:25:33 +0100 Subject: [PATCH 072/272] add Cache::move --- lib/files/cache/cache.php | 29 +++++++++++++++++++++++++++-- tests/lib/files/cache/cache.php | 26 ++++++++++++++++++++++++++ 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/lib/files/cache/cache.php b/lib/files/cache/cache.php index 138a5e6e63..9d13b56104 100644 --- a/lib/files/cache/cache.php +++ b/lib/files/cache/cache.php @@ -242,6 +242,32 @@ class Cache { $query->execute(array($entry['fileid'])); } + /** + * Move a file or folder in the cache + * + * @param string $source + * @param string $target + */ + public function move($source, $target) { + $sourceId = $this->getId($source); + $newParentId = $this->getParentId($target); + + //find all child entries + $query = \OC_DB::prepare('SELECT `path`, `fileid` FROM `*PREFIX*filecache` WHERE `path` LIKE ?'); + $result = $query->execute(array($source . '/%')); + $childEntries = $result->fetchAll(); + $sourceLength = strlen($source); + $query = \OC_DB::prepare('UPDATE `*PREFIX*filecache` SET `path` = ?, `path_hash` = ? WHERE `fileid` = ?'); + + foreach ($childEntries as $child) { + $targetPath = $target . substr($child['path'], $sourceLength); + $query->execute(array($targetPath, md5($targetPath), $child['fileid'])); + } + + $query = \OC_DB::prepare('UPDATE `*PREFIX*filecache` SET `path` = ?, `path_hash` = ?, `parent` =? WHERE `fileid` = ?'); + $query->execute(array($target, md5($target), $newParentId, $sourceId)); + } + /** * remove all entries for files that are stored on the storage from the cache */ @@ -296,8 +322,7 @@ class Cache { /** * search for files by mimetype * - * @param string $part1 - * @param string $part2 + * @param string $mimetype * @return array */ public function searchByMime($mimetype) { diff --git a/tests/lib/files/cache/cache.php b/tests/lib/files/cache/cache.php index 4f22e9bd1d..9c469aa937 100644 --- a/tests/lib/files/cache/cache.php +++ b/tests/lib/files/cache/cache.php @@ -149,6 +149,32 @@ class Cache extends \UnitTestCase { $this->assertEquals(2, count($this->cache->searchByMime('foo/file'))); } + function testMove() { + $file1 = 'folder'; + $file2 = 'folder/bar'; + $file3 = 'folder/foo'; + $file4 = 'folder/foo/1'; + $file5 = 'folder/foo/2'; + $data = array('size' => 100, 'mtime' => 50, 'mimetype' => 'foo/bar'); + + $this->cache->put($file1, $data); + $this->cache->put($file2, $data); + $this->cache->put($file3, $data); + $this->cache->put($file4, $data); + $this->cache->put($file5, $data); + + $this->cache->move('folder/foo', 'folder/foobar'); + + $this->assertFalse($this->cache->inCache('folder/foo')); + $this->assertFalse($this->cache->inCache('folder/foo/1')); + $this->assertFalse($this->cache->inCache('folder/foo/2')); + + $this->assertTrue($this->cache->inCache('folder/bar')); + $this->assertTrue($this->cache->inCache('folder/foobar')); + $this->assertTrue($this->cache->inCache('folder/foobar/1')); + $this->assertTrue($this->cache->inCache('folder/foobar/2')); + } + public function tearDown() { $this->cache->clear(); } From 72c38686449a8887d8441377a11f0bfc910bc31f Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 8 Nov 2012 17:42:26 +0100 Subject: [PATCH 073/272] add resolvePath to filesystem view api --- lib/files/view.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/files/view.php b/lib/files/view.php index 709e374d86..e3f9b762f7 100644 --- a/lib/files/view.php +++ b/lib/files/view.php @@ -101,6 +101,16 @@ class View { return Filesystem::getMountPoint($this->getAbsolutePath($path)); } + /** + * resolve a path to a storage and internal path + * + * @param string $path + * @return array consisting of the storage and the internal path + */ + public function resolvePath($path) { + return Filesystem::resolvePath($this->getAbsolutePath($path)); + } + /** * return the path to a local version of the file * we need this because we can't know if a file is stored local or not from outside the filestorage and for some purposes a local file is needed From 706bb3ccd654f76c0a6c5b7874fcc1e288b13f54 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 8 Nov 2012 17:47:00 +0100 Subject: [PATCH 074/272] move ETag generation to storage backends --- lib/connector/sabre/node.php | 16 +--------------- lib/files/filesystem.php | 10 ++++++++++ lib/files/storage/common.php | 16 ++++++++++++++++ lib/files/storage/storage.php | 8 ++++++++ lib/files/view.php | 15 +++++++++++++++ 5 files changed, 50 insertions(+), 15 deletions(-) diff --git a/lib/connector/sabre/node.php b/lib/connector/sabre/node.php index ebc9e53962..d0ab0c2498 100644 --- a/lib/connector/sabre/node.php +++ b/lib/connector/sabre/node.php @@ -210,27 +210,13 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr return $props; } - /** - * Creates a ETag for this path. - * @param string $path Path of the file - * @return string|null Returns null if the ETag can not effectively be determined - */ - static protected function createETag($path) { - if(self::$ETagFunction) { - $hash = call_user_func(self::$ETagFunction, $path); - return $hash; - }else{ - return uniqid('', true); - } - } - /** * Returns the ETag surrounded by double-quotes for this path. * @param string $path Path of the file * @return string|null Returns null if the ETag can not effectively be determined */ static public function getETagPropertyForPath($path) { - $tag = self::createETag($path); + $tag = \OC\Files\Filesystem::getETag($path); if (empty($tag)) { return null; } diff --git a/lib/files/filesystem.php b/lib/files/filesystem.php index 76a188e412..ed2814211f 100644 --- a/lib/files/filesystem.php +++ b/lib/files/filesystem.php @@ -654,6 +654,16 @@ class Filesystem { public static function getDirectoryContent($directory, $mimetype_filter = '') { return self::$defaultInstance->getDirectoryContent($directory, $mimetype_filter); } + + /** + * get the ETag for a file or folder + * + * @param string $path + * @return string + */ + static public function getETag($path){ + return self::$defaultInstance->getETag($path); + } } \OC_Hook::connect('OC_Filesystem', 'post_write', 'OC_Filesystem', 'removeETagHook'); diff --git a/lib/files/storage/common.php b/lib/files/storage/common.php index e22264d0da..f471752d1f 100644 --- a/lib/files/storage/common.php +++ b/lib/files/storage/common.php @@ -267,4 +267,20 @@ abstract class Common implements \OC\Files\Storage\Storage { public function getOwner($path) { return \OC_User::getUser(); } + + /** + * get the ETag for a file or folder + * + * @param string $path + * @return string + */ + public function getETag($path){ + $ETagFunction = \OC_Connector_Sabre_Node::$ETagFunction; + if($ETagFunction) { + $hash = call_user_func($ETagFunction, $path); + return $hash; + }else{ + return uniqid('', true); + } + } } diff --git a/lib/files/storage/storage.php b/lib/files/storage/storage.php index 1f5c935629..bb1ba16984 100644 --- a/lib/files/storage/storage.php +++ b/lib/files/storage/storage.php @@ -63,4 +63,12 @@ interface Storage{ public function getScanner(); public function getOwner($path); + + /** + * get the ETag for a file or folder + * + * @param string $path + * @return string + */ + public function getETag($path); } diff --git a/lib/files/view.php b/lib/files/view.php index e3f9b762f7..a54c3ee356 100644 --- a/lib/files/view.php +++ b/lib/files/view.php @@ -866,4 +866,19 @@ class View { return $files; } + + /** + * get the ETag for a file or folder + * + * @param string $path + * @return string + */ + public function getETag($path){ + /** + * @var Storage\Storage $storage + * @var string $internalPath + */ + list($storage, $internalPath) = $this->resolvePath($path); + return $storage->getETag($internalPath); + } } From e7bed5ddabea5a3841d4a08082badaec025ed242 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 8 Nov 2012 17:59:08 +0100 Subject: [PATCH 075/272] allow creating Cache objects with only the storage id instead of the whole storage object --- lib/files/cache/cache.php | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/files/cache/cache.php b/lib/files/cache/cache.php index 9d13b56104..dba026a611 100644 --- a/lib/files/cache/cache.php +++ b/lib/files/cache/cache.php @@ -19,24 +19,25 @@ class Cache { const SHALLOW = 2; //folder in cache, but not all child files are completely scanned const COMPLETE = 3; - /** - * @var \OC\Files\Storage\Storage - */ - private $storage; - /** * @var array partial data for the cache */ private $partial = array(); + /** + * @var string + */ private $storageId; /** - * @param \OC\Files\Storage\Storage $storage + * @param \OC\Files\Storage\Storage|string $storage */ - public function __construct(\OC\Files\Storage\Storage $storage) { - $this->storage = $storage; - $this->storageId = $storage->getId(); + public function __construct($storage) { + if($storage instanceof \OC\Files\Storage\Storage){ + $this->storageId = $storage->getId(); + }else{ + $this->storageId = $storage; + } } /** From 3f644fe70c8451ab1ab877c4dd97f34366548b70 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 8 Nov 2012 18:07:30 +0100 Subject: [PATCH 076/272] fix calculateFolderSize for non existing files --- lib/files/cache/cache.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/files/cache/cache.php b/lib/files/cache/cache.php index dba026a611..83d3585bb5 100644 --- a/lib/files/cache/cache.php +++ b/lib/files/cache/cache.php @@ -348,6 +348,9 @@ class Cache { */ public function calculateFolderSize($path) { $id = $this->getId($path); + if($id === -1){ + return 0; + } $query = \OC_DB::prepare('SELECT `size` FROM `*PREFIX*filecache` WHERE `parent` = ? AND `storage` = ?'); $result = $query->execute(array($id, $this->storageId)); $totalSize = 0; From 15b8a3f98757f220db190c1e9489d5888b2b1015 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 8 Nov 2012 18:10:54 +0100 Subject: [PATCH 077/272] move correctFolderSize from Scanner to Cache --- lib/files/cache/cache.php | 16 ++++++++++++++++ lib/files/cache/scanner.php | 16 ---------------- lib/files/cache/watcher.php | 2 +- tests/lib/files/cache/scanner.php | 2 +- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/lib/files/cache/cache.php b/lib/files/cache/cache.php index 83d3585bb5..7c6bba4fad 100644 --- a/lib/files/cache/cache.php +++ b/lib/files/cache/cache.php @@ -340,6 +340,22 @@ class Cache { return $result->fetchAll(); } + /** + * update the folder size and the size of all parent folders + * + * @param $path + */ + public function correctFolderSize($path) { + $this->calculateFolderSize($path); + if ($path !== '') { + $parent = dirname($path); + if ($parent === '.') { + $parent = ''; + } + $this->correctFolderSize($parent); + } + } + /** * get the size of a folder and set it in the cache * diff --git a/lib/files/cache/scanner.php b/lib/files/cache/scanner.php index 7062888d74..0adde1d354 100644 --- a/lib/files/cache/scanner.php +++ b/lib/files/cache/scanner.php @@ -107,20 +107,4 @@ class Scanner { } return $size; } - - /** - * update the folder size and the size of all parent folders - * - * @param $path - */ - public function correctFolderSize($path) { - $this->cache->calculateFolderSize($path); - if ($path !== '') { - $parent = dirname($path); - if ($parent === '.') { - $parent = ''; - } - $this->correctFolderSize($parent); - } - } } diff --git a/lib/files/cache/watcher.php b/lib/files/cache/watcher.php index f04ca9b465..d6039d9945 100644 --- a/lib/files/cache/watcher.php +++ b/lib/files/cache/watcher.php @@ -50,7 +50,7 @@ class Watcher { } else { $this->scanner->scanFile($path); } - $this->scanner->correctFolderSize($path); + $this->cache->correctFolderSize($path); } } diff --git a/tests/lib/files/cache/scanner.php b/tests/lib/files/cache/scanner.php index 34d38c4273..f784a82dad 100644 --- a/tests/lib/files/cache/scanner.php +++ b/tests/lib/files/cache/scanner.php @@ -98,7 +98,7 @@ class Scanner extends \UnitTestCase { $this->assertNotEqual($cachedDataFolder2['size'], -1); - $this->scanner->correctFolderSize('folder'); + $this->cache->correctFolderSize('folder'); $cachedDataFolder = $this->cache->get(''); $this->assertNotEqual($cachedDataFolder['size'], -1); From 5a173b901f90dba3465847cde4477be9994a1341 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 8 Nov 2012 20:47:40 +0100 Subject: [PATCH 078/272] fix Scanner->scan setting the filesize to 0 for files --- lib/files/cache/scanner.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/files/cache/scanner.php b/lib/files/cache/scanner.php index 0adde1d354..eb947a2d53 100644 --- a/lib/files/cache/scanner.php +++ b/lib/files/cache/scanner.php @@ -101,9 +101,9 @@ class Scanner { } } } - } - if ($size !== -1) { - $this->cache->put($path, array('size' => $size)); + if ($size !== -1) { + $this->cache->put($path, array('size' => $size)); + } } return $size; } From fedff3eafe73a3eafbc1bc7af364297e8135d086 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 8 Nov 2012 21:12:40 +0100 Subject: [PATCH 079/272] add cache updater --- lib/files/cache/updater.php | 71 ++++++++++++++++++ tests/lib/files/cache/updater.php | 116 ++++++++++++++++++++++++++++++ 2 files changed, 187 insertions(+) create mode 100644 lib/files/cache/updater.php create mode 100644 tests/lib/files/cache/updater.php diff --git a/lib/files/cache/updater.php b/lib/files/cache/updater.php new file mode 100644 index 0000000000..fb9783023e --- /dev/null +++ b/lib/files/cache/updater.php @@ -0,0 +1,71 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Files\Cache; + +/** + * listen to filesystem hooks and change the cache accordingly + */ +class Updater { + + /** + * resolve a path to a storage and internal path + * + * @param string $path + * @return array consisting of the storage and the internal path + */ + static public function resolvePath($path) { + $view = \OC\Files\Filesystem::getView(); + return $view->resolvePath($path); + } + + static public function writeUpdate($path) { + /** + * @var \OC\Files\Storage\Storage $storage + * @var string $internalPath + */ + list($storage, $internalPath) = self::resolvePath($path); + $cache = new Cache($storage); + $scanner = new Scanner($storage); + $scanner->scan($internalPath, Scanner::SCAN_SHALLOW); + $cache->correctFolderSize($internalPath); + } + + static public function deleteUpdate($path) { + /** + * @var \OC\Files\Storage\Storage $storage + * @var string $internalPath + */ + list($storage, $internalPath) = self::resolvePath($path); + $cache = new Cache($storage); + $cache->remove($internalPath); + $cache->correctFolderSize($internalPath); + } + + /** + * @param array $params + */ + static public function writeHook($params) { + self::writeUpdate($params['path']); + } + + /** + * @param array $params + */ + static public function renameHook($params) { + self::deleteUpdate($params['oldpath']); + self::writeUpdate($params['newpath']); + } + + /** + * @param array $params + */ + static public function deleteHook($params) { + self::deleteUpdate($params['path']); + } +} diff --git a/tests/lib/files/cache/updater.php b/tests/lib/files/cache/updater.php new file mode 100644 index 0000000000..8059418dc1 --- /dev/null +++ b/tests/lib/files/cache/updater.php @@ -0,0 +1,116 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace Test\Files\Cache; + +use \OC\Files\Filesystem as Filesystem; + +class Updater extends \PHPUnit_Framework_TestCase { + /** + * @var \OC\Files\Storage\Storage $storage + */ + private $storage; + + /** + * @var \OC\Files\Cache\Scanner $scanner + */ + private $scanner; + + /** + * @var \OC\Files\Cache\Cache $cache + */ + private $cache; + + private static $user; + + public function setUp() { + $this->storage = new \OC\Files\Storage\Temporary(array()); + $textData = "dummy file data\n"; + $imgData = file_get_contents(\OC::$SERVERROOT . '/core/img/logo.png'); + $this->storage->mkdir('folder'); + $this->storage->file_put_contents('foo.txt', $textData); + $this->storage->file_put_contents('foo.png', $imgData); + $this->storage->file_put_contents('folder/bar.txt', $textData); + $this->storage->file_put_contents('folder/bar2.txt', $textData); + + $this->scanner = $this->storage->getScanner(); + $this->scanner->scan(''); + $this->cache = $this->storage->getCache(); + + if (!self::$user) { + if (!\OC\Files\Filesystem::getView()) { + self::$user = uniqid(); + \OC\Files\Filesystem::init('/' . self::$user . '/files'); + } else { + self::$user = \OC_User::getUser(); + } + } + + Filesystem::clearMounts(); + Filesystem::mount($this->storage, array(), '/' . self::$user . '/files'); + + \OC_Hook::connect('OC_Filesystem', 'post_write', '\OC\Files\Cache\Updater', 'writeHook'); + \OC_Hook::connect('OC_Filesystem', 'post_delete', '\OC\Files\Cache\Updater', 'deleteHook'); + \OC_Hook::connect('OC_Filesystem', 'post_rename', '\OC\Files\Cache\Updater', 'renameHook'); + + } + + public function tearDown() { + $this->cache->clear(); + Filesystem::tearDown(); + } + + public function testWrite() { + $textSize = strlen("dummy file data\n"); + $imageSize = filesize(\OC::$SERVERROOT . '/core/img/logo.png'); + $cachedData = $this->cache->get(''); + $this->assertEquals(3 * $textSize + $imageSize, $cachedData['size']); + + Filesystem::file_put_contents('foo.txt', 'asd'); + $cachedData = $this->cache->get('foo.txt'); + $this->assertEquals(3, $cachedData['size']); + $cachedData = $this->cache->get(''); + $this->assertEquals(2 * $textSize + $imageSize + 3, $cachedData['size']); + + $this->assertFalse($this->cache->inCache('bar.txt')); + Filesystem::file_put_contents('bar.txt', 'asd'); + $this->assertTrue($this->cache->inCache('bar.txt')); + $cachedData = $this->cache->get('bar.txt'); + $this->assertEquals(3, $cachedData['size']); + $cachedData = $this->cache->get(''); + $this->assertEquals(2 * $textSize + $imageSize + 2 * 3, $cachedData['size']); + } + + public function testDelete() { + $textSize = strlen("dummy file data\n"); + $imageSize = filesize(\OC::$SERVERROOT . '/core/img/logo.png'); + $cachedData = $this->cache->get(''); + $this->assertEquals(3 * $textSize + $imageSize, $cachedData['size']); + + $this->assertTrue($this->cache->inCache('foo.txt')); + Filesystem::unlink('foo.txt', 'asd'); + $this->assertFalse($this->cache->inCache('foo.txt')); + $cachedData = $this->cache->get(''); + $this->assertEquals(2 * $textSize + $imageSize, $cachedData['size']); + } + + public function testRename() { + $textSize = strlen("dummy file data\n"); + $imageSize = filesize(\OC::$SERVERROOT . '/core/img/logo.png'); + $cachedData = $this->cache->get(''); + $this->assertEquals(3 * $textSize + $imageSize, $cachedData['size']); + + $this->assertTrue($this->cache->inCache('foo.txt')); + $this->assertFalse($this->cache->inCache('bar.txt')); + Filesystem::rename('foo.txt', 'bar.txt'); + $this->assertFalse($this->cache->inCache('foo.txt')); + $this->assertTrue($this->cache->inCache('bar.txt')); + $cachedData = $this->cache->get(''); + $this->assertEquals(3 * $textSize + $imageSize, $cachedData['size']); + } +} From d4858527c2fae4b58c5a9bc1c3aae9ada0ee7b24 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 8 Nov 2012 21:35:30 +0100 Subject: [PATCH 080/272] fix rename parameter order --- apps/files/ajax/rename.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/files/ajax/rename.php b/apps/files/ajax/rename.php index a2b9b8de25..470ee635a2 100644 --- a/apps/files/ajax/rename.php +++ b/apps/files/ajax/rename.php @@ -11,9 +11,9 @@ $dir = stripslashes($_GET["dir"]); $file = stripslashes($_GET["file"]); $newname = stripslashes($_GET["newname"]); -if (OC_User::isLoggedIn() && ($dir != '' || $file != 'Shared')) { - $targetFile = \OC\Files\Filesystem::normalizePath($dir . '/' . $file); - $sourceFile = \OC\Files\Filesystem::normalizePath($dir . '/' . $newname); +if (($dir != '' || $file != 'Shared')) { + $targetFile = \OC\Files\Filesystem::normalizePath($dir . '/' . $newname); + $sourceFile = \OC\Files\Filesystem::normalizePath($dir . '/' . $file); if(\OC\Files\Filesystem::rename($sourceFile, $targetFile)) { OCP\JSON::success(array("data" => array( "dir" => $dir, "file" => $file, "newname" => $newname ))); } else { From 46071fed80c7375edf4788578776a6b313b6dca3 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 8 Nov 2012 21:35:40 +0100 Subject: [PATCH 081/272] fix quota proxy --- lib/fileproxy/quota.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/fileproxy/quota.php b/lib/fileproxy/quota.php index b359b9ae02..a8219191bf 100644 --- a/lib/fileproxy/quota.php +++ b/lib/fileproxy/quota.php @@ -22,7 +22,7 @@ */ /** - * user quota managment + * user quota management */ class OC_FileProxy_Quota extends OC_FileProxy{ @@ -39,10 +39,8 @@ class OC_FileProxy_Quota extends OC_FileProxy{ return $this->userQuota[$user]; } $userQuota=OC_Preferences::getValue($user, 'files', 'quota', 'default'); - $userQuota=OC_Preferences::getValue($user,'files','quota','default'); if($userQuota=='default') { $userQuota=OC_AppConfig::getValue('files', 'default_quota', 'none'); - $userQuota=OC_AppConfig::getValue('files','default_quota','none'); } if($userQuota=='none') { $this->userQuota[$user]=0; @@ -59,8 +57,12 @@ class OC_FileProxy_Quota extends OC_FileProxy{ * @return int */ private function getFreeSpace($path) { - $storage=OC_Filesystem::getStorage($path); - $owner=$storage->getOwner($path); + /** + * @var \OC\Files\Storage\Storage $storage + * @var string $internalPath + */ + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($path); + $owner=$storage->getOwner($internalPath); $totalSpace=$this->getQuota($owner); if($totalSpace==0) { From 41a61bc637c3d60efc5abe81c39fc726fd78cd1d Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 8 Nov 2012 21:36:13 +0100 Subject: [PATCH 082/272] fix undefined index when loading files app --- apps/files/index.php | 4 +--- apps/files/templates/part.list.php | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/apps/files/index.php b/apps/files/index.php index cdb88ab83f..4e5f4cd93a 100644 --- a/apps/files/index.php +++ b/apps/files/index.php @@ -66,9 +66,7 @@ foreach( \OC\Files\Filesystem::getDirectoryContent( $dir ) as $i ) { $i['extension']=''; } } - if($i['directory']=='/') { - $i['directory']=''; - } + $i['directory'] = $dir; $files[] = $i; } diff --git a/apps/files/templates/part.list.php b/apps/files/templates/part.list.php index 4b5ac32567..9c9d9926e5 100644 --- a/apps/files/templates/part.list.php +++ b/apps/files/templates/part.list.php @@ -18,7 +18,7 @@ $name = str_replace('%2F', '/', $name); $directory = str_replace('+', '%20', urlencode($file['directory'])); $directory = str_replace('%2F', '/', $directory); ?> -

' data-permissions=''> + ' data-permissions=''>
From cbcd9ba84ada830d91a6f1d7bee0ac59762835ed Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 15 Nov 2012 00:57:30 +0100 Subject: [PATCH 083/272] allow storage backends to implement custom permission management --- lib/files/cache/permissions.php | 36 ++++++++++++++------- lib/files/storage/common.php | 14 ++++++--- lib/files/storage/storage.php | 5 +++ lib/files/view.php | 6 ++-- tests/lib/files/cache/permissions.php | 45 ++++++++++++++++----------- tests/lib/files/cache/scanner.php | 3 +- tests/lib/files/cache/watcher.php | 3 +- tests/lib/files/view.php | 3 +- 8 files changed, 76 insertions(+), 39 deletions(-) diff --git a/lib/files/cache/permissions.php b/lib/files/cache/permissions.php index dd7233abc7..d0968337f0 100644 --- a/lib/files/cache/permissions.php +++ b/lib/files/cache/permissions.php @@ -9,6 +9,22 @@ namespace OC\Files\Cache; class Permissions { + /** + * @var string $storageId + */ + private $storageId; + + /** + * @param \OC\Files\Storage\Storage|string $storage + */ + public function __construct($storage){ + if($storage instanceof \OC\Files\Storage\Storage){ + $this->storageId = $storage->getId(); + }else{ + $this->storageId = $storage; + } + } + /** * get the permissions for a single file * @@ -16,7 +32,7 @@ class Permissions { * @param string $user * @return int (-1 if file no permissions set) */ - static public function get($fileId, $user) { + public function get($fileId, $user) { $query = \OC_DB::prepare('SELECT `permissions` FROM `*PREFIX*permissions` WHERE `user` = ? AND `fileid` = ?'); $result = $query->execute(array($user, $fileId)); if ($row = $result->fetchRow()) { @@ -33,7 +49,7 @@ class Permissions { * @param string $user * @param int $permissions */ - static public function set($fileId, $user, $permissions) { + public function set($fileId, $user, $permissions) { if (self::get($fileId, $user) !== -1) { $query = \OC_DB::prepare('UPDATE `*PREFIX*permissions` SET `permissions` = ? WHERE `user` = ? AND `fileid` = ?'); } else { @@ -49,7 +65,7 @@ class Permissions { * @param string $user * @return int[] */ - static public function getMultiple($fileIds, $user) { + public function getMultiple($fileIds, $user) { if (count($fileIds) === 0) { return array(); } @@ -72,17 +88,15 @@ class Permissions { * @param int $fileId * @param string $user */ - static public function remove($fileId, $user) { + public function remove($fileId, $user) { $query = \OC_DB::prepare('DELETE FROM `*PREFIX*permissions` WHERE `fileid` = ? AND `user` = ?'); $query->execute(array($fileId, $user)); } - static public function removeMultiple($fileIds, $user) { - $params = $fileIds; - $params[] = $user; - $inPart = implode(', ', array_fill(0, count($fileIds), '?')); - - $query = \OC_DB::prepare('DELETE FROM `*PREFIX*permissions` WHERE `fileid` IN (' . $inPart . ') AND `user` = ?'); - $query->execute($params); + public function removeMultiple($fileIds, $user) { + $query = \OC_DB::prepare('DELETE FROM `*PREFIX*permissions` WHERE `fileid` = ? AND `user` = ?'); + foreach($fileIds as $fileId){ + $query->execute(array($fileId, $user)); + } } } diff --git a/lib/files/storage/common.php b/lib/files/storage/common.php index f471752d1f..cf6fe64a4f 100644 --- a/lib/files/storage/common.php +++ b/lib/files/storage/common.php @@ -57,19 +57,19 @@ abstract class Common implements \OC\Files\Storage\Storage { public function getPermissions($path){ $permissions = 0; if($this->isCreatable($path)){ - $permissions |= \OCP\Share::PERMISSION_CREATE; + $permissions |= \OCP\PERMISSION_CREATE; } if($this->isReadable($path)){ - $permissions |= \OCP\Share::PERMISSION_READ; + $permissions |= \OCP\PERMISSION_READ; } if($this->isUpdatable($path)){ - $permissions |= \OCP\Share::PERMISSION_UPDATE; + $permissions |= \OCP\PERMISSION_UPDATE; } if($this->isDeletable($path)){ - $permissions |= \OCP\Share::PERMISSION_DELETE; + $permissions |= \OCP\PERMISSION_DELETE; } if($this->isSharable($path)){ - $permissions |= \OCP\Share::PERMISSION_SHARE; + $permissions |= \OCP\PERMISSION_SHARE; } return $permissions; } @@ -259,6 +259,10 @@ abstract class Common implements \OC\Files\Storage\Storage { return new \OC\Files\Cache\Scanner($this); } + public function getPermissionsCache(){ + return new \OC\Files\Cache\Permissions($this); + } + /** * get the owner of a path * @param string $path The path to get the owner diff --git a/lib/files/storage/storage.php b/lib/files/storage/storage.php index bb1ba16984..73dcb8fe36 100644 --- a/lib/files/storage/storage.php +++ b/lib/files/storage/storage.php @@ -64,6 +64,11 @@ interface Storage{ public function getOwner($path); + /** + * @return \OC\Files\Cache\Permissions + */ + public function getPermissionsCache(); + /** * get the ETag for a file or folder * diff --git a/lib/files/view.php b/lib/files/view.php index a54c3ee356..e516a4fed6 100644 --- a/lib/files/view.php +++ b/lib/files/view.php @@ -701,7 +701,8 @@ class View { } } - $data['permissions'] = Cache\Permissions::get($data['fileid'], \OC_User::getUser()); + $permissionsCache = $storage->getPermissionsCache(); + $data['permissions'] = $permissionsCache->get($data['fileid'], \OC_User::getUser()); return $data; } @@ -759,8 +760,9 @@ class View { $files[$i]['type'] = $file['mimetype'] === 'httpd/unix-directory' ? 'dir' : 'file'; $ids[] = $file['fileid']; } + $permissionsCache = $storage->getPermissionsCache(); - $permissions = Cache\Permissions::getMultiple($ids, \OC_User::getUser()); + $permissions = $permissionsCache->getMultiple($ids, \OC_User::getUser()); foreach ($files as $i => $file) { $files[$i]['permissions'] = $permissions[$file['fileid']]; } diff --git a/tests/lib/files/cache/permissions.php b/tests/lib/files/cache/permissions.php index 4d47929a3e..56dbbc4518 100644 --- a/tests/lib/files/cache/permissions.php +++ b/tests/lib/files/cache/permissions.php @@ -9,39 +9,48 @@ namespace Test\Files\Cache; class Permissions extends \PHPUnit_Framework_TestCase { + /*** + * @var \OC\Files\Cache\Permissions $permissionsCache + */ + private $permissionsCache; + + function setUp(){ + $this->permissionsCache=new \OC\Files\Cache\Permissions('dummy'); + } + function testSimple() { $ids = range(1, 10); $user = uniqid(); - $this->assertEquals(-1, \OC\Files\Cache\Permissions::get(1, $user)); - \OC\Files\Cache\Permissions::set(1, $user, 1); - $this->assertEquals(1, \OC\Files\Cache\Permissions::get(1, $user)); - $this->assertEquals(-1, \OC\Files\Cache\Permissions::get(2, $user)); - $this->assertEquals(-1, \OC\Files\Cache\Permissions::get(1, $user . '2')); + $this->assertEquals(-1, $this->permissionsCache->get(1, $user)); + $this->permissionsCache->set(1, $user, 1); + $this->assertEquals(1, $this->permissionsCache->get(1, $user)); + $this->assertEquals(-1, $this->permissionsCache->get(2, $user)); + $this->assertEquals(-1, $this->permissionsCache->get(1, $user . '2')); - \OC\Files\Cache\Permissions::set(1, $user, 2); - $this->assertEquals(2, \OC\Files\Cache\Permissions::get(1, $user)); + $this->permissionsCache->set(1, $user, 2); + $this->assertEquals(2, $this->permissionsCache->get(1, $user)); - \OC\Files\Cache\Permissions::set(2, $user, 1); - $this->assertEquals(1, \OC\Files\Cache\Permissions::get(2, $user)); + $this->permissionsCache->set(2, $user, 1); + $this->assertEquals(1, $this->permissionsCache->get(2, $user)); - \OC\Files\Cache\Permissions::remove(1, $user); - $this->assertEquals(-1, \OC\Files\Cache\Permissions::get(1, $user)); - \OC\Files\Cache\Permissions::remove(1, $user . '2'); - $this->assertEquals(1, \OC\Files\Cache\Permissions::get(2, $user)); + $this->permissionsCache->remove(1, $user); + $this->assertEquals(-1, $this->permissionsCache->get(1, $user)); + $this->permissionsCache->remove(1, $user . '2'); + $this->assertEquals(1, $this->permissionsCache->get(2, $user)); $expected = array(); foreach ($ids as $id) { - \OC\Files\Cache\Permissions::set($id, $user, 10 + $id); + $this->permissionsCache->set($id, $user, 10 + $id); $expected[$id] = 10 + $id; } - $this->assertEquals($expected, \OC\Files\Cache\Permissions::getMultiple($ids, $user)); + $this->assertEquals($expected, $this->permissionsCache->getMultiple($ids, $user)); - \OC\Files\Cache\Permissions::removeMultiple(array(10, 9), $user); + $this->permissionsCache->removeMultiple(array(10, 9), $user); unset($expected[9]); unset($expected[10]); - $this->assertEquals($expected, \OC\Files\Cache\Permissions::getMultiple($ids, $user)); + $this->assertEquals($expected, $this->permissionsCache->getMultiple($ids, $user)); - \OC\Files\Cache\Permissions::removeMultiple($ids, $user); + $this->permissionsCache->removeMultiple($ids, $user); } } diff --git a/tests/lib/files/cache/scanner.php b/tests/lib/files/cache/scanner.php index f784a82dad..c53da92727 100644 --- a/tests/lib/files/cache/scanner.php +++ b/tests/lib/files/cache/scanner.php @@ -112,7 +112,8 @@ class Scanner extends \UnitTestCase { function tearDown() { $ids = $this->cache->getAll(); - \OC\Files\Cache\Permissions::removeMultiple($ids, \OC_User::getUser()); + $permissionsCache = $this->storage->getPermissionsCache(); + $permissionsCache->removeMultiple($ids, \OC_User::getUser()); $this->cache->clear(); } } diff --git a/tests/lib/files/cache/watcher.php b/tests/lib/files/cache/watcher.php index a7076d9b0b..0125dd843b 100644 --- a/tests/lib/files/cache/watcher.php +++ b/tests/lib/files/cache/watcher.php @@ -23,7 +23,8 @@ class Watcher extends \PHPUnit_Framework_TestCase { foreach ($this->storages as $storage) { $cache = $storage->getCache(); $ids = $cache->getAll(); - \OC\Files\Cache\Permissions::removeMultiple($ids, \OC_User::getUser()); + $permissionsCache = $storage->getPermissionsCache(); + $permissionsCache->removeMultiple($ids, \OC_User::getUser()); $cache->clear(); } } diff --git a/tests/lib/files/view.php b/tests/lib/files/view.php index fa562cb15c..a173094b1c 100644 --- a/tests/lib/files/view.php +++ b/tests/lib/files/view.php @@ -21,7 +21,8 @@ class View extends \PHPUnit_Framework_TestCase { foreach ($this->storages as $storage) { $cache = $storage->getCache(); $ids = $cache->getAll(); - \OC\Files\Cache\Permissions::removeMultiple($ids, \OC_User::getUser()); + $permissionsCache = $storage->getPermissionsCache(); + $permissionsCache->removeMultiple($ids, \OC_User::getUser()); $cache->clear(); } } From 5569f07e8d68716f01267c9f41e5f0be299cc67a Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 15 Nov 2012 22:04:48 +0100 Subject: [PATCH 084/272] add OC_FilesystemView alias for compatibility --- lib/filesystemview.php | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 lib/filesystemview.php diff --git a/lib/filesystemview.php b/lib/filesystemview.php new file mode 100644 index 0000000000..37e2a737e0 --- /dev/null +++ b/lib/filesystemview.php @@ -0,0 +1,8 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. */ + +class OC_FilesystemView extends \OC\Files\View {} From 288ecf318cbb742b2c6a40d6c6afedfafb3cafd4 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 15 Nov 2012 22:08:08 +0100 Subject: [PATCH 085/272] add getFileInfo and getDirectoryContent to OC_Files for compatibility --- lib/files.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/files.php b/lib/files.php index e6b324f062..3abc8c4aaf 100644 --- a/lib/files.php +++ b/lib/files.php @@ -28,6 +28,14 @@ class OC_Files { static $tmpFiles = array(); + public function getFileInfo($path){ + return \OC\Files\Filesystem::getFileInfo($path); + } + + public function getDirectoryContent($path){ + return \OC\Files\Filesystem::getDirectoryContent($path); + } + /** * return the content of a file or return a zip file containning multiply files * From 54240140b10f2d718753445b656c9607967d7c64 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 16 Nov 2012 12:14:29 +0100 Subject: [PATCH 086/272] fix incorectly merged smb.php --- apps/files_external/lib/smb.php | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/files_external/lib/smb.php b/apps/files_external/lib/smb.php index bf0d780d27..a4b2338e3b 100644 --- a/apps/files_external/lib/smb.php +++ b/apps/files_external/lib/smb.php @@ -35,6 +35,7 @@ class SMB extends \OC\Files\Storage\StreamWrapper{ if(substr($this->share, -1, 1)=='/') { $this->share = substr($this->share,0,-1); } + } public function getId(){ return 'smb::' . $this->user . '@' . $this->host . '/' . $this->share . '/' . $this->root; From 3358bface5c20aeb4ad41dc1edffb895bc24af71 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 16 Nov 2012 12:24:28 +0100 Subject: [PATCH 087/272] fix incorectly merged versions.php --- apps/files_versions/lib/versions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/files_versions/lib/versions.php b/apps/files_versions/lib/versions.php index b365f45679..7fe3d5549e 100644 --- a/apps/files_versions/lib/versions.php +++ b/apps/files_versions/lib/versions.php @@ -189,7 +189,7 @@ class Storage { $i = 0; - $files_view = new \OC\Files\View('/' \OCP\User::getUser() . '/files'); + $files_view = new \OC\Files\View('/' . \OCP\User::getUser() . '/files'); $local_file = $files_view->getLocalFile($filename); foreach( $matches as $ma ) { From 40fae0acbf2e84782c273a82eb10a2ed9fdf8162 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sun, 18 Nov 2012 14:10:28 +0100 Subject: [PATCH 088/272] fix outdated permissions cache use in scanner --- lib/files/cache/scanner.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/files/cache/scanner.php b/lib/files/cache/scanner.php index eb947a2d53..404029ee2e 100644 --- a/lib/files/cache/scanner.php +++ b/lib/files/cache/scanner.php @@ -19,12 +19,18 @@ class Scanner { */ private $cache; + /** + * @var \OC\Files\Cache\Permissions $permissionsCache + */ + private $permissionsCache; + const SCAN_RECURSIVE = true; const SCAN_SHALLOW = false; public function __construct(\OC\Files\Storage\Storage $storage) { $this->storage = $storage; - $this->cache = new Cache($storage); + $this->cache = $storage->getCache(); + $this->permissionsCache = $storage->getPermissionsCache(); } /** @@ -67,7 +73,7 @@ class Scanner { } } $id = $this->cache->put($file, $data); - Permissions::set($id, \OC_User::getUser(), $data['permissions']); + $this->permissionsCache->set($id, \OC_User::getUser(), $data['permissions']); return $data; } From 77fdb16b7cb97f4e68949baf6796ef0e290b03f5 Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Thu, 22 Nov 2012 00:22:27 -0500 Subject: [PATCH 089/272] Remove incorrect extra parameter for init() call --- lib/util.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/util.php b/lib/util.php index 89fbcd05cd..c79afe1957 100755 --- a/lib/util.php +++ b/lib/util.php @@ -52,7 +52,7 @@ class OC_Util { } //jail the user into his "home" directory \OC\Files\Filesystem::mount('\OC\Files\Storage\Local', array('datadir' => $user_root), $user); - \OC\Files\Filesystem::init($user_dir, $user); + \OC\Files\Filesystem::init($user_dir); $quotaProxy=new OC_FileProxy_Quota(); $fileOperationProxy = new OC_FileProxy_FileOperations(); From b76d1afe193fae3bf94aed9a95e270b452e62eb8 Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Thu, 22 Nov 2012 00:44:48 -0500 Subject: [PATCH 090/272] Create public function initMountPoints() for initializing a specified user's mount points --- lib/files/filesystem.php | 53 ++++++++++++++++++++++++++++------------ lib/util.php | 16 ------------ 2 files changed, 37 insertions(+), 32 deletions(-) diff --git a/lib/files/filesystem.php b/lib/files/filesystem.php index ed2814211f..cee3cd883b 100644 --- a/lib/files/filesystem.php +++ b/lib/files/filesystem.php @@ -229,35 +229,50 @@ class Filesystem { self::$defaultInstance = new View($root); //load custom mount config - if (is_file(\OC::$SERVERROOT . '/config/mount.php')) { + self::initMountPoints(); + + self::$loaded = true; + + return true; + } + + /** + * Initialize system and personal mount points for a user + * + * @param string $user + */ + public static function initMountPoints($user = '') { + if ($user == '') { + $user = \OC_User::getUser(); + } + // Load system mount points + if (is_file(\OC::$SERVERROOT.'/config/mount.php')) { $mountConfig = include 'config/mount.php'; if (isset($mountConfig['global'])) { foreach ($mountConfig['global'] as $mountPoint => $options) { self::mount($options['class'], $options['options'], $mountPoint); } } - if (isset($mountConfig['group'])) { foreach ($mountConfig['group'] as $group => $mounts) { - if (\OC_Group::inGroup(\OC_User::getUser(), $group)) { + if (\OC_Group::inGroup($user, $group)) { foreach ($mounts as $mountPoint => $options) { - $mountPoint = self::setUserVars($mountPoint); + $mountPoint = self::setUserVars($user, $mountPoint); foreach ($options as &$option) { - $option = self::setUserVars($option); + $option = self::setUserVars($user, $option); } self::mount($options['class'], $options['options'], $mountPoint); } } } } - if (isset($mountConfig['user'])) { - foreach ($mountConfig['user'] as $user => $mounts) { - if ($user === 'all' or strtolower($user) === strtolower(\OC_User::getUser())) { + foreach ($mountConfig['user'] as $mountUser => $mounts) { + if ($user === 'all' or strtolower($mountUser) === strtolower($user)) { foreach ($mounts as $mountPoint => $options) { - $mountPoint = self::setUserVars($mountPoint); + $mountPoint = self::setUserVars($user, $mountPoint); foreach ($options as &$option) { - $option = self::setUserVars($option); + $option = self::setUserVars($user, $option); } self::mount($options['class'], $options['options'], $mountPoint); } @@ -265,10 +280,16 @@ class Filesystem { } } } - - self::$loaded = true; - - return true; + // Load personal mount points + $root = OC_User::getHome($user); + if (is_file($root.'/mount.php')) { + $mountConfig = include $root.'/mount.php'; + if (isset($mountConfig['user'][$user])) { + foreach ($mountConfig['user'][$user] as $mountPoint => $options) { + self::mount($options['class'], $options['options'], $mountPoint); + } + } + } } /** @@ -277,8 +298,8 @@ class Filesystem { * @param string $input * @return string */ - private static function setUserVars($input) { - return str_replace('$user', \OC_User::getUser(), $input); + private static function setUserVars($user, $input) { + return str_replace('$user', $user, $input); } /** diff --git a/lib/util.php b/lib/util.php index c79afe1957..25fdcef1ae 100755 --- a/lib/util.php +++ b/lib/util.php @@ -58,8 +58,6 @@ class OC_Util { $fileOperationProxy = new OC_FileProxy_FileOperations(); OC_FileProxy::register($quotaProxy); OC_FileProxy::register($fileOperationProxy); - // Load personal mount config - self::loadUserMountPoints($user); OC_Hook::emit('OC_Filesystem', 'setup', array('user' => $user, 'user_dir' => $user_dir)); } @@ -71,20 +69,6 @@ class OC_Util { self::$fsSetup=false; } - public static function loadUserMountPoints($user) { - $user_dir = '/'.$user.'/files'; - $user_root = OC_User::getHome($user); - $userdirectory = $user_root . '/files'; - if (is_file($user_root.'/mount.php')) { - $mountConfig = include $user_root.'/mount.php'; - if (isset($mountConfig['user'][$user])) { - foreach ($mountConfig['user'][$user] as $mountPoint => $options) { - \OC\Files\Filesystem::mount($options['class'], $options['options'], $mountPoint); - } - } - } - } - /** * get the current installed version of ownCloud * @return array From 208c6fd966dd5f9f387cdd1b4f030a2723144347 Mon Sep 17 00:00:00 2001 From: Thomas Mueller Date: Thu, 22 Nov 2012 10:21:48 +0100 Subject: [PATCH 091/272] fixing namespace --- lib/files/filesystem.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/files/filesystem.php b/lib/files/filesystem.php index cee3cd883b..9e8ce3ec18 100644 --- a/lib/files/filesystem.php +++ b/lib/files/filesystem.php @@ -281,7 +281,7 @@ class Filesystem { } } // Load personal mount points - $root = OC_User::getHome($user); + $root = \OC_User::getHome($user); if (is_file($root.'/mount.php')) { $mountConfig = include $root.'/mount.php'; if (isset($mountConfig['user'][$user])) { From e6cf082fe07e4e33c883bd5f9aaa0cc72b082741 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 21 Nov 2012 22:44:43 +0100 Subject: [PATCH 092/272] emit a hooks during the filesystem scan --- lib/files/cache/scanner.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/files/cache/scanner.php b/lib/files/cache/scanner.php index 404029ee2e..e650a220f5 100644 --- a/lib/files/cache/scanner.php +++ b/lib/files/cache/scanner.php @@ -14,6 +14,11 @@ class Scanner { */ private $storage; + /** + * @var string $storageId + */ + private $storageId; + /** * @var \OC\Files\Cache\Cache $cache */ @@ -29,6 +34,7 @@ class Scanner { public function __construct(\OC\Files\Storage\Storage $storage) { $this->storage = $storage; + $this->storageId = $this->storage->getId(); $this->cache = $storage->getCache(); $this->permissionsCache = $storage->getPermissionsCache(); } @@ -62,6 +68,7 @@ class Scanner { * @return array with metadata of the scanned file */ public function scanFile($file) { + \OC_Hook::emit('\OC\Files\Cache\Scanner', 'scan_file', array('path' => $file, 'storage' => $this->storageId)); $data = $this->getData($file); if ($file !== '') { $parent = dirname($file); @@ -85,6 +92,7 @@ class Scanner { * @return int the size of the scanned folder or -1 if the size is unknown at this stage */ public function scan($path, $recursive = self::SCAN_RECURSIVE) { + \OC_Hook::emit('\OC\Files\Cache\Scanner', 'scan_folder', array('path' => $path, 'storage' => $this->storageId)); $this->scanFile($path); $size = 0; From 186c9e77e89dcd057a311ed08df9e1bb9e13ea8f Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 21 Nov 2012 23:02:43 +0100 Subject: [PATCH 093/272] add Cache->getIncomplete for use in background scanning --- lib/files/cache/cache.php | 19 +++++++++++++++++++ tests/lib/files/cache/cache.php | 17 +++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/lib/files/cache/cache.php b/lib/files/cache/cache.php index 7c6bba4fad..b52c0f4067 100644 --- a/lib/files/cache/cache.php +++ b/lib/files/cache/cache.php @@ -402,4 +402,23 @@ class Cache { } return $ids; } + + /** + * find a folder in the cache which has not been fully scanned + * + * If multiply incomplete folders are in the cache, the one with the highest id will be returned, + * use the one with the highest id gives the best result with the background scanner, since that is most + * likely the folder where we stopped scanning previously + * + * @return string|bool the path of the folder or false when no folder matched + */ + public function getIncomplete(){ + $query = \OC_DB::prepare('SELECT `path` FROM `*PREFIX*filecache` WHERE `storage` = ? AND `size` = -1 ORDER BY `fileid` DESC LIMIT 1'); + $query->execute(array($this->storageId)); + if($row = $query->fetchRow()){ + return $row['path']; + }else{ + return false; + } + } } diff --git a/tests/lib/files/cache/cache.php b/tests/lib/files/cache/cache.php index 9c469aa937..e9105cd5ab 100644 --- a/tests/lib/files/cache/cache.php +++ b/tests/lib/files/cache/cache.php @@ -175,6 +175,23 @@ class Cache extends \UnitTestCase { $this->assertTrue($this->cache->inCache('folder/foobar/2')); } + function testGetIncomplete() { + $file1 = 'folder1'; + $file2 = 'folder2'; + $file3 = 'folder3'; + $file4 = 'folder4'; + $data = array('size' => 10, 'mtime' => 50, 'mimetype' => 'foo/bar'); + + $this->cache->put($file1, $data); + $data['size'] = -1; + $this->cache->put($file2, $data); + $this->cache->put($file3, $data); + $data['size'] = 12; + $this->cache->put($file4, $data); + + $this->assertEquals($file3, $this->cache->getIncomplete()); + } + public function tearDown() { $this->cache->clear(); } From 8687e0d346e7dad6890982c1a7b08befd8998aeb Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 21 Nov 2012 23:18:58 +0100 Subject: [PATCH 094/272] add Scanner->backgroundScan --- lib/files/cache/scanner.php | 10 ++++++++++ tests/lib/files/cache/scanner.php | 22 ++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/lib/files/cache/scanner.php b/lib/files/cache/scanner.php index e650a220f5..3c83b6528a 100644 --- a/lib/files/cache/scanner.php +++ b/lib/files/cache/scanner.php @@ -121,4 +121,14 @@ class Scanner { } return $size; } + + /** + * walk over any folders that are not fully scanned yet and scan them + */ + public function backgroundScan() { + while ($path = $this->cache->getIncomplete()) { + $this->scan($path); + $this->cache->correctFolderSize($path); + } + } } diff --git a/tests/lib/files/cache/scanner.php b/tests/lib/files/cache/scanner.php index c53da92727..6d26150d82 100644 --- a/tests/lib/files/cache/scanner.php +++ b/tests/lib/files/cache/scanner.php @@ -104,6 +104,28 @@ class Scanner extends \UnitTestCase { $this->assertNotEqual($cachedDataFolder['size'], -1); } + function testBackgroundScan(){ + $this->fillTestFolders(); + $this->storage->mkdir('folder2'); + $this->storage->file_put_contents('folder2/bar.txt', 'foobar'); + + $this->scanner->scan('', \OC\Files\Cache\Scanner::SCAN_SHALLOW); + $this->assertFalse($this->cache->inCache('folder/bar.txt')); + $this->assertFalse($this->cache->inCache('folder/2bar.txt')); + $cachedData = $this->cache->get(''); + $this->assertEquals(-1, $cachedData['size']); + + $this->scanner->backgroundScan(); + + $this->assertTrue($this->cache->inCache('folder/bar.txt')); + $this->assertTrue($this->cache->inCache('folder/bar.txt')); + + $cachedData = $this->cache->get(''); + $this->assertnotEquals(-1, $cachedData['size']); + + $this->assertFalse($this->cache->getIncomplete()); + } + function setUp() { $this->storage = new \OC\Files\Storage\Temporary(array()); $this->scanner = new \OC\Files\Cache\Scanner($this->storage); From 810563ae8aa5884a101cc64226c4f8c3c897132e Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 22 Nov 2012 12:44:31 +0100 Subject: [PATCH 095/272] don't redefine inherited functions as abstract --- lib/files/storage/common.php | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/lib/files/storage/common.php b/lib/files/storage/common.php index cf6fe64a4f..9978cea571 100644 --- a/lib/files/storage/common.php +++ b/lib/files/storage/common.php @@ -23,18 +23,12 @@ namespace OC\Files\Storage; abstract class Common implements \OC\Files\Storage\Storage { public function __construct($parameters) {} - abstract public function getId(); - abstract public function mkdir($path); - abstract public function rmdir($path); - abstract public function opendir($path); public function is_dir($path) { return $this->filetype($path)=='dir'; } public function is_file($path) { return $this->filetype($path)=='file'; } - abstract public function stat($path); - abstract public function filetype($path); public function filesize($path) { if($this->is_dir($path)) { return 0;//by definition @@ -46,8 +40,6 @@ abstract class Common implements \OC\Files\Storage\Storage { public function isCreatable($path) { return $this->isUpdatable($path); } - abstract public function isReadable($path); - abstract public function isUpdatable($path); public function isDeletable($path) { return $this->isUpdatable($path); } @@ -73,7 +65,6 @@ abstract class Common implements \OC\Files\Storage\Storage { } return $permissions; } - abstract public function file_exists($path); public function filemtime($path) { $stat = $this->stat($path); return $stat['mtime']; @@ -97,7 +88,6 @@ abstract class Common implements \OC\Files\Storage\Storage { $handle = $this->fopen($path, "w"); return fwrite($handle, $data); } - abstract public function unlink($path); public function rename($path1,$path2) { if($this->copy($path1,$path2)) { return $this->unlink($path1); @@ -111,7 +101,6 @@ abstract class Common implements \OC\Files\Storage\Storage { $count=\OC_Helper::streamCopy($source,$target); return $count>0; } - abstract public function fopen($path,$mode); /** * @brief Deletes all files and folders recursively within a directory @@ -180,7 +169,6 @@ abstract class Common implements \OC\Files\Storage\Storage { unlink($tmpFile); return $hash; } - abstract public function free_space($path); public function search($query) { return $this->searchInDir($query); } @@ -222,7 +210,6 @@ abstract class Common implements \OC\Files\Storage\Storage { } } } - abstract public function touch($path, $mtime=null); protected function searchInDir($query,$dir='') { $files=array(); From 8ce5e0d30d874bf59a81aa01202a497ea4cb8492 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 22 Nov 2012 13:14:39 +0100 Subject: [PATCH 096/272] don't throw fatal error in updater cache if setup failed for some reason --- tests/lib/files/cache/updater.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/lib/files/cache/updater.php b/tests/lib/files/cache/updater.php index 8059418dc1..b2eccf9130 100644 --- a/tests/lib/files/cache/updater.php +++ b/tests/lib/files/cache/updater.php @@ -61,7 +61,9 @@ class Updater extends \PHPUnit_Framework_TestCase { } public function tearDown() { - $this->cache->clear(); + if($this->cache){ + $this->cache->clear(); + } Filesystem::tearDown(); } From ad706229f5dbc6382ade61493e9f100a2dc07293 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 23 Nov 2012 00:17:18 +0100 Subject: [PATCH 097/272] explicitly sort files when using getFolderContents --- lib/files/cache/cache.php | 14 +++++++------- tests/lib/files/view.php | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/files/cache/cache.php b/lib/files/cache/cache.php index b52c0f4067..6b93673097 100644 --- a/lib/files/cache/cache.php +++ b/lib/files/cache/cache.php @@ -33,9 +33,9 @@ class Cache { * @param \OC\Files\Storage\Storage|string $storage */ public function __construct($storage) { - if($storage instanceof \OC\Files\Storage\Storage){ + if ($storage instanceof \OC\Files\Storage\Storage) { $this->storageId = $storage->getId(); - }else{ + } else { $this->storageId = $storage; } } @@ -87,7 +87,7 @@ class Cache { if ($fileId > -1) { $query = \OC_DB::prepare( 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted` - FROM `*PREFIX*filecache` WHERE parent = ?'); + FROM `*PREFIX*filecache` WHERE parent = ? ORDER BY `fileid` ASC'); $result = $query->execute(array($fileId)); return $result->fetchAll(); } else { @@ -364,7 +364,7 @@ class Cache { */ public function calculateFolderSize($path) { $id = $this->getId($path); - if($id === -1){ + if ($id === -1) { return 0; } $query = \OC_DB::prepare('SELECT `size` FROM `*PREFIX*filecache` WHERE `parent` = ? AND `storage` = ?'); @@ -412,12 +412,12 @@ class Cache { * * @return string|bool the path of the folder or false when no folder matched */ - public function getIncomplete(){ + public function getIncomplete() { $query = \OC_DB::prepare('SELECT `path` FROM `*PREFIX*filecache` WHERE `storage` = ? AND `size` = -1 ORDER BY `fileid` DESC LIMIT 1'); $query->execute(array($this->storageId)); - if($row = $query->fetchRow()){ + if ($row = $query->fetchRow()) { return $row['path']; - }else{ + } else { return false; } } diff --git a/tests/lib/files/view.php b/tests/lib/files/view.php index a173094b1c..ecfc803dc2 100644 --- a/tests/lib/files/view.php +++ b/tests/lib/files/view.php @@ -55,20 +55,20 @@ class View extends \PHPUnit_Framework_TestCase { $folderData = $rootView->getDirectoryContent('/'); /** * expected entries: - * folder * foo.png * foo.txt + * folder * substorage */ $this->assertEquals(4, count($folderData)); - $this->assertEquals('folder', $folderData[0]['name']); - $this->assertEquals('foo.png', $folderData[1]['name']); - $this->assertEquals('foo.txt', $folderData[2]['name']); + $this->assertEquals('foo.png', $folderData[0]['name']); + $this->assertEquals('foo.txt', $folderData[1]['name']); + $this->assertEquals('folder', $folderData[2]['name']); $this->assertEquals('substorage', $folderData[3]['name']); - $this->assertEquals($storageSize + $textSize, $folderData[0]['size']); - $this->assertEquals($imageSize, $folderData[1]['size']); - $this->assertEquals($textSize, $folderData[2]['size']); + $this->assertEquals($imageSize, $folderData[0]['size']); + $this->assertEquals($textSize, $folderData[1]['size']); + $this->assertEquals($storageSize + $textSize, $folderData[2]['size']); $this->assertEquals($storageSize, $folderData[3]['size']); $folderView = new \OC\Files\View('/folder'); From 8fb4dfd2eabcc267725d3c151d93644d6f28f010 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 23 Nov 2012 00:20:46 +0100 Subject: [PATCH 098/272] use background scanner --- apps/files/ajax/scan.php | 97 +++++++++++++++++++++++++--------------- apps/files/js/files.js | 38 +++++++--------- 2 files changed, 76 insertions(+), 59 deletions(-) diff --git a/apps/files/ajax/scan.php b/apps/files/ajax/scan.php index 91b20fa836..53ae4f5ff0 100644 --- a/apps/files/ajax/scan.php +++ b/apps/files/ajax/scan.php @@ -1,45 +1,70 @@ send('success', true); +OC_Hook::connect('\OC\Files\Cache\Scanner', 'scan_folder', 'ScanListener', 'folder'); +OC_Hook::connect('\OC\Files\Cache\Scanner', 'scan_file', 'ScanListener', 'file'); + +$absolutePath = \OC\Files\Filesystem::getView()->getAbsolutePath($dir); + +$mountPoints = \OC\Files\Filesystem::getMountPoints($absolutePath); +$mountPoints[] = \OC\Files\Filesystem::getMountPoint($absolutePath); +$mountPoints = array_reverse($mountPoints); //start with the mount point of $dir + +foreach ($mountPoints as $mountPoint) { + $storage = \OC\Files\Filesystem::getStorage($mountPoint); + error_log('scanning mp '.$mountPoint); + ScanListener::$mountPoints[$storage->getId()] = $mountPoint; + $scanner = $storage->getScanner(); + if ($force) { + $scanner->scan(''); } else { - OCP\JSON::success(array('data'=>array('done'=>true))); - exit; - } -} else { - if($checkOnly) { - OCP\JSON::success(array('data'=>array('done'=>false))); - exit; - } - if(isset($eventSource)) { - $eventSource->send('success', false); - } else { - exit; + $scanner->backgroundScan(); } } + +$eventSource->send('done', ScanListener::$fileCount); $eventSource->close(); + +class ScanListener { + + static public $fileCount = 0; + static public $lastCount = 0; + + /** + * @var \OC\Files\View $view + */ + static public $view; + + /** + * @var array $mountPoints map storage ids to mountpoints + */ + static public $mountPoints = array(); + + /** + * @var \OC_EventSource event source to pass events to + */ + static public $eventSource; + + static function folder($params) { + $internalPath = $params['path']; + $mountPoint = self::$mountPoints[$params['storage']]; + $path = self::$view->getRelativePath($mountPoint . $internalPath); + self::$eventSource->send('folder', $path); + } + + static function file() { + self::$fileCount++; + if (self::$fileCount > self::$lastCount + 20) { //send a count update every 20 files + self::$lastCount = self::$fileCount; + self::$eventSource->send('count', self::$fileCount); + } + } +} diff --git a/apps/files/js/files.js b/apps/files/js/files.js index bb80841055..f95bfa2bf8 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -602,12 +602,8 @@ $(document).ready(function() { }); }); - //check if we need to scan the filesystem - $.get(OC.filePath('files','ajax','scan.php'),{checkonly:'true'}, function(response) { - if(response.data.done){ - scanFiles(); - } - }, "json"); + //do a background scan if needed + scanFiles(); var lastWidth = 0; var breadcrumbs = []; @@ -676,27 +672,23 @@ $(document).ready(function() { resizeBreadcrumbs(true); }); -function scanFiles(force,dir){ +function scanFiles(force, dir){ if(!dir){ - dir=''; + dir = ''; } - force=!!force; //cast to bool - scanFiles.scanning=true; - $('#scanning-message').show(); - $('#fileList').remove(); - var scannerEventSource=new OC.EventSource(OC.filePath('files','ajax','scan.php'),{force:force,dir:dir}); - scanFiles.cancel=scannerEventSource.close.bind(scannerEventSource); - scannerEventSource.listen('scanning',function(data){ - $('#scan-count').text(t('files', '{count} files scanned', {count: data.count})); - $('#scan-current').text(data.file+'/'); + force = !!force; //cast to bool + scanFiles.scanning = true; + var scannerEventSource = new OC.EventSource(OC.filePath('files','ajax','scan.php'),{force:force,dir:dir}); + scanFiles.cancel = scannerEventSource.close.bind(scannerEventSource); + scannerEventSource.listen('count',function(count){ + console.log(count + 'files scanned') }); - scannerEventSource.listen('success',function(success){ + scannerEventSource.listen('folder',function(path){ + console.log('now scanning ' + path) + }); + scannerEventSource.listen('done',function(count){ scanFiles.scanning=false; - if(success){ - window.location.reload(); - }else{ - alert(t('files', 'error while scanning')); - } + console.log('done after ' + count + 'files'); }); } scanFiles.scanning=false; From c47bf9bbcefb0640c24ab0aa6ee79f4c52222b45 Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Sat, 24 Nov 2012 16:42:54 -0500 Subject: [PATCH 099/272] Add checks for storage object --- apps/files/ajax/scan.php | 15 +-- lib/files/view.php | 221 +++++++++++++++++++++------------------ 2 files changed, 127 insertions(+), 109 deletions(-) diff --git a/apps/files/ajax/scan.php b/apps/files/ajax/scan.php index 53ae4f5ff0..391b98608b 100644 --- a/apps/files/ajax/scan.php +++ b/apps/files/ajax/scan.php @@ -20,13 +20,14 @@ $mountPoints = array_reverse($mountPoints); //start with the mount point of $dir foreach ($mountPoints as $mountPoint) { $storage = \OC\Files\Filesystem::getStorage($mountPoint); - error_log('scanning mp '.$mountPoint); - ScanListener::$mountPoints[$storage->getId()] = $mountPoint; - $scanner = $storage->getScanner(); - if ($force) { - $scanner->scan(''); - } else { - $scanner->backgroundScan(); + if ($storage) { + ScanListener::$mountPoints[$storage->getId()] = $mountPoint; + $scanner = $storage->getScanner(); + if ($force) { + $scanner->scan(''); + } else { + $scanner->backgroundScan(); + } } } diff --git a/lib/files/view.php b/lib/files/view.php index e516a4fed6..468808566a 100644 --- a/lib/files/view.php +++ b/lib/files/view.php @@ -671,39 +671,41 @@ class View { * - versioned */ public function getFileInfo($path) { + $data = array(); $path = Filesystem::normalizePath($this->fakeRoot . '/' . $path); /** * @var \OC\Files\Storage\Storage $storage * @var string $internalPath */ list($storage, $internalPath) = Filesystem::resolvePath($path); - $cache = $storage->getCache(); + if ($storage) { + $cache = $storage->getCache(); - if (!$cache->inCache($internalPath)) { - $scanner = $storage->getScanner(); - $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW); - } else { - $watcher = new \OC\Files\Cache\Watcher($storage); - $watcher->checkUpdate($internalPath); - } - - $data = $cache->get($internalPath); - - if ($data['mimetype'] === 'httpd/unix-directory') { - //add the sizes of other mountpoints to the folder - $mountPoints = Filesystem::getMountPoints($path); - foreach ($mountPoints as $mountPoint) { - $subStorage = Filesystem::getStorage($mountPoint); - $subCache = $subStorage->getCache(); - $rootEntry = $subCache->get(''); - - $data['size'] += $rootEntry['size']; + if (!$cache->inCache($internalPath)) { + $scanner = $storage->getScanner(); + $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW); + } else { + $watcher = new \OC\Files\Cache\Watcher($storage); + $watcher->checkUpdate($internalPath); } + + $data = $cache->get($internalPath); + + if ($data['mimetype'] === 'httpd/unix-directory') { + //add the sizes of other mountpoints to the folder + $mountPoints = Filesystem::getMountPoints($path); + foreach ($mountPoints as $mountPoint) { + $subStorage = Filesystem::getStorage($mountPoint); + $subCache = $subStorage->getCache(); + $rootEntry = $subCache->get(''); + + $data['size'] += $rootEntry['size']; + } + } + + $permissionsCache = $storage->getPermissionsCache(); + $data['permissions'] = $permissionsCache->get($data['fileid'], \OC_User::getUser()); } - - $permissionsCache = $storage->getPermissionsCache(); - $data['permissions'] = $permissionsCache->get($data['fileid'], \OC_User::getUser()); - return $data; } @@ -714,75 +716,79 @@ class View { * @return array */ public function getDirectoryContent($directory, $mimetype_filter = '') { + $result = array(); $path = Filesystem::normalizePath($this->fakeRoot . '/' . $directory); /** * @var \OC\Files\Storage\Storage $storage * @var string $internalPath */ list($storage, $internalPath) = Filesystem::resolvePath($path); - $cache = $storage->getCache(); + if ($storage) { + $cache = $storage->getCache(); - if (!$cache->inCache($internalPath)) { - $scanner = $storage->getScanner(); - $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW); - } else { - $watcher = new \OC\Files\Cache\Watcher($storage); - $watcher->checkUpdate($internalPath); - } - - $files = $cache->getFolderContents($internalPath); //TODO: mimetype_filter - - //add a folder for any mountpoint in this directory and add the sizes of other mountpoints to the folders - $mountPoints = Filesystem::getMountPoints($path); - $dirLength = strlen($path); - foreach ($mountPoints as $mountPoint) { - $subStorage = Filesystem::getStorage($mountPoint); - $subCache = $subStorage->getCache(); - $rootEntry = $subCache->get(''); - - $relativePath = trim(substr($mountPoint, $dirLength), '/'); - if ($pos = strpos($relativePath, '/')) { //mountpoint inside subfolder add size to the correct folder - $entryName = substr($relativePath, 0, $pos); - foreach ($files as &$entry) { - if ($entry['name'] === $entryName) { - $entry['size'] += $rootEntry['size']; - } - } - } else { //mountpoint in this folder, add an entry for it - $rootEntry['name'] = $relativePath; - $files[] = $rootEntry; + if (!$cache->inCache($internalPath)) { + $scanner = $storage->getScanner(); + $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW); + } else { + $watcher = new \OC\Files\Cache\Watcher($storage); + $watcher->checkUpdate($internalPath); } - } - $ids = array(); + $files = $cache->getFolderContents($internalPath); //TODO: mimetype_filter - foreach ($files as $i => $file) { - $files[$i]['type'] = $file['mimetype'] === 'httpd/unix-directory' ? 'dir' : 'file'; - $ids[] = $file['fileid']; - } - $permissionsCache = $storage->getPermissionsCache(); + //add a folder for any mountpoint in this directory and add the sizes of other mountpoints to the folders + $mountPoints = Filesystem::getMountPoints($path); + $dirLength = strlen($path); + foreach ($mountPoints as $mountPoint) { + $subStorage = Filesystem::getStorage($mountPoint); + if ($subStorage) { + $subCache = $subStorage->getCache(); + $rootEntry = $subCache->get(''); - $permissions = $permissionsCache->getMultiple($ids, \OC_User::getUser()); - foreach ($files as $i => $file) { - $files[$i]['permissions'] = $permissions[$file['fileid']]; - } - - if ($mimetype_filter) { - foreach ($files as $file) { - if (strpos($mimetype_filter, '/')) { - if ($file['mimetype'] === $mimetype_filter) { - $result[] = $file; - } - } else { - if ($file['mimepart'] === $mimetype_filter) { - $result[] = $file; + $relativePath = trim(substr($mountPoint, $dirLength), '/'); + if ($pos = strpos($relativePath, '/')) { //mountpoint inside subfolder add size to the correct folder + $entryName = substr($relativePath, 0, $pos); + foreach ($files as &$entry) { + if ($entry['name'] === $entryName) { + $entry['size'] += $rootEntry['size']; + } + } + } else { //mountpoint in this folder, add an entry for it + $rootEntry['name'] = $relativePath; + $files[] = $rootEntry; } } } - } else { - $result = $files; - } + $ids = array(); + + foreach ($files as $i => $file) { + $files[$i]['type'] = $file['mimetype'] === 'httpd/unix-directory' ? 'dir' : 'file'; + $ids[] = $file['fileid']; + } + $permissionsCache = $storage->getPermissionsCache(); + + $permissions = $permissionsCache->getMultiple($ids, \OC_User::getUser()); + foreach ($files as $i => $file) { + $files[$i]['permissions'] = $permissions[$file['fileid']]; + } + + if ($mimetype_filter) { + foreach ($files as $file) { + if (strpos($mimetype_filter, '/')) { + if ($file['mimetype'] === $mimetype_filter) { + $result[] = $file; + } + } else { + if ($file['mimepart'] === $mimetype_filter) { + $result[] = $file; + } + } + } + } else { + $result = $files; + } + } return $result; } @@ -802,14 +808,18 @@ class View { * @var string $internalPath */ list($storage, $internalPath) = Filesystem::resolvePath($path); - $cache = $storage->getCache(); + if ($storage) { + $cache = $storage->getCache(); - if (!$cache->inCache($internalPath)) { - $scanner = $storage->getScanner(); - $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW); + if (!$cache->inCache($internalPath)) { + $scanner = $storage->getScanner(); + $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW); + } + + return $cache->put($internalPath, $data); + } else { + return -1; } - - return $cache->put($internalPath, $data); } /** @@ -843,29 +853,32 @@ class View { $mountPoint = Filesystem::getMountPoint($this->fakeRoot); $storage = Filesystem::getStorage($mountPoint); - $cache = $storage->getCache(); - - $results = $cache->$method($query); - foreach ($results as $result) { - if (substr($mountPoint . $result['path'], 0, $rootLength) === $this->fakeRoot) { - $result['path'] = substr($mountPoint . $result['path'], $rootLength); - $files[] = $result; - } - } - - $mountPoints = Filesystem::getMountPoints($this->fakeRoot); - foreach ($mountPoints as $mountPoint) { - $storage = Filesystem::getStorage($mountPoint); + if ($storage) { $cache = $storage->getCache(); - $relativeMountPoint = substr($mountPoint, $rootLength); $results = $cache->$method($query); foreach ($results as $result) { - $result['path'] = $relativeMountPoint . $result['path']; - $files[] = $result; + if (substr($mountPoint . $result['path'], 0, $rootLength) === $this->fakeRoot) { + $result['path'] = substr($mountPoint . $result['path'], $rootLength); + $files[] = $result; + } + } + + $mountPoints = Filesystem::getMountPoints($this->fakeRoot); + foreach ($mountPoints as $mountPoint) { + $storage = Filesystem::getStorage($mountPoint); + if ($storage) { + $cache = $storage->getCache(); + + $relativeMountPoint = substr($mountPoint, $rootLength); + $results = $cache->$method($query); + foreach ($results as $result) { + $result['path'] = $relativeMountPoint . $result['path']; + $files[] = $result; + } + } } } - return $files; } @@ -881,6 +894,10 @@ class View { * @var string $internalPath */ list($storage, $internalPath) = $this->resolvePath($path); - return $storage->getETag($internalPath); + if ($storage) { + return $storage->getETag($internalPath); + } else { + return null; + } } } From d3e37fa157faa59598e92a9aa02c6bbf818b60e0 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 23 Nov 2012 16:23:52 +0100 Subject: [PATCH 100/272] remove fileatime from common storage backend --- lib/fileproxy.php | 2 +- lib/files/storage/common.php | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/fileproxy.php b/lib/fileproxy.php index 2f81bde64a..52ec79b4bd 100644 --- a/lib/fileproxy.php +++ b/lib/fileproxy.php @@ -36,7 +36,7 @@ * The return value of the post-proxy will be used as the new result of the operation * The operations that have a post-proxy are: * file_get_contents, is_file, is_dir, file_exists, stat, is_readable, - * is_writable, fileatime, filemtime, filectime, file_get_contents, + * is_writable, filemtime, filectime, file_get_contents, * getMimeType, hash, fopen, free_space and search */ diff --git a/lib/files/storage/common.php b/lib/files/storage/common.php index 9978cea571..c891d0c3ad 100644 --- a/lib/files/storage/common.php +++ b/lib/files/storage/common.php @@ -69,10 +69,6 @@ abstract class Common implements \OC\Files\Storage\Storage { $stat = $this->stat($path); return $stat['mtime']; } - public function fileatime($path) { - $stat = $this->stat($path); - return $stat['atime']; - } public function file_get_contents($path) { $handle = $this->fopen($path, "r"); if(!$handle) { From 709aacfa0fef29692bff231f09625db5ba0bef6f Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sat, 24 Nov 2012 23:41:39 +0100 Subject: [PATCH 101/272] change behaviour of Filesystem::getMountPoint when a mountpoint is passed as path without trailing slash --- lib/files/filesystem.php | 8 +------- tests/lib/files/filesystem.php | 2 +- tests/lib/files/view.php | 12 ++++++++++++ 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/lib/files/filesystem.php b/lib/files/filesystem.php index 9e8ce3ec18..4e3eb1989b 100644 --- a/lib/files/filesystem.php +++ b/lib/files/filesystem.php @@ -144,13 +144,7 @@ class Filesystem { */ static public function getMountPoint($path) { \OC_Hook::emit(self::CLASSNAME, 'get_mountpoint', array('path' => $path)); - if (!$path) { - $path = '/'; - } - if ($path[0] !== '/') { - $path = '/' . $path; - } - $path = str_replace('//', '/', $path); + $path = self::normalizePath($path) . '/'; $foundMountPoint = ''; $mountPoints = array_keys(self::$mounts); foreach ($mountPoints as $mountpoint) { diff --git a/tests/lib/files/filesystem.php b/tests/lib/files/filesystem.php index 363426511b..5837093fdd 100644 --- a/tests/lib/files/filesystem.php +++ b/tests/lib/files/filesystem.php @@ -60,7 +60,7 @@ class Filesystem extends \PHPUnit_Framework_TestCase { $this->assertEquals('/',\OC\Files\Filesystem::getMountPoint('/')); $this->assertEquals('/some/',\OC\Files\Filesystem::getMountPoint('/some/folder')); $this->assertEquals('/some/',\OC\Files\Filesystem::getMountPoint('/some/')); - $this->assertEquals('/',\OC\Files\Filesystem::getMountPoint('/some')); + $this->assertEquals('/some/',\OC\Files\Filesystem::getMountPoint('/some')); list( , $internalPath)=\OC\Files\Filesystem::resolvePath('/some/folder'); $this->assertEquals('folder',$internalPath); } diff --git a/tests/lib/files/view.php b/tests/lib/files/view.php index ecfc803dc2..6f8d29c25b 100644 --- a/tests/lib/files/view.php +++ b/tests/lib/files/view.php @@ -71,6 +71,18 @@ class View extends \PHPUnit_Framework_TestCase { $this->assertEquals($storageSize + $textSize, $folderData[2]['size']); $this->assertEquals($storageSize, $folderData[3]['size']); + $folderData = $rootView->getDirectoryContent('/substorage'); + /** + * expected entries: + * foo.png + * foo.txt + * folder + */ + $this->assertEquals(3, count($folderData)); + $this->assertEquals('foo.png', $folderData[0]['name']); + $this->assertEquals('foo.txt', $folderData[1]['name']); + $this->assertEquals('folder', $folderData[2]['name']); + $folderView = new \OC\Files\View('/folder'); $this->assertEquals($rootView->getFileInfo('/folder'), $folderView->getFileInfo('/')); From cc5d8e56098189f49fbd68c598c11be2b8354846 Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Sat, 24 Nov 2012 20:29:57 -0500 Subject: [PATCH 102/272] Check if data variable in scanner isn't null before using it --- lib/files/cache/scanner.php | 42 ++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/lib/files/cache/scanner.php b/lib/files/cache/scanner.php index 3c83b6528a..9ee509bb13 100644 --- a/lib/files/cache/scanner.php +++ b/lib/files/cache/scanner.php @@ -70,17 +70,19 @@ class Scanner { public function scanFile($file) { \OC_Hook::emit('\OC\Files\Cache\Scanner', 'scan_file', array('path' => $file, 'storage' => $this->storageId)); $data = $this->getData($file); - if ($file !== '') { - $parent = dirname($file); - if ($parent === '.') { - $parent = ''; - } - if (!$this->cache->inCache($parent)) { - $this->scanFile($parent); + if ($data) { + if ($file !== '') { + $parent = dirname($file); + if ($parent === '.') { + $parent = ''; + } + if (!$this->cache->inCache($parent)) { + $this->scanFile($parent); + } } + $id = $this->cache->put($file, $data); + $this->permissionsCache->set($id, \OC_User::getUser(), $data['permissions']); } - $id = $this->cache->put($file, $data); - $this->permissionsCache->set($id, \OC_User::getUser(), $data['permissions']); return $data; } @@ -101,17 +103,19 @@ class Scanner { if ($file !== '.' and $file !== '..') { $child = ($path !== '') ? $path . '/' . $file : $file; $data = $this->scanFile($child); - if ($data['mimetype'] === 'httpd/unix-directory') { - if ($recursive === self::SCAN_RECURSIVE) { - $data['size'] = $this->scan($child, self::SCAN_RECURSIVE); - } else { - $data['size'] = -1; + if ($data) { + if ($data['mimetype'] === 'httpd/unix-directory') { + if ($recursive === self::SCAN_RECURSIVE) { + $data['size'] = $this->scan($child, self::SCAN_RECURSIVE); + } else { + $data['size'] = -1; + } + } + if ($data['size'] === -1) { + $size = -1; + } elseif ($size !== -1) { + $size += $data['size']; } - } - if ($data['size'] === -1) { - $size = -1; - } elseif ($size !== -1) { - $size += $data['size']; } } } From 0cfef83ed98951f852b07ce61dc10ee2e8266445 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sun, 25 Nov 2012 16:08:35 +0100 Subject: [PATCH 103/272] sort output of getFolderContent by name --- lib/files/cache/cache.php | 2 +- tests/lib/files/view.php | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/lib/files/cache/cache.php b/lib/files/cache/cache.php index 6b93673097..bc52f21d91 100644 --- a/lib/files/cache/cache.php +++ b/lib/files/cache/cache.php @@ -87,7 +87,7 @@ class Cache { if ($fileId > -1) { $query = \OC_DB::prepare( 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted` - FROM `*PREFIX*filecache` WHERE parent = ? ORDER BY `fileid` ASC'); + FROM `*PREFIX*filecache` WHERE parent = ? ORDER BY `name` ASC'); $result = $query->execute(array($fileId)); return $result->fetchAll(); } else { diff --git a/tests/lib/files/view.php b/tests/lib/files/view.php index 6f8d29c25b..ed08dcc114 100644 --- a/tests/lib/files/view.php +++ b/tests/lib/files/view.php @@ -55,33 +55,33 @@ class View extends \PHPUnit_Framework_TestCase { $folderData = $rootView->getDirectoryContent('/'); /** * expected entries: + * folder * foo.png * foo.txt - * folder * substorage */ $this->assertEquals(4, count($folderData)); - $this->assertEquals('foo.png', $folderData[0]['name']); - $this->assertEquals('foo.txt', $folderData[1]['name']); - $this->assertEquals('folder', $folderData[2]['name']); + $this->assertEquals('folder', $folderData[0]['name']); + $this->assertEquals('foo.png', $folderData[1]['name']); + $this->assertEquals('foo.txt', $folderData[2]['name']); $this->assertEquals('substorage', $folderData[3]['name']); - $this->assertEquals($imageSize, $folderData[0]['size']); - $this->assertEquals($textSize, $folderData[1]['size']); - $this->assertEquals($storageSize + $textSize, $folderData[2]['size']); + $this->assertEquals($storageSize + $textSize, $folderData[0]['size']); + $this->assertEquals($imageSize, $folderData[1]['size']); + $this->assertEquals($textSize, $folderData[2]['size']); $this->assertEquals($storageSize, $folderData[3]['size']); $folderData = $rootView->getDirectoryContent('/substorage'); /** * expected entries: + * folder * foo.png * foo.txt - * folder */ $this->assertEquals(3, count($folderData)); - $this->assertEquals('foo.png', $folderData[0]['name']); - $this->assertEquals('foo.txt', $folderData[1]['name']); - $this->assertEquals('folder', $folderData[2]['name']); + $this->assertEquals('folder', $folderData[0]['name']); + $this->assertEquals('foo.png', $folderData[1]['name']); + $this->assertEquals('foo.txt', $folderData[2]['name']); $folderView = new \OC\Files\View('/folder'); $this->assertEquals($rootView->getFileInfo('/folder'), $folderView->getFileInfo('/')); From a609992a75d1dad15398f55e22ad2244c78650dc Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sun, 25 Nov 2012 16:30:57 +0100 Subject: [PATCH 104/272] better check if we are passing a fileid to Cache::get --- lib/files/cache/cache.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/files/cache/cache.php b/lib/files/cache/cache.php index bc52f21d91..5aeb6f25af 100644 --- a/lib/files/cache/cache.php +++ b/lib/files/cache/cache.php @@ -47,7 +47,7 @@ class Cache { * @return array */ public function get($file) { - if (is_string($file)) { + if (is_string($file) or $file == '') { $where = 'WHERE `storage` = ? AND `path_hash` = ?'; $params = array($this->storageId, md5($file)); } else { //file id From dbbb357f62c1ba86bdff0fbe63e4cea9bc2977fc Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 30 Nov 2012 01:41:30 +0100 Subject: [PATCH 105/272] add upgrade path from old cache to preserve file id's --- apps/files/appinfo/app.php | 14 +++++++++-- apps/files/appinfo/version | 2 +- lib/files/cache/upgrade.php | 50 +++++++++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 3 deletions(-) create mode 100644 lib/files/cache/upgrade.php diff --git a/apps/files/appinfo/app.php b/apps/files/appinfo/app.php index b431ddfec0..fb64a80ec0 100644 --- a/apps/files/appinfo/app.php +++ b/apps/files/appinfo/app.php @@ -1,8 +1,18 @@ "files_index", "order" => 0, "href" => OCP\Util::linkTo( "files", "index.php" ), "icon" => OCP\Util::imagePath( "core", "places/home.svg" ), "name" => $l->t("Files") )); +OCP\App::addNavigationEntry(array("id" => "files_index", "order" => 0, "href" => OCP\Util::linkTo("files", "index.php"), "icon" => OCP\Util::imagePath("core", "places/home.svg"), "name" => $l->t("Files"))); OC_Search::registerProvider('OC_Search_Provider_File'); + +if (OC_User::isLoggedIn()) { + // update OC4.5 filecache to OC5 filecache, can't do this in update.php since it needs to happen for each user individually + $cacheVersion = (int)OCP\Config::getUserValue(OC_User::getUser(), 'files', 'cache_version', 4); + if ($cacheVersion < 5) { + \OC_Log::write('files', 'updating filecache to 5.0 for user ' . OC_User::getUser(), \OC_Log::INFO); + \OC\Files\Cache\Upgrade::upgrade(); + OCP\Config::setUserValue(OC_User::getUser(), 'files', 'cache_version', 5); + } +} diff --git a/apps/files/appinfo/version b/apps/files/appinfo/version index 0664a8fd29..2bf1ca5f54 100644 --- a/apps/files/appinfo/version +++ b/apps/files/appinfo/version @@ -1 +1 @@ -1.1.6 +1.1.7 diff --git a/lib/files/cache/upgrade.php b/lib/files/cache/upgrade.php new file mode 100644 index 0000000000..5be04b4207 --- /dev/null +++ b/lib/files/cache/upgrade.php @@ -0,0 +1,50 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Files\Cache; + +class Upgrade { + static $permissionsCaches = array(); + + static function upgrade() { + $insertQuery = \OC_DB::prepare('INSERT INTO `*PREFIX*filecache`( `fileid`, `storage`, `path`, `path_hash`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted` ) + VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'); + + $oldEntriesQuery = \OC_DB::prepare('SELECT * FROM `*PREFIX*fscache` ORDER BY `id` ASC'); //sort ascending to ensure the parent gets inserted before a child + $oldEntriesResult = $oldEntriesQuery->execute(); + + while ($row = $oldEntriesResult->fetchRow()) { + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($row['path']); + /** + * @var \OC\Files\Storage\Storage $storage + * @var string $internalPath; + */ + $pathHash = md5($internalPath); + $storageId = $storage->getId(); + $parentId = ($internalPath === '') ? -1 : $row['parent']; + + $insertQuery->execute(array($row['id'], $storageId, $internalPath, $pathHash, $parentId, $row['name'], $row['mimetype'], $row['mimepart'], $row['size'], $row['mtime'], $row['encrypted'])); + + $permissions = ($row['writable']) ? \OCP\PERMISSION_ALL : \OCP\PERMISSION_READ; + $permissionsCache = self::getPermissionsCache($storage); + $permissionsCache->set($row['id'], $row['user'], $permissions); + } + } + + /** + * @param \OC\Files\Storage\Storage $storage + * @return Permissions + */ + static function getPermissionsCache($storage) { + $storageId = $storage->getId(); + if (!isset(self::$permissionsCaches[$storageId])) { + self::$permissionsCaches[$storageId] = $storage->getPermissionsCache(); + } + return self::$permissionsCaches[$storageId]; + } +} From 702444b2422326388e494091e815fd3d9b03cc87 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sat, 1 Dec 2012 00:59:49 +0100 Subject: [PATCH 106/272] fail gracefully when no old filecache is present during upgrade --- lib/files/cache/upgrade.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/files/cache/upgrade.php b/lib/files/cache/upgrade.php index 5be04b4207..899f6f7ac8 100644 --- a/lib/files/cache/upgrade.php +++ b/lib/files/cache/upgrade.php @@ -16,7 +16,14 @@ class Upgrade { VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'); $oldEntriesQuery = \OC_DB::prepare('SELECT * FROM `*PREFIX*fscache` ORDER BY `id` ASC'); //sort ascending to ensure the parent gets inserted before a child - $oldEntriesResult = $oldEntriesQuery->execute(); + try{ + $oldEntriesResult = $oldEntriesQuery->execute(); + }catch(\Exception $e){ + return; + } + if(!$oldEntriesResult){ + return; + } while ($row = $oldEntriesResult->fetchRow()) { list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($row['path']); From 01eb5d2790638ab836adc95a9726b6a1003562b9 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sun, 2 Dec 2012 03:43:51 +0100 Subject: [PATCH 107/272] fix some edge cases while scanning the root of a storage --- lib/files/cache/scanner.php | 4 ++-- lib/files/filesystem.php | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/files/cache/scanner.php b/lib/files/cache/scanner.php index 9ee509bb13..4c0ec9617f 100644 --- a/lib/files/cache/scanner.php +++ b/lib/files/cache/scanner.php @@ -71,7 +71,7 @@ class Scanner { \OC_Hook::emit('\OC\Files\Cache\Scanner', 'scan_file', array('path' => $file, 'storage' => $this->storageId)); $data = $this->getData($file); if ($data) { - if ($file !== '') { + if ($file) { $parent = dirname($file); if ($parent === '.') { $parent = ''; @@ -101,7 +101,7 @@ class Scanner { if ($dh = $this->storage->opendir($path)) { while ($file = readdir($dh)) { if ($file !== '.' and $file !== '..') { - $child = ($path !== '') ? $path . '/' . $file : $file; + $child = ($path) ? $path . '/' . $file : $file; $data = $this->scanFile($child); if ($data) { if ($data['mimetype'] === 'httpd/unix-directory') { diff --git a/lib/files/filesystem.php b/lib/files/filesystem.php index 4e3eb1989b..724c83b361 100644 --- a/lib/files/filesystem.php +++ b/lib/files/filesystem.php @@ -143,8 +143,8 @@ class Filesystem { * @return string */ static public function getMountPoint($path) { - \OC_Hook::emit(self::CLASSNAME, 'get_mountpoint', array('path' => $path)); $path = self::normalizePath($path) . '/'; + \OC_Hook::emit(self::CLASSNAME, 'get_mountpoint', array('path' => $path)); $foundMountPoint = ''; $mountPoints = array_keys(self::$mounts); foreach ($mountPoints as $mountpoint) { @@ -205,7 +205,7 @@ class Filesystem { $mountpoint = self::getMountPoint($path); if ($mountpoint) { $storage = self::getStorage($mountpoint); - if ($mountpoint === $path) { + if ($mountpoint === $path or $mountpoint . '/' === $path) { $internalPath = ''; } else { $internalPath = substr($path, strlen($mountpoint)); From 18663100d9f68400fba1fc344874aacb62bb4659 Mon Sep 17 00:00:00 2001 From: Thomas Mueller Date: Sun, 2 Dec 2012 12:12:20 +0100 Subject: [PATCH 108/272] fixing syntax error + reformat the code --- apps/files_sharing/public.php | 358 ++++++++++++++++++---------------- 1 file changed, 185 insertions(+), 173 deletions(-) diff --git a/apps/files_sharing/public.php b/apps/files_sharing/public.php index fcf7063955..27d2b9dfa4 100644 --- a/apps/files_sharing/public.php +++ b/apps/files_sharing/public.php @@ -9,10 +9,10 @@ if (isset($_GET['token'])) { unset($_GET['file']); $qry = \OC_DB::prepare('SELECT `source` FROM `*PREFIX*sharing` WHERE `target` = ? LIMIT 1'); $filepath = $qry->execute(array($_GET['token']))->fetchOne(); - if(isset($filepath)) { + if (isset($filepath)) { $rootView = new \OC\Files\View(''); $info = $rootView->getFileInfo($filepath, ''); - if(strtolower($info['mimetype']) == 'httpd/unix-directory') { + if (strtolower($info['mimetype']) == 'httpd/unix-directory') { $_GET['dir'] = $filepath; } else { $_GET['file'] = $filepath; @@ -27,7 +27,7 @@ function getID($path) { if (substr(\OC\Files\Filesystem::getMountPoint($path), -7, 6) == "Shared") { $path_parts = explode('/', $path, 5); $user = $path_parts[1]; - $intPath = '/'.$path_parts[4]; + $intPath = '/' . $path_parts[4]; $query = \OC_DB::prepare('SELECT `item_source` FROM `*PREFIX*share` WHERE `uid_owner` = ? AND `file_target` = ? '); $result = $query->execute(array($user, $intPath)); $row = $result->fetchRow(); @@ -40,11 +40,12 @@ function getID($path) { return $fileSource; } + // Enf of backward compatibility /** * lookup file path and owner by fetching it from the fscache - * needed becaus OC_FileCache::getPath($id, $user) already requires the user + * needed because OC_FileCache::getPath($id, $user) already requires the user * @param int $id * @return array */ @@ -64,60 +65,62 @@ if (isset($_GET['t'])) { $type = $linkItem['item_type']; $fileSource = $linkItem['file_source']; $shareOwner = $linkItem['uid_owner']; - - if (OCP\User::userExists($shareOwner) && $fileSource != -1 ) { - + + if (OCP\User::userExists($shareOwner) && $fileSource != -1) { + $pathAndUser = getPathAndUser($linkItem['file_source']); $fileOwner = $pathAndUser['user']; - + //if this is a reshare check the file owner also exists - if ($shareOwner != $fileOwner && ! OCP\User::userExists($fileOwner)) { - OCP\Util::writeLog('share', 'original file owner '.$fileOwner.' does not exist for share '.$linkItem['id'], \OCP\Util::ERROR); - header('HTTP/1.0 404 Not Found'); - $tmpl = new OCP\Template('', '404', 'guest'); - $tmpl->printPage(); - exit(); + if ($shareOwner != $fileOwner && !OCP\User::userExists($fileOwner)) { + OCP\Util::writeLog('share', 'original file owner ' . $fileOwner . ' does not exist for share ' . $linkItem['id'], \OCP\Util::ERROR); + header('HTTP/1.0 404 Not Found'); + $tmpl = new OCP\Template('', '404', 'guest'); + $tmpl->printPage(); + exit(); } - + //mount filesystem of file owner OC_Util::setupFS($fileOwner); } } -} else if (isset($_GET['file']) || isset($_GET['dir'])) { - OCP\Util::writeLog('share', 'Missing token, trying fallback file/dir links', \OCP\Util::DEBUG); - if (isset($_GET['dir'])) { - $type = 'folder'; - $path = $_GET['dir']; - if(strlen($path)>1 and substr($path, -1, 1)==='/') { - $path=substr($path, 0, -1); +} else { + if (isset($_GET['file']) || isset($_GET['dir'])) { + OCP\Util::writeLog('share', 'Missing token, trying fallback file/dir links', \OCP\Util::DEBUG); + if (isset($_GET['dir'])) { + $type = 'folder'; + $path = $_GET['dir']; + if (strlen($path) > 1 and substr($path, -1, 1) === '/') { + $path = substr($path, 0, -1); + } + $baseDir = $path; + $dir = $baseDir; + } else { + $type = 'file'; + $path = $_GET['file']; + if (strlen($path) > 1 and substr($path, -1, 1) === '/') { + $path = substr($path, 0, -1); + } } - $baseDir = $path; - $dir = $baseDir; - } else { - $type = 'file'; - $path = $_GET['file']; - if(strlen($path)>1 and substr($path, -1, 1)==='/') { - $path=substr($path, 0, -1); - } - } - $shareOwner = substr($path, 1, strpos($path, '/', 1) - 1); - - if (OCP\User::userExists($shareOwner)) { - OC_Util::setupFS($shareOwner); - $fileSource = getId($path); - if ($fileSource != -1 ) { - $linkItem = OCP\Share::getItemSharedWithByLink($type, $fileSource, $shareOwner); - $pathAndUser['path'] = $path; - $path_parts = explode('/', $path, 5); - $pathAndUser['user'] = $path_parts[1]; - $fileOwner = $path_parts[1]; + $shareOwner = substr($path, 1, strpos($path, '/', 1) - 1); + + if (OCP\User::userExists($shareOwner)) { + OC_Util::setupFS($shareOwner); + $fileSource = getId($path); + if ($fileSource != -1) { + $linkItem = OCP\Share::getItemSharedWithByLink($type, $fileSource, $shareOwner); + $pathAndUser['path'] = $path; + $path_parts = explode('/', $path, 5); + $pathAndUser['user'] = $path_parts[1]; + $fileOwner = $path_parts[1]; + } } } } if ($linkItem) { if (!isset($linkItem['item_type'])) { - OCP\Util::writeLog('share', 'No item type set for share id: '.$linkItem['id'], \OCP\Util::ERROR); + OCP\Util::writeLog('share', 'No item type set for share id: ' . $linkItem['id'], \OCP\Util::ERROR); header('HTTP/1.0 404 Not Found'); $tmpl = new OCP\Template('', '404', 'guest'); $tmpl->printPage(); @@ -125,11 +128,13 @@ if ($linkItem) { } if (isset($linkItem['share_with'])) { // Authenticate share_with - $url = OCP\Util::linkToPublic('files').'&t='.$token; + $url = OCP\Util::linkToPublic('files') . '&t=' . $token; if (isset($_GET['file'])) { - $url .= '&file='.urlencode($_GET['file']); - } else if (isset($_GET['dir'])) { - $url .= '&dir='.urlencode($_GET['dir']); + $url .= '&file=' . urlencode($_GET['file']); + } else { + if (isset($_GET['dir'])) { + $url .= '&dir=' . urlencode($_GET['dir']); + } } if (isset($_POST['password'])) { $password = $_POST['password']; @@ -137,7 +142,7 @@ if ($linkItem) { // Check Password $forcePortable = (CRYPT_BLOWFISH != 1); $hasher = new PasswordHash(8, $forcePortable); - if (!($hasher->CheckPassword($password.OC_Config::getValue('passwordsalt', ''), $linkItem['share_with']))) { + if (!($hasher->CheckPassword($password . OC_Config::getValue('passwordsalt', ''), $linkItem['share_with']))) { $tmpl = new OCP\Template('files_sharing', 'authenticate', 'guest'); $tmpl->assign('URL', $url); $tmpl->assign('error', true); @@ -148,28 +153,30 @@ if ($linkItem) { $_SESSION['public_link_authenticated'] = $linkItem['id']; } } else { - OCP\Util::writeLog('share', 'Unknown share type '.$linkItem['share_type'].' for share id '.$linkItem['id'], \OCP\Util::ERROR); + OCP\Util::writeLog('share', 'Unknown share type ' . $linkItem['share_type'] . ' for share id ' . $linkItem['id'], \OCP\Util::ERROR); header('HTTP/1.0 404 Not Found'); $tmpl = new OCP\Template('', '404', 'guest'); $tmpl->printPage(); exit(); } - // Check if item id is set in session - } else if (!isset($_SESSION['public_link_authenticated']) || $_SESSION['public_link_authenticated'] !== $linkItem['id']) { - // Prompt for password - $tmpl = new OCP\Template('files_sharing', 'authenticate', 'guest'); - $tmpl->assign('URL', $url); - $tmpl->printPage(); - exit(); + // Check if item id is set in session + } else { + if (!isset($_SESSION['public_link_authenticated']) || $_SESSION['public_link_authenticated'] !== $linkItem['id']) { + // Prompt for password + $tmpl = new OCP\Template('files_sharing', 'authenticate', 'guest'); + $tmpl->assign('URL', $url); + $tmpl->printPage(); + exit(); + } } } - $basePath = substr($pathAndUser['path'] , strlen('/'.$fileOwner.'/files')); + $basePath = substr($pathAndUser['path'], strlen('/' . $fileOwner . '/files')); $path = $basePath; if (isset($_GET['path'])) { $path .= $_GET['path']; } if (!$path || !OC_Filesystem::isValidPath($path) || !OC_Filesystem::file_exists($path)) { - OCP\Util::writeLog('share', 'Invalid path '.$path.' for share id '.$linkItem['id'], \OCP\Util::ERROR); + OCP\Util::writeLog('share', 'Invalid path ' . $path . ' for share id ' . $linkItem['id'], \OCP\Util::ERROR); header('HTTP/1.0 404 Not Found'); $tmpl = new OCP\Template('', '404', 'guest'); $tmpl->printPage(); @@ -179,13 +186,15 @@ if ($linkItem) { $file = basename($path); // Download the file if (isset($_GET['download'])) { - if (isset($_GET['path']) && $_GET['path'] !== '' ) { - if ( isset($_GET['files']) ) { // download selected files + if (isset($_GET['path']) && $_GET['path'] !== '') { + if (isset($_GET['files'])) { // download selected files OC_Files::get($path, $_GET['files'], $_SERVER['REQUEST_METHOD'] == 'HEAD' ? true : false); - } else if (isset($_GET['path']) && $_GET['path'] != '' ) { // download a file from a shared directory - OC_Files::get($dir, $file, $_SERVER['REQUEST_METHOD'] == 'HEAD' ? true : false); - } else { // download the whole shared directory - OC_Files::get($dir, $file, $_SERVER['REQUEST_METHOD'] == 'HEAD' ? true : false); + } else { + if (isset($_GET['path']) && $_GET['path'] != '') { // download a file from a shared directory + OC_Files::get($dir, $file, $_SERVER['REQUEST_METHOD'] == 'HEAD' ? true : false); + } else { // download the whole shared directory + OC_Files::get($dir, $file, $_SERVER['REQUEST_METHOD'] == 'HEAD' ? true : false); + } } } else { // download a single shared file OC_Files::get($dir, $file, $_SERVER['REQUEST_METHOD'] == 'HEAD' ? true : false); @@ -206,7 +215,7 @@ if ($linkItem) { $getPath = ''; } // - $urlLinkIdentifiers= (isset($token)?'&t='.$token:'').(isset($_GET['dir'])?'&dir='.$_GET['dir']:'').(isset($_GET['file'])?'&file='.$_GET['file']:''); + $urlLinkIdentifiers = (isset($token) ? '&t=' . $token : '') . (isset($_GET['dir']) ? '&dir=' . $_GET['dir'] : '') . (isset($_GET['file']) ? '&file=' . $_GET['file'] : ''); // Show file list if (OC_Filesystem::is_dir($path)) { OCP\Util::addStyle('files', 'files'); @@ -219,9 +228,9 @@ if ($linkItem) { if ($i['type'] == 'file') { $fileinfo = pathinfo($i['name']); $i['basename'] = $fileinfo['filename']; - $i['extension'] = isset($fileinfo['extension']) ? ('.'.$fileinfo['extension']) : ''; + $i['extension'] = isset($fileinfo['extension']) ? ('.' . $fileinfo['extension']) : ''; } - $i['directory'] = '/'.substr($i['directory'], $rootLength); + $i['directory'] = '/' . substr($i['directory'], $rootLength); if ($i['directory'] == '/') { $i['directory'] = ''; } @@ -238,98 +247,31 @@ if ($linkItem) { //add subdir breadcrumbs foreach (explode('/', urldecode($_GET['path'])) as $i) { if ($i != '') { - $pathtohere .= '/'.$i; + $pathtohere .= '/' . $i; $breadcrumb[] = array('dir' => $pathtohere, 'name' => $i); - $path = $linkItem['path']; - if (isset($_GET['path'])) { - $path .= $_GET['path']; - $dir .= $_GET['path']; - if (!\OC\Files\Filesystem::file_exists($path)) { - header('HTTP/1.0 404 Not Found'); - $tmpl = new OCP\Template('', '404', 'guest'); - $tmpl->printPage(); - exit(); - } - } + $path = $linkItem['path']; + if (isset($_GET['path'])) { + $path .= $_GET['path']; + $dir .= $_GET['path']; + if (!\OC\Files\Filesystem::file_exists($path)) { + header('HTTP/1.0 404 Not Found'); + $tmpl = new OCP\Template('', '404', 'guest'); + $tmpl->printPage(); + exit(); + } + } - $list = new OCP\Template('files', 'part.list', ''); - $list->assign('files', $files, false); - $list->assign('publicListView', true); - $list->assign('baseURL', OCP\Util::linkToPublic('files').$urlLinkIdentifiers.'&path=', false); - $list->assign('downloadURL', OCP\Util::linkToPublic('files').$urlLinkIdentifiers.'&download&path=', false); - $breadcrumbNav = new OCP\Template('files', 'part.breadcrumb', '' ); - $breadcrumbNav->assign('breadcrumb', $breadcrumb, false); - $breadcrumbNav->assign('baseURL', OCP\Util::linkToPublic('files').$urlLinkIdentifiers.'&path=', false); - $folder = new OCP\Template('files', 'index', ''); - $folder->assign('fileList', $list->fetchPage(), false); - $folder->assign('breadcrumb', $breadcrumbNav->fetchPage(), false); - $folder->assign('isCreatable', false); - $folder->assign('permissions', 0); - $folder->assign('files', $files); - $folder->assign('uploadMaxFilesize', 0); - $folder->assign('uploadMaxHumanFilesize', 0); - $folder->assign('allowZipDownload', intval(OCP\Config::getSystemValue('allowZipDownload', true))); - $tmpl->assign('folder', $folder->fetchPage(), false); - $tmpl->assign('allowZipDownload', intval(OCP\Config::getSystemValue('allowZipDownload', true))); - $tmpl->assign('downloadURL', OCP\Util::linkToPublic('files').$urlLinkIdentifiers.'&download&path='.urlencode($getPath)); - } else { - // Show file preview if viewer is available - if ($type == 'file') { - $tmpl->assign('downloadURL', OCP\Util::linkToPublic('files').$urlLinkIdentifiers.'&download'); - } else { - OCP\Util::addStyle('files_sharing', 'public'); - OCP\Util::addScript('files_sharing', 'public'); - OCP\Util::addScript('files', 'fileactions'); - $tmpl = new OCP\Template('files_sharing', 'public', 'base'); - $tmpl->assign('owner', $uidOwner); - // Show file list - if (OC_Filesystem::is_dir($path)) { - OCP\Util::addStyle('files', 'files'); - OCP\Util::addScript('files', 'files'); - OCP\Util::addScript('files', 'filelist'); - $files = array(); - $rootLength = strlen($baseDir) + 1; - foreach (OC_Files::getDirectoryContent($path) as $i) { - $i['date'] = OCP\Util::formatDate($i['mtime']); - if ($i['type'] == 'file') { - $fileinfo = pathinfo($i['name']); - $i['basename'] = $fileinfo['filename']; - $i['extension'] = isset($fileinfo['extension']) ? ('.'.$fileinfo['extension']) : ''; - } - $i['directory'] = '/'.substr('/'.$uidOwner.'/files'.$i['directory'], $rootLength); - if ($i['directory'] == '/') { - $i['directory'] = ''; - } - $i['permissions'] = OCP\PERMISSION_READ; - $files[] = $i; - } - // Make breadcrumb - $breadcrumb = array(); - $pathtohere = ''; - $count = 1; - foreach (explode('/', $dir) as $i) { - if ($i != '') { - if ($i != $baseDir) { - $pathtohere .= '/'.$i; - } - if ( strlen($pathtohere) < strlen($_GET['dir'])) { - continue; - } - $breadcrumb[] = array('dir' => str_replace($_GET['dir'], "", $pathtohere, $count), 'name' => $i); - } - } $list = new OCP\Template('files', 'part.list', ''); $list->assign('files', $files, false); $list->assign('publicListView', true); - $list->assign('baseURL', OCP\Util::linkToPublic('files').'&dir='.urlencode($_GET['dir']).'&path=', false); - $list->assign('downloadURL', OCP\Util::linkToPublic('files').'&download&dir='.urlencode($_GET['dir']).'&path=', false); - $breadcrumbNav = new OCP\Template('files', 'part.breadcrumb', '' ); + $list->assign('baseURL', OCP\Util::linkToPublic('files') . $urlLinkIdentifiers . '&path=', false); + $list->assign('downloadURL', OCP\Util::linkToPublic('files') . $urlLinkIdentifiers . '&download&path=', false); + $breadcrumbNav = new OCP\Template('files', 'part.breadcrumb', ''); $breadcrumbNav->assign('breadcrumb', $breadcrumb, false); - $breadcrumbNav->assign('baseURL', OCP\Util::linkToPublic('files').'&dir='.urlencode($_GET['dir']).'&path=', false); + $breadcrumbNav->assign('baseURL', OCP\Util::linkToPublic('files') . $urlLinkIdentifiers . '&path=', false); $folder = new OCP\Template('files', 'index', ''); $folder->assign('fileList', $list->fetchPage(), false); $folder->assign('breadcrumb', $breadcrumbNav->fetchPage(), false); - $folder->assign('dir', basename($dir)); $folder->assign('isCreatable', false); $folder->assign('permissions', 0); $folder->assign('files', $files); @@ -337,43 +279,113 @@ if ($linkItem) { $folder->assign('uploadMaxHumanFilesize', 0); $folder->assign('allowZipDownload', intval(OCP\Config::getSystemValue('allowZipDownload', true))); $tmpl->assign('folder', $folder->fetchPage(), false); - $tmpl->assign('uidOwner', $uidOwner); - $tmpl->assign('dir', basename($dir)); - $tmpl->assign('filename', basename($path)); - $tmpl->assign('mimetype', OC_Filesystem::getMimeType($path)); $tmpl->assign('allowZipDownload', intval(OCP\Config::getSystemValue('allowZipDownload', true))); - if (isset($_GET['path'])) { - $getPath = $_GET['path']; - } else { - $getPath = ''; - } - $tmpl->assign('downloadURL', OCP\Util::linkToPublic('files').'&download&dir='.urlencode($_GET['dir']).'&path='.urlencode($getPath), false); + $tmpl->assign('downloadURL', OCP\Util::linkToPublic('files') . $urlLinkIdentifiers . '&download&path=' . urlencode($getPath)); } else { // Show file preview if viewer is available - $tmpl->assign('uidOwner', $uidOwner); - $tmpl->assign('dir', dirname($path)); - $tmpl->assign('filename', basename($path)); - $tmpl->assign('mimetype', OC_Filesystem::getMimeType($path)); if ($type == 'file') { - $tmpl->assign('downloadURL', OCP\Util::linkToPublic('files').'&file='.urlencode($_GET['file']).'&download', false); + $tmpl->assign('downloadURL', OCP\Util::linkToPublic('files') . $urlLinkIdentifiers . '&download'); } else { - if (isset($_GET['path'])) { - $getPath = $_GET['path']; + OCP\Util::addStyle('files_sharing', 'public'); + OCP\Util::addScript('files_sharing', 'public'); + OCP\Util::addScript('files', 'fileactions'); + $tmpl = new OCP\Template('files_sharing', 'public', 'base'); + $tmpl->assign('owner', $uidOwner); + // Show file list + if (OC_Filesystem::is_dir($path)) { + OCP\Util::addStyle('files', 'files'); + OCP\Util::addScript('files', 'files'); + OCP\Util::addScript('files', 'filelist'); + $files = array(); + $rootLength = strlen($baseDir) + 1; + foreach (OC_Files::getDirectoryContent($path) as $i) { + $i['date'] = OCP\Util::formatDate($i['mtime']); + if ($i['type'] == 'file') { + $fileinfo = pathinfo($i['name']); + $i['basename'] = $fileinfo['filename']; + $i['extension'] = isset($fileinfo['extension']) ? ('.' . $fileinfo['extension']) : ''; + } + $i['directory'] = '/' . substr('/' . $uidOwner . '/files' . $i['directory'], $rootLength); + if ($i['directory'] == '/') { + $i['directory'] = ''; + } + $i['permissions'] = OCP\PERMISSION_READ; + $files[] = $i; + } + // Make breadcrumb + $breadcrumb = array(); + $pathtohere = ''; + $count = 1; + foreach (explode('/', $dir) as $i) { + if ($i != '') { + if ($i != $baseDir) { + $pathtohere .= '/' . $i; + } + if (strlen($pathtohere) < strlen($_GET['dir'])) { + continue; + } + $breadcrumb[] = array('dir' => str_replace($_GET['dir'], "", $pathtohere, $count), 'name' => $i); + } + } + $list = new OCP\Template('files', 'part.list', ''); + $list->assign('files', $files, false); + $list->assign('publicListView', true); + $list->assign('baseURL', OCP\Util::linkToPublic('files') . '&dir=' . urlencode($_GET['dir']) . '&path=', false); + $list->assign('downloadURL', OCP\Util::linkToPublic('files') . '&download&dir=' . urlencode($_GET['dir']) . '&path=', false); + $breadcrumbNav = new OCP\Template('files', 'part.breadcrumb', ''); + $breadcrumbNav->assign('breadcrumb', $breadcrumb, false); + $breadcrumbNav->assign('baseURL', OCP\Util::linkToPublic('files') . '&dir=' . urlencode($_GET['dir']) . '&path=', false); + $folder = new OCP\Template('files', 'index', ''); + $folder->assign('fileList', $list->fetchPage(), false); + $folder->assign('breadcrumb', $breadcrumbNav->fetchPage(), false); + $folder->assign('dir', basename($dir)); + $folder->assign('isCreatable', false); + $folder->assign('permissions', 0); + $folder->assign('files', $files); + $folder->assign('uploadMaxFilesize', 0); + $folder->assign('uploadMaxHumanFilesize', 0); + $folder->assign('allowZipDownload', intval(OCP\Config::getSystemValue('allowZipDownload', true))); + $tmpl->assign('folder', $folder->fetchPage(), false); + $tmpl->assign('uidOwner', $uidOwner); + $tmpl->assign('dir', basename($dir)); + $tmpl->assign('filename', basename($path)); + $tmpl->assign('mimetype', OC_Filesystem::getMimeType($path)); + $tmpl->assign('allowZipDownload', intval(OCP\Config::getSystemValue('allowZipDownload', true))); + if (isset($_GET['path'])) { + $getPath = $_GET['path']; + } else { + $getPath = ''; + } + $tmpl->assign('downloadURL', OCP\Util::linkToPublic('files') . '&download&dir=' . urlencode($_GET['dir']) . '&path=' . urlencode($getPath), false); } else { - $getPath = ''; + // Show file preview if viewer is available + $tmpl->assign('uidOwner', $uidOwner); + $tmpl->assign('dir', dirname($path)); + $tmpl->assign('filename', basename($path)); + $tmpl->assign('mimetype', OC_Filesystem::getMimeType($path)); + if ($type == 'file') { + $tmpl->assign('downloadURL', OCP\Util::linkToPublic('files') . '&file=' . urlencode($_GET['file']) . '&download', false); + } else { + if (isset($_GET['path'])) { + $getPath = $_GET['path']; + } else { + $getPath = ''; + } + $tmpl->assign('downloadURL', OCP\Util::linkToPublic('files') . '&download&dir=' . urlencode($_GET['dir']) . '&path=' . urlencode($getPath), false); + } } - $tmpl->assign('downloadURL', OCP\Util::linkToPublic('files').'&download&dir='.urlencode($_GET['dir']).'&path='.urlencode($getPath), false); + $tmpl->printPage(); } } $tmpl->printPage(); } + exit(); + } else { + OCP\Util::writeLog('share', 'could not resolve linkItem', \OCP\Util::DEBUG); } - $tmpl->printPage(); } - exit(); -} else { - OCP\Util::writeLog('share', 'could not resolve linkItem', \OCP\Util::DEBUG); } header('HTTP/1.0 404 Not Found'); $tmpl = new OCP\Template('', '404', 'guest'); $tmpl->printPage(); + From 317cd4c70a3042c4e16424cafb4a8b34c8cd8c3c Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 6 Dec 2012 17:49:35 +0100 Subject: [PATCH 109/272] catch error if old filecache table is not present during upgrade --- lib/files/cache/upgrade.php | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/files/cache/upgrade.php b/lib/files/cache/upgrade.php index 899f6f7ac8..ebac387de9 100644 --- a/lib/files/cache/upgrade.php +++ b/lib/files/cache/upgrade.php @@ -15,13 +15,17 @@ class Upgrade { $insertQuery = \OC_DB::prepare('INSERT INTO `*PREFIX*filecache`( `fileid`, `storage`, `path`, `path_hash`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted` ) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'); - $oldEntriesQuery = \OC_DB::prepare('SELECT * FROM `*PREFIX*fscache` ORDER BY `id` ASC'); //sort ascending to ensure the parent gets inserted before a child - try{ - $oldEntriesResult = $oldEntriesQuery->execute(); - }catch(\Exception $e){ + try { + $oldEntriesQuery = \OC_DB::prepare('SELECT * FROM `*PREFIX*fscache` ORDER BY `id` ASC'); //sort ascending to ensure the parent gets inserted before a child + } catch (\Exception $e) { return; } - if(!$oldEntriesResult){ + try { + $oldEntriesResult = $oldEntriesQuery->execute(); + } catch (\Exception $e) { + return; + } + if (!$oldEntriesResult) { return; } From 8635699db932959ac147193473b014406b0e9e09 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 11 Dec 2012 01:06:21 +0100 Subject: [PATCH 110/272] fix cache behaviour for non existing files --- lib/files/view.php | 26 ++++++++++++++------------ tests/lib/files/cache/cache.php | 5 +++++ tests/lib/files/view.php | 3 +++ 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/lib/files/view.php b/lib/files/view.php index 468808566a..994dbcc85c 100644 --- a/lib/files/view.php +++ b/lib/files/view.php @@ -691,20 +691,22 @@ class View { $data = $cache->get($internalPath); - if ($data['mimetype'] === 'httpd/unix-directory') { - //add the sizes of other mountpoints to the folder - $mountPoints = Filesystem::getMountPoints($path); - foreach ($mountPoints as $mountPoint) { - $subStorage = Filesystem::getStorage($mountPoint); - $subCache = $subStorage->getCache(); - $rootEntry = $subCache->get(''); + if ($data) { + if ($data['mimetype'] === 'httpd/unix-directory') { + //add the sizes of other mountpoints to the folder + $mountPoints = Filesystem::getMountPoints($path); + foreach ($mountPoints as $mountPoint) { + $subStorage = Filesystem::getStorage($mountPoint); + $subCache = $subStorage->getCache(); + $rootEntry = $subCache->get(''); - $data['size'] += $rootEntry['size']; + $data['size'] += $rootEntry['size']; + } } - } - $permissionsCache = $storage->getPermissionsCache(); - $data['permissions'] = $permissionsCache->get($data['fileid'], \OC_User::getUser()); + $permissionsCache = $storage->getPermissionsCache(); + $data['permissions'] = $permissionsCache->get($data['fileid'], \OC_User::getUser()); + } } return $data; } @@ -888,7 +890,7 @@ class View { * @param string $path * @return string */ - public function getETag($path){ + public function getETag($path) { /** * @var Storage\Storage $storage * @var string $internalPath diff --git a/tests/lib/files/cache/cache.php b/tests/lib/files/cache/cache.php index e9105cd5ab..a2b131ac0a 100644 --- a/tests/lib/files/cache/cache.php +++ b/tests/lib/files/cache/cache.php @@ -192,6 +192,11 @@ class Cache extends \UnitTestCase { $this->assertEquals($file3, $this->cache->getIncomplete()); } + function testNonExisting() { + $this->assertFalse($this->cache->get('foo.txt')); + $this->assertEquals(array(), $this->cache->getFolderContents('foo')); + } + public function tearDown() { $this->cache->clear(); } diff --git a/tests/lib/files/view.php b/tests/lib/files/view.php index ed08dcc114..1b8f6dc1e8 100644 --- a/tests/lib/files/view.php +++ b/tests/lib/files/view.php @@ -92,6 +92,9 @@ class View extends \PHPUnit_Framework_TestCase { $cachedData = $rootView->getFileInfo('/foo.txt'); $this->assertTrue($cachedData['encrypted']); $this->assertEquals($cachedData['fileid'], $id); + + $this->assertFalse($rootView->getFileInfo('/non/existing')); + $this->assertEquals(array(), $rootView->getDirectoryContent('/non/existing')); } public function testAutoScan() { From 438d3c21f651240e416b6e50ad49f72dd079a9ae Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 11 Dec 2012 01:24:53 +0100 Subject: [PATCH 111/272] actually connect the filesystem hooks to the cache updater --- lib/files/filesystem.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/files/filesystem.php b/lib/files/filesystem.php index 724c83b361..c5b56ba9f0 100644 --- a/lib/files/filesystem.php +++ b/lib/files/filesystem.php @@ -685,4 +685,8 @@ class Filesystem { \OC_Hook::connect('OC_Filesystem', 'post_delete', 'OC_Filesystem', 'removeETagHook'); \OC_Hook::connect('OC_Filesystem', 'post_rename', 'OC_Filesystem', 'removeETagHook'); +\OC_Hook::connect('OC_Filesystem', 'post_write', '\OC\Files\Cache\Updater', 'writeHook'); +\OC_Hook::connect('OC_Filesystem', 'post_delete', '\OC\Files\Cache\Updater', 'deleteHook'); +\OC_Hook::connect('OC_Filesystem', 'post_rename', '\OC\Files\Cache\Updater', 'renameHook'); + \OC_Util::setupFS(); From cc0a0df88b3b6fdd2b1f9c85349683eb640f9670 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 11 Dec 2012 01:25:21 +0100 Subject: [PATCH 112/272] one additional test case for the cache updater --- tests/lib/files/cache/updater.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/lib/files/cache/updater.php b/tests/lib/files/cache/updater.php index b2eccf9130..d19966c191 100644 --- a/tests/lib/files/cache/updater.php +++ b/tests/lib/files/cache/updater.php @@ -99,6 +99,11 @@ class Updater extends \PHPUnit_Framework_TestCase { $this->assertFalse($this->cache->inCache('foo.txt')); $cachedData = $this->cache->get(''); $this->assertEquals(2 * $textSize + $imageSize, $cachedData['size']); + + Filesystem::mkdir('bar_folder'); + $this->assertTrue($this->cache->inCache('bar_folder')); + Filesystem::rmdir('bar_folder'); + $this->assertFalse($this->cache->inCache('bar_folder')); } public function testRename() { From c6a5ce54a706efe6d157ec30a3960a3ebf8719a7 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sat, 15 Dec 2012 02:22:09 +0100 Subject: [PATCH 113/272] these functions should be static --- lib/files.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/files.php b/lib/files.php index 768a2ad219..2d7e335e96 100644 --- a/lib/files.php +++ b/lib/files.php @@ -28,11 +28,11 @@ class OC_Files { static $tmpFiles = array(); - public function getFileInfo($path){ + static public function getFileInfo($path){ return \OC\Files\Filesystem::getFileInfo($path); } - public function getDirectoryContent($path){ + static public function getDirectoryContent($path){ return \OC\Files\Filesystem::getDirectoryContent($path); } From bc52f121626bb1bb1f67bc1f876d0420d153b3d1 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sat, 15 Dec 2012 02:29:34 +0100 Subject: [PATCH 114/272] dont insert and entry in the filecache during upgrade if the id already exists in the filecache most likely the result from an incompelte upgrade --- lib/files/cache/upgrade.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/files/cache/upgrade.php b/lib/files/cache/upgrade.php index ebac387de9..9219deebef 100644 --- a/lib/files/cache/upgrade.php +++ b/lib/files/cache/upgrade.php @@ -29,7 +29,13 @@ class Upgrade { return; } + $checkExistingQuery = \OC_DB::prepare('SELECT `fileid` FROM `*PREFIX*filecache` WHERE `fileid` = ?'); + while ($row = $oldEntriesResult->fetchRow()) { + if($checkExistingQuery->execute(array($row['id']))->fetchRow()){ + continue; + } + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($row['path']); /** * @var \OC\Files\Storage\Storage $storage From 4be039e6cbc0a826b07fc2af1a6c6d94888a9187 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sat, 15 Dec 2012 03:10:56 +0100 Subject: [PATCH 115/272] Filecache Watcher: use scan or scanFile based on the current item, not the cached item --- lib/files/cache/watcher.php | 6 ++++-- tests/lib/files/cache/watcher.php | 34 +++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/lib/files/cache/watcher.php b/lib/files/cache/watcher.php index d6039d9945..31059ec7f5 100644 --- a/lib/files/cache/watcher.php +++ b/lib/files/cache/watcher.php @@ -44,12 +44,14 @@ class Watcher { public function checkUpdate($path) { $cachedEntry = $this->cache->get($path); if ($this->storage->hasUpdated($path, $cachedEntry['mtime'])) { - if ($cachedEntry['mimetype'] === 'httpd/unix-directory') { + if ($this->storage->is_dir($path)) { $this->scanner->scan($path, Scanner::SCAN_SHALLOW); - $this->cleanFolder($path); } else { $this->scanner->scanFile($path); } + if ($cachedEntry['mimetype'] === 'httpd/unix-directory') { + $this->cleanFolder($path); + } $this->cache->correctFolderSize($path); } } diff --git a/tests/lib/files/cache/watcher.php b/tests/lib/files/cache/watcher.php index 0125dd843b..07c8ac3640 100644 --- a/tests/lib/files/cache/watcher.php +++ b/tests/lib/files/cache/watcher.php @@ -63,6 +63,40 @@ class Watcher extends \PHPUnit_Framework_TestCase { $this->assertFalse($cache->inCache('folder/bar2.txt')); } + public function testFileToFolder() { + $storage = $this->getTestStorage(); + $cache = $storage->getCache(); + $updater = new \OC\Files\Cache\Watcher($storage); + + //set the mtime to the past so it can detect an mtime change + $cache->put('', array('mtime' => 10)); + + $storage->unlink('foo.txt'); + $storage->rename('folder','foo.txt'); + $updater->checkUpdate(''); + + $entry= $cache->get('foo.txt'); + $this->assertEquals(-1, $entry['size']); + $this->assertEquals('httpd/unix-directory', $entry['mimetype']); + $this->assertFalse($cache->inCache('folder')); + $this->assertFalse($cache->inCache('folder/bar.txt')); + + $storage = $this->getTestStorage(); + $cache = $storage->getCache(); + $updater = new \OC\Files\Cache\Watcher($storage); + + //set the mtime to the past so it can detect an mtime change + $cache->put('foo.txt', array('mtime' => 10)); + + $storage->unlink('foo.txt'); + $storage->rename('folder','foo.txt'); + $updater->checkUpdate('foo.txt'); + + $entry= $cache->get('foo.txt'); + $this->assertEquals('httpd/unix-directory', $entry['mimetype']); + $this->assertTrue($cache->inCache('foo.txt/bar.txt')); + } + /** * @param bool $scan * @return \OC\Files\Storage\Storage From cf3665057c0c195bb3a0e2d9f8f8746f5d2f5787 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sat, 15 Dec 2012 03:20:50 +0100 Subject: [PATCH 116/272] make sure folders that are not fully scanned are at least shallow scanned when we open them the fact that they are in the cache does not mean they are scanned --- lib/files/view.php | 2 +- tests/lib/files/view.php | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/files/view.php b/lib/files/view.php index 994dbcc85c..6d917bb585 100644 --- a/lib/files/view.php +++ b/lib/files/view.php @@ -728,7 +728,7 @@ class View { if ($storage) { $cache = $storage->getCache(); - if (!$cache->inCache($internalPath)) { + if ($cache->getStatus($internalPath) < Cache\Cache::COMPLETE) { $scanner = $storage->getScanner(); $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW); } else { diff --git a/tests/lib/files/view.php b/tests/lib/files/view.php index 1b8f6dc1e8..712166ab32 100644 --- a/tests/lib/files/view.php +++ b/tests/lib/files/view.php @@ -97,6 +97,19 @@ class View extends \PHPUnit_Framework_TestCase { $this->assertEquals(array(), $rootView->getDirectoryContent('/non/existing')); } + function testCacheIncompleteFolder() { + $storage1 = $this->getTestStorage(false); + \OC\Files\Filesystem::mount($storage1, array(), '/'); + $rootView = new \OC\Files\View(''); + + $entries = $rootView->getDirectoryContent('/'); + $this->assertEquals(3, count($entries)); + + // /folder will already be in the cache but not scanned + $entries = $rootView->getDirectoryContent('/folder'); + $this->assertEquals(1, count($entries)); + } + public function testAutoScan() { $storage1 = $this->getTestStorage(false); $storage2 = $this->getTestStorage(false); From 8951769cae5f1acc9b709ac676fffe26513d14f6 Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Sat, 15 Dec 2012 17:16:26 -0500 Subject: [PATCH 117/272] Check sub storage isn't null or false --- lib/files/view.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/files/view.php b/lib/files/view.php index 6d917bb585..592c484a21 100644 --- a/lib/files/view.php +++ b/lib/files/view.php @@ -697,10 +697,11 @@ class View { $mountPoints = Filesystem::getMountPoints($path); foreach ($mountPoints as $mountPoint) { $subStorage = Filesystem::getStorage($mountPoint); - $subCache = $subStorage->getCache(); - $rootEntry = $subCache->get(''); - - $data['size'] += $rootEntry['size']; + if ($subStorage) { + $subCache = $subStorage->getCache(); + $rootEntry = $subCache->get(''); + $data['size'] += $rootEntry['size']; + } } } From b12abb2c94072ab5b84d0477ecb7ce9789bdda0d Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sat, 15 Dec 2012 23:28:07 +0100 Subject: [PATCH 118/272] use numeric ids for storages in the filecache --- db_structure.xml | 40 +++++++++++++++++++++++++++++++-- lib/files/cache/cache.php | 44 ++++++++++++++++++++++++++++--------- lib/files/cache/upgrade.php | 21 ++++++++++++++++-- lib/util.php | 2 +- 4 files changed, 92 insertions(+), 15 deletions(-) diff --git a/db_structure.xml b/db_structure.xml index 2856ee4ff9..aa0916264c 100644 --- a/db_structure.xml +++ b/db_structure.xml @@ -58,6 +58,42 @@
+ + + *dbprefix*storages + + + + + id + text + + true + 64 + + + + numeric_id + integer + 0 + true + 1 + 4 + + + + storages_id_index + true + + id + ascending + + + + + +
+ *dbprefix*filecache @@ -75,10 +111,10 @@ storage - text + integer true - 64 + 4 diff --git a/lib/files/cache/cache.php b/lib/files/cache/cache.php index 5aeb6f25af..3ebae9baa5 100644 --- a/lib/files/cache/cache.php +++ b/lib/files/cache/cache.php @@ -29,6 +29,13 @@ class Cache { */ private $storageId; + /** + * numeric storage id + * + * @var int $numericId + */ + private $numericId; + /** * @param \OC\Files\Storage\Storage|string $storage */ @@ -38,6 +45,20 @@ class Cache { } else { $this->storageId = $storage; } + + $query = \OC_DB::prepare('SELECT `numeric_id` FROM `*PREFIX*storages` WHERE `id` = ?'); + $result = $query->execute(array($this->storageId)); + if ($row = $result->fetchRow()) { + $this->numericId = $row['numeric_id']; + } else { + $query = \OC_DB::prepare('INSERT INTO `*PREFIX*storages`(`id`) VALUES(?)'); + $query->execute(array($this->storageId)); + $this->numericId = \OC_DB::insertid('*PREFIX*filecache'); + } + } + + public function getNumericStorageId() { + return $this->numericId; } /** @@ -49,7 +70,7 @@ class Cache { public function get($file) { if (is_string($file) or $file == '') { $where = 'WHERE `storage` = ? AND `path_hash` = ?'; - $params = array($this->storageId, md5($file)); + $params = array($this->numericId, md5($file)); } else { //file id $where = 'WHERE `fileid` = ?'; $params = array($file); @@ -128,7 +149,7 @@ class Cache { list($queryParts, $params) = $this->buildParts($data); $queryParts[] = '`storage`'; - $params[] = $this->storageId; + $params[] = $this->numericId; $valuesPlaceholder = array_fill(0, count($queryParts), '?'); $query = \OC_DB::prepare('INSERT INTO `*PREFIX*filecache`(' . implode(', ', $queryParts) . ') VALUES(' . implode(', ', $valuesPlaceholder) . ')'); @@ -189,7 +210,7 @@ class Cache { $pathHash = md5($file); $query = \OC_DB::prepare('SELECT `fileid` FROM `*PREFIX*filecache` WHERE `storage` = ? AND `path_hash` = ?'); - $result = $query->execute(array($this->storageId, $pathHash)); + $result = $query->execute(array($this->numericId, $pathHash)); if ($row = $result->fetchRow()) { return $row['fileid']; @@ -273,7 +294,10 @@ class Cache { * remove all entries for files that are stored on the storage from the cache */ public function clear() { - $query = \OC_DB::prepare('DELETE FROM `*PREFIX*filecache` WHERE storage=?'); + $query = \OC_DB::prepare('DELETE FROM `*PREFIX*filecache` WHERE storage = ?'); + $query->execute(array($this->numericId)); + + $query = \OC_DB::prepare('DELETE FROM `*PREFIX*storages` WHERE id = ?'); $query->execute(array($this->storageId)); } @@ -285,7 +309,7 @@ class Cache { public function getStatus($file) { $pathHash = md5($file); $query = \OC_DB::prepare('SELECT `size` FROM `*PREFIX*filecache` WHERE `storage` = ? AND `path_hash` = ?'); - $result = $query->execute(array($this->storageId, $pathHash)); + $result = $query->execute(array($this->numericId, $pathHash)); if ($row = $result->fetchRow()) { if ((int)$row['size'] === -1) { return self::SHALLOW; @@ -312,7 +336,7 @@ class Cache { SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted` FROM `*PREFIX*filecache` WHERE `name` LIKE ? AND `storage` = ?' ); - $result = $query->execute(array($pattern, $this->storageId)); + $result = $query->execute(array($pattern, $this->numericId)); $files = array(); while ($row = $result->fetchRow()) { $files[] = $row; @@ -336,7 +360,7 @@ class Cache { SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted` FROM `*PREFIX*filecache` WHERE ' . $where . ' AND `storage` = ?' ); - $result = $query->execute(array($mimetype, $this->storageId)); + $result = $query->execute(array($mimetype, $this->numericId)); return $result->fetchAll(); } @@ -368,7 +392,7 @@ class Cache { return 0; } $query = \OC_DB::prepare('SELECT `size` FROM `*PREFIX*filecache` WHERE `parent` = ? AND `storage` = ?'); - $result = $query->execute(array($id, $this->storageId)); + $result = $query->execute(array($id, $this->numericId)); $totalSize = 0; $hasChilds = 0; while ($row = $result->fetchRow()) { @@ -395,7 +419,7 @@ class Cache { */ public function getAll() { $query = \OC_DB::prepare('SELECT `fileid` FROM `*PREFIX*filecache` WHERE `storage` = ?'); - $result = $query->execute(array($this->storageId)); + $result = $query->execute(array($this->numericId)); $ids = array(); while ($row = $result->fetchRow()) { $ids[] = $row['fileid']; @@ -414,7 +438,7 @@ class Cache { */ public function getIncomplete() { $query = \OC_DB::prepare('SELECT `path` FROM `*PREFIX*filecache` WHERE `storage` = ? AND `size` = -1 ORDER BY `fileid` DESC LIMIT 1'); - $query->execute(array($this->storageId)); + $query->execute(array($this->numericId)); if ($row = $query->fetchRow()) { return $row['path']; } else { diff --git a/lib/files/cache/upgrade.php b/lib/files/cache/upgrade.php index 9219deebef..77db4c2339 100644 --- a/lib/files/cache/upgrade.php +++ b/lib/files/cache/upgrade.php @@ -11,6 +11,8 @@ namespace OC\Files\Cache; class Upgrade { static $permissionsCaches = array(); + static $numericIds = array(); + static function upgrade() { $insertQuery = \OC_DB::prepare('INSERT INTO `*PREFIX*filecache`( `fileid`, `storage`, `path`, `path_hash`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted` ) VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'); @@ -32,7 +34,7 @@ class Upgrade { $checkExistingQuery = \OC_DB::prepare('SELECT `fileid` FROM `*PREFIX*filecache` WHERE `fileid` = ?'); while ($row = $oldEntriesResult->fetchRow()) { - if($checkExistingQuery->execute(array($row['id']))->fetchRow()){ + if ($checkExistingQuery->execute(array($row['id']))->fetchRow()) { continue; } @@ -42,7 +44,7 @@ class Upgrade { * @var string $internalPath; */ $pathHash = md5($internalPath); - $storageId = $storage->getId(); + $storageId = self::getNumericId($storage); $parentId = ($internalPath === '') ? -1 : $row['parent']; $insertQuery->execute(array($row['id'], $storageId, $internalPath, $pathHash, $parentId, $row['name'], $row['mimetype'], $row['mimepart'], $row['size'], $row['mtime'], $row['encrypted'])); @@ -64,4 +66,19 @@ class Upgrade { } return self::$permissionsCaches[$storageId]; } + + /** + * get the numeric storage id + * + * @param \OC\Files\Storage\Storage $storage + * @return int + */ + static function getNumericId($storage) { + $storageId = $storage->getId(); + if (!isset(self::$numericIds[$storageId])) { + $cache = new Cache($storage); + self::$numericIds[$storageId] = $cache->getNumericStorageId(); + } + return self::$numericIds[$storageId]; + } } diff --git a/lib/util.php b/lib/util.php index 0f6ead2419..4411b32731 100755 --- a/lib/util.php +++ b/lib/util.php @@ -75,7 +75,7 @@ class OC_Util { */ public static function getVersion() { // hint: We only can count up. So the internal version number of ownCloud 4.5 will be 4.90.0. This is not visible to the user - return array(4,91,02); + return array(4,91,04); } /** From bf05ff351faa693337107ed4a316e36e9aacd296 Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Sat, 15 Dec 2012 19:44:46 -0500 Subject: [PATCH 119/272] Initial support for file sharing with filesystem branch --- apps/files_sharing/appinfo/app.php | 3 + apps/files_sharing/lib/cache.php | 220 +++++++++++++++++++++++ apps/files_sharing/lib/permissions.php | 82 +++++++++ apps/files_sharing/lib/scanner.php | 69 +++++++ apps/files_sharing/lib/share/file.php | 74 ++++---- apps/files_sharing/lib/share/folder.php | 28 --- apps/files_sharing/lib/sharedstorage.php | 148 ++++++--------- lib/public/share.php | 8 +- 8 files changed, 475 insertions(+), 157 deletions(-) create mode 100644 apps/files_sharing/lib/cache.php create mode 100644 apps/files_sharing/lib/permissions.php create mode 100644 apps/files_sharing/lib/scanner.php diff --git a/apps/files_sharing/appinfo/app.php b/apps/files_sharing/appinfo/app.php index 210c78ad17..189fd20cae 100644 --- a/apps/files_sharing/appinfo/app.php +++ b/apps/files_sharing/appinfo/app.php @@ -3,6 +3,9 @@ OC::$CLASSPATH['OC_Share_Backend_File'] = "apps/files_sharing/lib/share/file.php"; OC::$CLASSPATH['OC_Share_Backend_Folder'] = 'apps/files_sharing/lib/share/folder.php'; OC::$CLASSPATH['OC\Files\Storage\Shared'] = "apps/files_sharing/lib/sharedstorage.php"; +OC::$CLASSPATH['OC\Files\Cache\Shared_Cache'] = 'apps/files_sharing/lib/cache.php'; +OC::$CLASSPATH['OC\Files\Cache\Shared_Permissions'] = 'apps/files_sharing/lib/permissions.php'; +OC::$CLASSPATH['OC\Files\Cache\Shared_Scanner'] = 'apps/files_sharing/lib/scanner.php'; OCP\Util::connectHook('OC_Filesystem', 'setup', '\OC\Files\Storage\Shared', 'setup'); OCP\Share::registerBackend('file', 'OC_Share_Backend_File'); OCP\Share::registerBackend('folder', 'OC_Share_Backend_Folder', 'file'); diff --git a/apps/files_sharing/lib/cache.php b/apps/files_sharing/lib/cache.php new file mode 100644 index 0000000000..196e767cf6 --- /dev/null +++ b/apps/files_sharing/lib/cache.php @@ -0,0 +1,220 @@ +. +*/ + +namespace OC\Files\Cache; + +/** + * Metadata cache for shared files + * + * don't use this class directly if you need to get metadata, use \OC\Files\Filesystem::getFileInfo instead + */ +class Shared_Cache extends Cache { + + private $files = array(); + + /** + * @brief Get the source cache of a shared file or folder + * @param string Shared target file path + * @return \OC\Files\Storage\Cache + */ + private function getSourceCache($target) { + $source = \OC_Share_Backend_File::getSource($target); + if (isset($source['path'])) { + $source['path'] = '/'.$source['uid_owner'].'/'.$source['path']; + \OC\Files\Filesystem::initMountPoints($source['uid_owner']); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source['path']); + $this->files[$target] = $internalPath; + return $storage->getCache(); + } + return false; + } + + /** + * get the stored metadata of a file or folder + * + * @param string/int $file + * @return array + */ + public function get($file) { + if (is_string($file)) { + if ($cache = $this->getSourceCache($file)) { + return $cache->get($this->files[$file]); + } + } else { + $query = \OC_DB::prepare( + 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted` + FROM `*PREFIX*filecache` WHERE `fileid` = ?'); + $result = $query->execute(array($file)); + $data = $result->fetchRow(); + $data['fileid'] = (int)$data['fileid']; + $data['size'] = (int)$data['size']; + $data['mtime'] = (int)$data['mtime']; + $data['encrypted'] = (bool)$data['encrypted']; + return $data; + } + return false; + } + + /** + * get the metadata of all files stored in $folder + * + * @param string $folder + * @return array + */ + public function getFolderContents($folder) { + if ($folder == '') { + return \OCP\Share::getItemsSharedWith('file', \OC_Share_Backend_File::FORMAT_GET_FOLDER_CONTENTS); + } else { + return $this->getSourceCache($folder)->getFolderContents('/'.$this->files[$folder]); + } + } + + /** + * store meta data for a file or folder + * + * @param string $file + * @param array $data + * + * @return int file id + */ + public function put($file, array $data) { + if ($cache = $this->getSourceCache($file)) { + return $cache->put($this->files[$file]); + } + return false; + } + + /** + * get the file id for a file + * + * @param string $file + * @return int + */ + public function getId($file) { + if ($cache = $this->getSourceCache($file)) { + return $cache->getId($this->files[$file]); + } + return -1; + } + + /** + * remove a file or folder from the cache + * + * @param string $file + */ + public function remove($file) { + if ($cache = $this->getSourceCache($file)) { + $cache->remove($this->files[$file]); + } + } + + /** + * Move a file or folder in the cache + * + * @param string $source + * @param string $target + */ + public function move($source, $target) { + if ($cache = $this->getSourceCache($source)) { + $targetPath = \OC_Share_Backend_File::getSourcePath(dirname($target)); + if ($targetPath) { + $targetPath .= '/'.basename($target); + $cache->move($this->files[$source], $targetPath); + } + + } + } + + /** + * remove all entries for files that are stored on the storage from the cache + */ + public function clear() { + // Not a valid action for Shared Cache + } + + /** + * @param string $file + * + * @return int, Cache::NOT_FOUND, Cache::PARTIAL, Cache::SHALLOW or Cache::COMPLETE + */ + public function getStatus($file) { + if ($cache = $this->getSourceCache($file)) { + return $cache->getStatus($this->files[$file]); + } + return false; + } + + /** + * search for files matching $pattern + * + * @param string $pattern + * @return array of file data + */ + public function search($pattern) { + // TODO + } + + /** + * search for files by mimetype + * + * @param string $part1 + * @param string $part2 + * @return array + */ + public function searchByMime($mimetype) { + if (strpos($mimetype, '/')) { + $where = '`mimetype` = ?'; + } else { + $where = '`mimepart` = ?'; + } + $ids = $this->getAll(); + $placeholders = join(',', array_fill(0, count($ids), '?')); + $query = \OC_DB::prepare(' + SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted` + FROM `*PREFIX*filecache` WHERE ' . $where . ' AND `fileid` IN = ('.$placeholders.')' + ); + $result = $query->execute(array_merge(array($mimetype), $ids)); + return $result->fetchAll(); + } + + /** + * get the size of a folder and set it in the cache + * + * @param string $path + * @return int + */ + public function calculateFolderSize($path) { + if ($cache = $this->getSourceCache($path)) { + return $cache->calculateFolderSize($this->files[$path]); + } + return 0; + } + + /** + * get all file ids on the files on the storage + * + * @return int[] + */ + public function getAll() { + return OCP\Share::getItemsSharedWith('file', \OC_Share_Backend_File::FORMAT_GET_ALL); + } + +} \ No newline at end of file diff --git a/apps/files_sharing/lib/permissions.php b/apps/files_sharing/lib/permissions.php new file mode 100644 index 0000000000..51ae4cf102 --- /dev/null +++ b/apps/files_sharing/lib/permissions.php @@ -0,0 +1,82 @@ +. +*/ +namespace OC\Files\Cache; + +class Shared_Permissions { + + /** + * get the permissions for a single file + * + * @param int $fileId + * @param string $user + * @return int (-1 if file no permissions set) + */ + static public function get($fileId, $user) { + $source = \OCP\Share::getItemSharedWithBySource('file', $fileId, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE); + if ($source) { + return $source['permissions']; + } else { + return -1; + } + } + + /** + * set the permissions of a file + * + * @param int $fileId + * @param string $user + * @param int $permissions + */ + static public function set($fileId, $user, $permissions) { + // Not a valid action for Shared Permissions + } + + /** + * get the permissions of multiply files + * + * @param int[] $fileIds + * @param string $user + * @return int[] + */ + static public function getMultiple($fileIds, $user) { + if (count($fileIds) === 0) { + return array(); + } + foreach ($fileIds as $fileId) { + $filePermissions[$fileId] = self::get($fileId, $user); + } + return $filePermissions; + } + + /** + * remove the permissions for a file + * + * @param int $fileId + * @param string $user + */ + static public function remove($fileId, $user) { + // Not a valid action for Shared Permissions + } + + static public function removeMultiple($fileIds, $user) { + // Not a valid action for Shared Permissions + } +} diff --git a/apps/files_sharing/lib/scanner.php b/apps/files_sharing/lib/scanner.php new file mode 100644 index 0000000000..d13d2f9cbc --- /dev/null +++ b/apps/files_sharing/lib/scanner.php @@ -0,0 +1,69 @@ +. +*/ + +namespace OC\Files\Cache; + +class Shared_Scanner extends Scanner { + + public function __construct(\OC\Files\Storage\Storage $storage) { + + } + + /** + * get all the metadata of a file or folder + * * + * + * @param string $path + * @return array with metadata of the file + */ + public function getData($path) { + // Not a valid action for Shared Scanner + } + + /** + * scan a single file and store it in the cache + * + * @param string $file + * @return array with metadata of the scanned file + */ + public function scanFile($file) { + // Not a valid action for Shared Scanner + } + + /** + * scan all the files in a folder and store them in the cache + * + * @param string $path + * @param SCAN_RECURSIVE/SCAN_SHALLOW $recursive + * @return int the size of the scanned folder or -1 if the size is unknown at this stage + */ + public function scan($path, $recursive = self::SCAN_RECURSIVE) { + // Not a valid action for Shared Scanner + } + + /** + * walk over any folders that are not fully scanned yet and scan them + */ + public function backgroundScan() { + // Not a valid action for Shared Scanner + } + +} \ No newline at end of file diff --git a/apps/files_sharing/lib/share/file.php b/apps/files_sharing/lib/share/file.php index ac58523683..52220dc5b2 100644 --- a/apps/files_sharing/lib/share/file.php +++ b/apps/files_sharing/lib/share/file.php @@ -22,16 +22,17 @@ class OC_Share_Backend_File implements OCP\Share_Backend_File_Dependent { const FORMAT_SHARED_STORAGE = 0; - const FORMAT_FILE_APP = 1; - const FORMAT_FILE_APP_ROOT = 2; + const FORMAT_GET_FOLDER_CONTENTS = 1; + const FORMAT_GET_ALL = 2; const FORMAT_OPENDIR = 3; private $path; public function isValidSource($itemSource, $uidOwner) { - $path = OC_FileCache::getPath($itemSource, $uidOwner); - if ($path) { - $this->path = $path; + $query = \OC_DB::prepare('SELECT `name` FROM `*PREFIX*filecache` WHERE `fileid` = ?'); + $result = $query->execute(array($itemSource)); + if ($row = $result->fetchRow()) { + $this->path = $row['name']; return true; } return false; @@ -70,50 +71,29 @@ class OC_Share_Backend_File implements OCP\Share_Backend_File_Dependent { public function formatItems($items, $format, $parameters = null) { if ($format == self::FORMAT_SHARED_STORAGE) { // Only 1 item should come through for this format call - return array('path' => $items[key($items)]['path'], 'permissions' => $items[key($items)]['permissions']); - } else if ($format == self::FORMAT_FILE_APP) { - if (isset($parameters['mimetype_filter']) && $parameters['mimetype_filter']) { - $mimetype_filter = $parameters['mimetype_filter']; - } + return array('path' => $items[key($items)]['path'], 'permissions' => $items[key($items)]['permissions'], 'uid_owner' => $items[key($items)]['uid_owner']); + } else if ($format == self::FORMAT_GET_FOLDER_CONTENTS) { $files = array(); foreach ($items as $item) { - if (isset($mimetype_filter) - && strpos($item['mimetype'], $mimetype_filter) !== 0 - && $item['mimetype'] != 'httpd/unix-directory') { - continue; - } $file = array(); - $file['id'] = $item['file_source']; + $file['fileid'] = $item['file_source']; + $file['storage'] = $item['storage']; $file['path'] = $item['file_target']; + $file['parent'] = $item['file_parent']; $file['name'] = basename($item['file_target']); - $file['ctime'] = $item['ctime']; - $file['mtime'] = $item['mtime']; $file['mimetype'] = $item['mimetype']; $file['size'] = $item['size']; + $file['mtime'] = $item['mtime']; $file['encrypted'] = $item['encrypted']; - $file['versioned'] = $item['versioned']; - $file['directory'] = $parameters['folder']; - $file['type'] = ($item['mimetype'] == 'httpd/unix-directory') ? 'dir' : 'file'; - $file['permissions'] = $item['permissions']; - if ($file['type'] == 'file') { - // Remove Create permission if type is file - $file['permissions'] &= ~OCP\PERMISSION_CREATE; - } - // NOTE: Temporary fix to allow unsharing of files in root of Shared directory - $file['permissions'] |= OCP\PERMISSION_DELETE; $files[] = $file; } return $files; - } else if ($format == self::FORMAT_FILE_APP_ROOT) { - $mtime = 0; - $size = 0; + } else if ($format == self::FORMAT_GET_ALL) { + $ids = array(); foreach ($items as $item) { - if ($item['mtime'] > $mtime) { - $mtime = $item['mtime']; - } - $size += $item['size']; + $ids[] = $item['file_source']; } - return array(0 => array('id' => -1, 'name' => 'Shared', 'mtime' => $mtime, 'mimetype' => 'httpd/unix-directory', 'size' => $size, 'writable' => false, 'type' => 'dir', 'directory' => '', 'permissions' => OCP\PERMISSION_READ)); + return $ids; } else if ($format == self::FORMAT_OPENDIR) { $files = array(); foreach ($items as $item) { @@ -124,4 +104,26 @@ class OC_Share_Backend_File implements OCP\Share_Backend_File_Dependent { return array(); } + public static function getSource($target) { + $target = '/'.$target; + $target = rtrim($target, '/'); + $pos = strpos($target, '/', 1); + // Get shared folder name + if ($pos !== false) { + $folder = substr($target, 0, $pos); + $source = \OCP\Share::getItemSharedWith('folder', $folder, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE); + if ($source) { + $source['path'] = $source['path'].substr($target, strlen($folder)); + return $source; + } + } else { + $source = \OCP\Share::getItemSharedWith('file', $target, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE); + if ($source) { + return $source; + } + } + \OCP\Util::writeLog('files_sharing', 'File source not found for: '.$target, \OCP\Util::ERROR); + return false; + } + } diff --git a/apps/files_sharing/lib/share/folder.php b/apps/files_sharing/lib/share/folder.php index d414fcf10f..a1bb843d72 100644 --- a/apps/files_sharing/lib/share/folder.php +++ b/apps/files_sharing/lib/share/folder.php @@ -21,34 +21,6 @@ class OC_Share_Backend_Folder extends OC_Share_Backend_File implements OCP\Share_Backend_Collection { - public function formatItems($items, $format, $parameters = null) { - if ($format == self::FORMAT_SHARED_STORAGE) { - // Only 1 item should come through for this format call - return array('path' => $items[key($items)]['path'], 'permissions' => $items[key($items)]['permissions']); - } else if ($format == self::FORMAT_FILE_APP && isset($parameters['folder'])) { - // Only 1 item should come through for this format call - $folder = $items[key($items)]; - if (isset($parameters['mimetype_filter'])) { - $mimetype_filter = $parameters['mimetype_filter']; - } else { - $mimetype_filter = ''; - } - $path = $folder['path'].substr($parameters['folder'], 7 + strlen($folder['file_target'])); - $files = OC_FileCache::getFolderContent($path, '', $mimetype_filter); - foreach ($files as &$file) { - $file['directory'] = $parameters['folder']; - $file['type'] = ($file['mimetype'] == 'httpd/unix-directory') ? 'dir' : 'file'; - $file['permissions'] = $folder['permissions']; - if ($file['type'] == 'file') { - // Remove Create permission if type is file - $file['permissions'] &= ~OCP\PERMISSION_CREATE; - } - } - return $files; - } - return array(); - } - public function getChildren($itemSource) { $children = array(); $parents = array($itemSource); diff --git a/apps/files_sharing/lib/sharedstorage.php b/apps/files_sharing/lib/sharedstorage.php index 5abdbf29f0..960aa64099 100644 --- a/apps/files_sharing/lib/sharedstorage.php +++ b/apps/files_sharing/lib/sharedstorage.php @@ -34,41 +34,8 @@ class Shared extends \OC\Files\Storage\Common { $this->sharedFolder = $arguments['sharedFolder']; } - /** - * @brief Get the source file path and the permissions granted for a shared file - * @param string Shared target file path - * @return array with the keys path and permissions or false if not found - */ - private function getFile($target) { - $target = '/'.$target; - $target = rtrim($target, '/'); - if (isset($this->files[$target])) { - return $this->files[$target]; - } else { - $pos = strpos($target, '/', 1); - // Get shared folder name - if ($pos !== false) { - $folder = substr($target, 0, $pos); - if (isset($this->files[$folder])) { - $file = $this->files[$folder]; - } else { - $file = \OCP\Share::getItemSharedWith('folder', $folder, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE); - } - if ($file) { - $this->files[$target]['path'] = $file['path'].substr($target, strlen($folder)); - $this->files[$target]['permissions'] = $file['permissions']; - return $this->files[$target]; - } - } else { - $file = \OCP\Share::getItemSharedWith('file', $target, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE); - if ($file) { - $this->files[$target] = $file; - return $this->files[$target]; - } - } - \OCP\Util::writeLog('files_sharing', 'File source not found for: '.$target, \OCP\Util::ERROR); - return false; - } + public function getId(){ + return 'shared::' . $this->sharedFolder; } /** @@ -77,13 +44,13 @@ class Shared extends \OC\Files\Storage\Common { * @return string source file path or false if not found */ private function getSourcePath($target) { - $file = $this->getFile($target); - if (isset($file['path'])) { - $uid = substr($file['path'], 1, strpos($file['path'], '/', 1) - 1); - \OC\Files\Filesystem::mount('\OC\Files\Storage\Local', array('datadir' => \OC_User::getHome($uid)), $uid); - return $file['path']; + if (!isset($this->files[$target])) { + $source = \OC_Share_Backend_File::getSource($target); + $source['path'] = '/'.$source['uid_owner'].'/'.$source['path']; + $this->files[$target] = $source; + \OC\Files\Filesystem::initMountPoints($source['uid_owner']); } - return false; + return $this->files[$target]['path']; } /** @@ -92,26 +59,18 @@ class Shared extends \OC\Files\Storage\Common { * @return int CRUDS permissions granted or false if not found */ public function getPermissions($target) { - $file = $this->getFile($target); - if (isset($file['permissions'])) { - return $file['permissions']; + if (!isset($this->files[$target])) { + $source = \OC_Share_Backend_File::getSource($target); + $this->files[$target] = $source; } - return false; - } - - public function getOwner($target) { - $shared_item = \OCP\Share::getItemSharedWith('folder', $target, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE); - if ($shared_item) { - return $shared_item[0]["uid_owner"]; - } - return null; + return $this->files[$target]['permissions']; } public function mkdir($path) { if ($path == '' || $path == '/' || !$this->isCreatable(dirname($path))) { return false; } else if ($source = $this->getSourcePath($path)) { - list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($source); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); return $storage->mkdir($internalPath); } return false; @@ -119,7 +78,7 @@ class Shared extends \OC\Files\Storage\Common { public function rmdir($path) { if (($source = $this->getSourcePath($path)) && $this->isDeletable($path)) { - list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($source); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); return $storage->rmdir($internalPath); } return false; @@ -131,7 +90,7 @@ class Shared extends \OC\Files\Storage\Common { \OC_FakeDirStream::$dirs['shared'] = $files; return opendir('fakedir://shared'); } else if ($source = $this->getSourcePath($path)) { - list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($source); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); return $storage->opendir($internalPath); } return false; @@ -141,7 +100,7 @@ class Shared extends \OC\Files\Storage\Common { if ($path == '' || $path == '/') { return true; } else if ($source = $this->getSourcePath($path)) { - list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($source); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); return $storage->is_dir($internalPath); } return false; @@ -149,7 +108,7 @@ class Shared extends \OC\Files\Storage\Common { public function is_file($path) { if ($source = $this->getSourcePath($path)) { - list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($source); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); return $storage->is_file($internalPath); } return false; @@ -161,7 +120,7 @@ class Shared extends \OC\Files\Storage\Common { $stat['mtime'] = $this->filemtime($path); return $stat; } else if ($source = $this->getSourcePath($path)) { - list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($source); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); return $storage->stat($internalPath); } return false; @@ -171,7 +130,7 @@ class Shared extends \OC\Files\Storage\Common { if ($path == '' || $path == '/') { return 'dir'; } else if ($source = $this->getSourcePath($path)) { - list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($source); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); return $storage->filetype($internalPath); } return false; @@ -181,7 +140,7 @@ class Shared extends \OC\Files\Storage\Common { if ($path == '' || $path == '/' || $this->is_dir($path)) { return 0; } else if ($source = $this->getSourcePath($path)) { - list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($source); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); return $storage->filesize($internalPath); } return false; @@ -191,7 +150,7 @@ class Shared extends \OC\Files\Storage\Common { if ($path == '') { return false; } - return ($this->getPermissions($path) & OCP\PERMISSION_CREATE); + return ($this->getPermissions($path) & \OCP\PERMISSION_CREATE); } public function isReadable($path) { @@ -202,28 +161,28 @@ class Shared extends \OC\Files\Storage\Common { if ($path == '') { return false; } - return ($this->getPermissions($path) & OCP\PERMISSION_UPDATE); + return ($this->getPermissions($path) & \OCP\PERMISSION_UPDATE); } public function isDeletable($path) { if ($path == '') { return true; } - return ($this->getPermissions($path) & OCP\PERMISSION_DELETE); + return ($this->getPermissions($path) & \OCP\PERMISSION_DELETE); } public function isSharable($path) { if ($path == '') { return false; } - return ($this->getPermissions($path) & OCP\PERMISSION_SHARE); + return ($this->getPermissions($path) & \OCP\PERMISSION_SHARE); } public function file_exists($path) { if ($path == '' || $path == '/') { return true; } else if ($source = $this->getSourcePath($path)) { - list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($source); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); return $storage->file_exists($internalPath); } return false; @@ -244,7 +203,7 @@ class Shared extends \OC\Files\Storage\Common { } else { $source = $this->getSourcePath($path); if ($source) { - list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($source); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); return $storage->filemtime($internalPath); } } @@ -258,7 +217,7 @@ class Shared extends \OC\Files\Storage\Common { 'source' => $source, ); \OCP\Util::emitHook('\OC\Files\Storage\Shared', 'file_get_contents', $info); - list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($source); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); return $storage->file_get_contents($internalPath); } } @@ -274,7 +233,7 @@ class Shared extends \OC\Files\Storage\Common { 'source' => $source, ); \OCP\Util::emitHook('\OC\Files\Storage\Shared', 'file_put_contents', $info); - list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($source); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); $result = $storage->file_put_contents($internalPath, $data); return $result; } @@ -285,7 +244,7 @@ class Shared extends \OC\Files\Storage\Common { // Delete the file if DELETE permission is granted if ($source = $this->getSourcePath($path)) { if ($this->isDeletable($path)) { - list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($source); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); return $storage->unlink($internalPath); } else if (dirname($path) == '/' || dirname($path) == '.') { // Unshare the file from the user if in the root of the Shared folder @@ -309,8 +268,8 @@ class Shared extends \OC\Files\Storage\Common { if (dirname($path1) == dirname($path2)) { // Rename the file if UPDATE permission is granted if ($this->isUpdatable($path1)) { - list($storage, $oldInternalPath)=\OC\Files\Filesystem::resolvePath($oldSource); - list( , $newInternalPath)=\OC\Files\Filesystem::resolvePath($newSource); + list($storage, $oldInternalPath) = \OC\Files\Filesystem::resolvePath($oldSource); + list( , $newInternalPath) = \OC\Files\Filesystem::resolvePath($newSource); return $storage->rename($oldInternalPath, $newInternalPath); } } else { @@ -325,8 +284,8 @@ class Shared extends \OC\Files\Storage\Common { return $this->unlink($path1); } } else { - list($storage, $oldInternalPath)=\OC\Files\Filesystem::resolvePath($oldSource); - list( , $newInternalPath)=\OC\Files\Filesystem::resolvePath($newSource); + list($storage, $oldInternalPath) = \OC\Files\Filesystem::resolvePath($oldSource); + list( , $newInternalPath) = \OC\Files\Filesystem::resolvePath($newSource); return $storage->rename($oldInternalPath, $newInternalPath); } } @@ -372,7 +331,7 @@ class Shared extends \OC\Files\Storage\Common { 'mode' => $mode, ); \OCP\Util::emitHook('\OC\Files\Storage\Shared', 'fopen', $info); - list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($source); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); return $storage->fopen($internalPath, $mode); } return false; @@ -383,7 +342,7 @@ class Shared extends \OC\Files\Storage\Common { return 'httpd/unix-directory'; } if ($source = $this->getSourcePath($path)) { - list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($source); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); return $storage->getMimeType($internalPath); } return false; @@ -392,21 +351,21 @@ class Shared extends \OC\Files\Storage\Common { public function free_space($path) { $source = $this->getSourcePath($path); if ($source) { - list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($source); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); return $storage->free_space($internalPath); } } public function getLocalFile($path) { if ($source = $this->getSourcePath($path)) { - list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($source); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); return $storage->getLocalFile($internalPath); } return false; } public function touch($path, $mtime = null) { if ($source = $this->getSourcePath($path)) { - list($storage, $internalPath)=\OC\Files\Filesystem::resolvePath($source); + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); return $storage->touch($internalPath, $mtime); } return false; @@ -417,17 +376,28 @@ class Shared extends \OC\Files\Storage\Common { \OC\Files\Filesystem::mount('\OC\Files\Storage\Shared', array('sharedFolder' => '/Shared'), $user_dir.'/Shared/'); } - /** - * check if a file or folder has been updated since $time - * @param int $time - * @return bool - */ - public function hasUpdated($path, $time) { - //TODO - return false; + public function getCache() { + return new \OC\Files\Cache\Shared_Cache($this); } - public function getId(){ - return 'shared::' . $this->sharedFolder; + public function getScanner(){ + return new \OC\Files\Cache\Shared_Scanner($this); } + + public function getPermissionsCache() { + return new \OC\Files\Cache\Shared_Permissions($this); + } + + public function getOwner($path) { + if (!isset($this->files[$path])) { + $source = \OC_Share_Backend_File::getSource($path); + $this->files[$path] = $source; + } + return $this->files[$path]['uid_owner']; + } + + public function getETag($path) { + + } + } diff --git a/lib/public/share.php b/lib/public/share.php index 9bb64a38c8..8981de1b50 100644 --- a/lib/public/share.php +++ b/lib/public/share.php @@ -552,7 +552,7 @@ class Share { // Get filesystem root to add it to the file target and remove from the file source, match file_source with the file cache if ($itemType == 'file' || $itemType == 'folder') { $root = \OC\Files\Filesystem::getRoot(); - $where = 'INNER JOIN `*PREFIX*fscache` ON `file_source` = `*PREFIX*fscache`.`id`'; + $where = 'INNER JOIN `*PREFIX*filecache` ON `file_source` = `*PREFIX*filecache`.`fileid`'; if (!isset($item)) { $where .= ' WHERE `file_target` IS NOT NULL'; } @@ -569,7 +569,7 @@ class Share { $itemTypes = $collectionTypes; } $placeholders = join(',', array_fill(0, count($itemTypes), '?')); - $where .= ' WHERE `item_type` IN ('.$placeholders.'))'; + $where = ' WHERE `item_type` IN ('.$placeholders.'))'; $queryArgs = $itemTypes; } else { $where = ' WHERE `item_type` = ?'; @@ -681,8 +681,8 @@ class Share { } } else { if ($fileDependent) { - if (($itemType == 'file' || $itemType == 'folder') && $format == \OC_Share_Backend_File::FORMAT_FILE_APP || $format == \OC_Share_Backend_File::FORMAT_FILE_APP_ROOT) { - $select = '`*PREFIX*share`.`id`, `item_type`, `*PREFIX*share`.`parent`, `uid_owner`, `share_type`, `share_with`, `file_source`, `path`, `file_target`, `permissions`, `expiration`, `name`, `ctime`, `mtime`, `mimetype`, `size`, `encrypted`, `versioned`, `writable`'; + if (($itemType == 'file' || $itemType == 'folder') && $format == \OC_Share_Backend_File::FORMAT_GET_FOLDER_CONTENTS) { + $select = '`*PREFIX*share`.`id`, `item_type`, `*PREFIX*share`.`parent`, `uid_owner`, `share_type`, `share_with`, `file_source`, `path`, `file_target`, `permissions`, `expiration`, `*PREFIX*filecache`.`parent` as `file_parent`, `name`, `mtime`, `mimetype`, `mimepart`, `size`, `encrypted`'; } else { $select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `item_target`, `*PREFIX*share`.`parent`, `share_type`, `share_with`, `uid_owner`, `file_source`, `path`, `file_target`, `permissions`, `stime`, `expiration`, `token`'; } From b4515d874e5259647886d148db902f9753bbeb81 Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Wed, 26 Dec 2012 15:36:50 -0500 Subject: [PATCH 120/272] Only folders have Create permission --- lib/files/storage/common.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/files/storage/common.php b/lib/files/storage/common.php index c891d0c3ad..3cf960d05d 100644 --- a/lib/files/storage/common.php +++ b/lib/files/storage/common.php @@ -38,7 +38,10 @@ abstract class Common implements \OC\Files\Storage\Storage { } } public function isCreatable($path) { - return $this->isUpdatable($path); + if ($this->is_dir($path) && $this->isUpdatable($path)) { + return true; + } + return false; } public function isDeletable($path) { return $this->isUpdatable($path); From 1910057900476c5b7509a456a460d70664c01610 Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Wed, 26 Dec 2012 16:20:10 -0500 Subject: [PATCH 121/272] Fix shared statuses inside folders --- apps/files_sharing/lib/share/folder.php | 4 ++-- lib/public/share.php | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/files_sharing/lib/share/folder.php b/apps/files_sharing/lib/share/folder.php index a1bb843d72..bbe4c130bd 100644 --- a/apps/files_sharing/lib/share/folder.php +++ b/apps/files_sharing/lib/share/folder.php @@ -26,11 +26,11 @@ class OC_Share_Backend_Folder extends OC_Share_Backend_File implements OCP\Share $parents = array($itemSource); while (!empty($parents)) { $parents = "'".implode("','", $parents)."'"; - $query = OC_DB::prepare('SELECT `id`, `name`, `mimetype` FROM `*PREFIX*fscache` WHERE `parent` IN ('.$parents.')'); + $query = OC_DB::prepare('SELECT `fileid`, `name`, `mimetype` FROM `*PREFIX*filecache` WHERE `parent` IN ('.$parents.')'); $result = $query->execute(); $parents = array(); while ($file = $result->fetchRow()) { - $children[] = array('source' => $file['id'], 'file_path' => $file['name']); + $children[] = array('source' => $file['fileid'], 'file_path' => $file['name']); // If a child folder is found look inside it if ($file['mimetype'] == 'httpd/unix-directory') { $parents[] = $file['id']; diff --git a/lib/public/share.php b/lib/public/share.php index 8981de1b50..2775a07f13 100644 --- a/lib/public/share.php +++ b/lib/public/share.php @@ -739,7 +739,8 @@ class Share { if (isset($row['parent'])) { $row['path'] = '/Shared/'.basename($row['path']); } else { - $row['path'] = substr($row['path'], $root); + // Strip 'files' from path + $row['path'] = substr($row['path'], 5); } } if (isset($row['expiration'])) { From 65f1e521079968a4450468917649b241b0fb6b24 Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Thu, 27 Dec 2012 15:49:48 -0500 Subject: [PATCH 122/272] Change old is_readable to isReadable, fixes downloads --- lib/files.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/files.php b/lib/files.php index e12797bd61..15bfbc3fc2 100644 --- a/lib/files.php +++ b/lib/files.php @@ -100,7 +100,7 @@ class OC_Files { $filename = $dir . '/' . $files; } OC_Util::obEnd(); - if($zip or \OC\Files\Filesystem::is_readable($filename)) { + if ($zip or \OC\Files\Filesystem::isReadable($filename)) { if ( preg_match( "/MSIE/", $_SERVER["HTTP_USER_AGENT"] ) ) { header( 'Content-Disposition: attachment; filename="' . rawurlencode( basename($filename) ) . '"' ); } else { From b03562670973fd924a042064644994639a59667d Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Thu, 27 Dec 2012 16:53:32 -0500 Subject: [PATCH 123/272] Retrieve storage correctly, filename is not the mountpoint --- lib/files.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/files.php b/lib/files.php index 15bfbc3fc2..e1a31c6f03 100644 --- a/lib/files.php +++ b/lib/files.php @@ -117,7 +117,7 @@ class OC_Files { }else{ header('Content-Type: '.\OC\Files\Filesystem::getMimeType($filename)); header("Content-Length: ".\OC\Files\Filesystem::filesize($filename)); - $storage = \OC\Files\Filesystem::getStorage($filename); + list($storage) = \OC\Files\Filesystem::resolvePath($filename); if ($storage instanceof \OC\File\Storage\Local) { self::addSendfileHeader(\OC\Files\Filesystem::getLocalFile($filename)); } From 8bdfb040565eafb33bc27305463eb08374f14e32 Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Fri, 28 Dec 2012 15:06:12 -0500 Subject: [PATCH 124/272] Fix shared folders --- apps/files_sharing/lib/cache.php | 17 ++++++++++++++--- lib/files/filesystem.php | 1 + lib/util.php | 1 - 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/apps/files_sharing/lib/cache.php b/apps/files_sharing/lib/cache.php index 196e767cf6..d2ac8ccaaa 100644 --- a/apps/files_sharing/lib/cache.php +++ b/apps/files_sharing/lib/cache.php @@ -30,6 +30,10 @@ class Shared_Cache extends Cache { private $files = array(); + public function __construct($storage) { + + } + /** * @brief Get the source cache of a shared file or folder * @param string Shared target file path @@ -41,8 +45,12 @@ class Shared_Cache extends Cache { $source['path'] = '/'.$source['uid_owner'].'/'.$source['path']; \OC\Files\Filesystem::initMountPoints($source['uid_owner']); list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source['path']); - $this->files[$target] = $internalPath; - return $storage->getCache(); + if ($storage) { + $this->files[$target] = $internalPath; + $cache = $storage->getCache(); + $this->numericId = $cache->getNumericStorageId(); + return $cache; + } } return false; } @@ -83,8 +91,11 @@ class Shared_Cache extends Cache { if ($folder == '') { return \OCP\Share::getItemsSharedWith('file', \OC_Share_Backend_File::FORMAT_GET_FOLDER_CONTENTS); } else { - return $this->getSourceCache($folder)->getFolderContents('/'.$this->files[$folder]); + if ($cache = $this->getSourceCache($folder)) { + return $cache->getFolderContents($this->files[$folder]); + } } + return false; } /** diff --git a/lib/files/filesystem.php b/lib/files/filesystem.php index c5b56ba9f0..8183b8ff99 100644 --- a/lib/files/filesystem.php +++ b/lib/files/filesystem.php @@ -276,6 +276,7 @@ class Filesystem { } // Load personal mount points $root = \OC_User::getHome($user); + self::mount('\OC\Files\Storage\Local', array('datadir' => $root), $user); if (is_file($root.'/mount.php')) { $mountConfig = include $root.'/mount.php'; if (isset($mountConfig['user'][$user])) { diff --git a/lib/util.php b/lib/util.php index 4ebc2564d1..c5a495234d 100755 --- a/lib/util.php +++ b/lib/util.php @@ -51,7 +51,6 @@ class OC_Util { mkdir( $userdirectory, 0755, true ); } //jail the user into his "home" directory - \OC\Files\Filesystem::mount('\OC\Files\Storage\Local', array('datadir' => $user_root), $user); \OC\Files\Filesystem::init($user_dir); $quotaProxy=new OC_FileProxy_Quota(); From fb053f8e73056108acfd1574973d7b18581fc833 Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Sat, 29 Dec 2012 11:09:57 -0500 Subject: [PATCH 125/272] Fix retrieving of shared source paths --- apps/files_sharing/lib/sharedstorage.php | 41 ++++++++++++++++-------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/apps/files_sharing/lib/sharedstorage.php b/apps/files_sharing/lib/sharedstorage.php index 960aa64099..cb9b36482f 100644 --- a/apps/files_sharing/lib/sharedstorage.php +++ b/apps/files_sharing/lib/sharedstorage.php @@ -38,19 +38,34 @@ class Shared extends \OC\Files\Storage\Common { return 'shared::' . $this->sharedFolder; } + /** + * @brief Get the source file path, permissions, and owner for a shared file + * @param string Shared target file path + * @return Returns array with the keys path, permissions, and owner or false if not found + */ + private function getFile($target) { + if (!isset($this->files[$target])) { + $source = \OC_Share_Backend_File::getSource($target); + if ($source) { + $source['path'] = '/'.$source['uid_owner'].'/'.$source['path']; + } + $this->files[$target] = $source; + } + return $this->files[$target]; + } + /** * @brief Get the source file path for a shared file * @param string Shared target file path * @return string source file path or false if not found */ private function getSourcePath($target) { - if (!isset($this->files[$target])) { - $source = \OC_Share_Backend_File::getSource($target); - $source['path'] = '/'.$source['uid_owner'].'/'.$source['path']; - $this->files[$target] = $source; + $source = $this->getFile($target); + if ($source) { \OC\Files\Filesystem::initMountPoints($source['uid_owner']); + return $source['path']; } - return $this->files[$target]['path']; + return false; } /** @@ -59,11 +74,11 @@ class Shared extends \OC\Files\Storage\Common { * @return int CRUDS permissions granted or false if not found */ public function getPermissions($target) { - if (!isset($this->files[$target])) { - $source = \OC_Share_Backend_File::getSource($target); - $this->files[$target] = $source; + $source = $this->getFile($target); + if ($source) { + return $source['permissions']; } - return $this->files[$target]['permissions']; + return false; } public function mkdir($path) { @@ -389,11 +404,11 @@ class Shared extends \OC\Files\Storage\Common { } public function getOwner($path) { - if (!isset($this->files[$path])) { - $source = \OC_Share_Backend_File::getSource($path); - $this->files[$path] = $source; + $source = $this->getFile($path); + if ($source) { + return $source['uid_owner']; } - return $this->files[$path]['uid_owner']; + return false; } public function getETag($path) { From cfc3526b25f1b96ab7a989f1c89f5b3911bc18fc Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Sat, 29 Dec 2012 11:45:13 -0500 Subject: [PATCH 126/272] Fix data for shared root folder --- apps/files_sharing/lib/cache.php | 4 +++- apps/files_sharing/lib/permissions.php | 3 +++ apps/files_sharing/lib/share/file.php | 26 +++++++++++++++++++------- lib/public/share.php | 2 +- 4 files changed, 26 insertions(+), 9 deletions(-) diff --git a/apps/files_sharing/lib/cache.php b/apps/files_sharing/lib/cache.php index d2ac8ccaaa..a22e7af7f5 100644 --- a/apps/files_sharing/lib/cache.php +++ b/apps/files_sharing/lib/cache.php @@ -62,7 +62,9 @@ class Shared_Cache extends Cache { * @return array */ public function get($file) { - if (is_string($file)) { + if ($file == '') { + return \OCP\Share::getItemsSharedWith('file', \OC_Share_Backend_File::FORMAT_FILE_APP_ROOT); + } else if (is_string($file)) { if ($cache = $this->getSourceCache($file)) { return $cache->get($this->files[$file]); } diff --git a/apps/files_sharing/lib/permissions.php b/apps/files_sharing/lib/permissions.php index 51ae4cf102..6eaed34b33 100644 --- a/apps/files_sharing/lib/permissions.php +++ b/apps/files_sharing/lib/permissions.php @@ -30,6 +30,9 @@ class Shared_Permissions { * @return int (-1 if file no permissions set) */ static public function get($fileId, $user) { + if ($fileId == -1) { + return \OCP\PERMISSION_READ; + } $source = \OCP\Share::getItemSharedWithBySource('file', $fileId, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE); if ($source) { return $source['permissions']; diff --git a/apps/files_sharing/lib/share/file.php b/apps/files_sharing/lib/share/file.php index 52220dc5b2..5e98c455d3 100644 --- a/apps/files_sharing/lib/share/file.php +++ b/apps/files_sharing/lib/share/file.php @@ -23,8 +23,9 @@ class OC_Share_Backend_File implements OCP\Share_Backend_File_Dependent { const FORMAT_SHARED_STORAGE = 0; const FORMAT_GET_FOLDER_CONTENTS = 1; - const FORMAT_GET_ALL = 2; + const FORMAT_FILE_APP_ROOT = 2; const FORMAT_OPENDIR = 3; + const FORMAT_GET_ALL = 4; private $path; @@ -82,24 +83,35 @@ class OC_Share_Backend_File implements OCP\Share_Backend_File_Dependent { $file['parent'] = $item['file_parent']; $file['name'] = basename($item['file_target']); $file['mimetype'] = $item['mimetype']; + $file['mimepart'] = $item['mimepart']; $file['size'] = $item['size']; $file['mtime'] = $item['mtime']; $file['encrypted'] = $item['encrypted']; $files[] = $file; } return $files; + } else if ($format == self::FORMAT_FILE_APP_ROOT) { + $mtime = 0; + $size = 0; + foreach ($items as $item) { + if ($item['mtime'] > $mtime) { + $mtime = $item['mtime']; + } + $size += (int)$item['size']; + } + return array('fileid' => -1, 'name' => 'Shared', 'mtime' => $mtime, 'mimetype' => 'httpd/unix-directory', 'size' => $size); + } else if ($format == self::FORMAT_OPENDIR) { + $files = array(); + foreach ($items as $item) { + $files[] = basename($item['file_target']); + } + return $files; } else if ($format == self::FORMAT_GET_ALL) { $ids = array(); foreach ($items as $item) { $ids[] = $item['file_source']; } return $ids; - } else if ($format == self::FORMAT_OPENDIR) { - $files = array(); - foreach ($items as $item) { - $files[] = basename($item['file_target']); - } - return $files; } return array(); } diff --git a/lib/public/share.php b/lib/public/share.php index 2775a07f13..e438386ca3 100644 --- a/lib/public/share.php +++ b/lib/public/share.php @@ -681,7 +681,7 @@ class Share { } } else { if ($fileDependent) { - if (($itemType == 'file' || $itemType == 'folder') && $format == \OC_Share_Backend_File::FORMAT_GET_FOLDER_CONTENTS) { + if (($itemType == 'file' || $itemType == 'folder') && $format == \OC_Share_Backend_File::FORMAT_GET_FOLDER_CONTENTS || $format == \OC_Share_Backend_File::FORMAT_FILE_APP_ROOT) { $select = '`*PREFIX*share`.`id`, `item_type`, `*PREFIX*share`.`parent`, `uid_owner`, `share_type`, `share_with`, `file_source`, `path`, `file_target`, `permissions`, `expiration`, `*PREFIX*filecache`.`parent` as `file_parent`, `name`, `mtime`, `mimetype`, `mimepart`, `size`, `encrypted`'; } else { $select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `item_target`, `*PREFIX*share`.`parent`, `share_type`, `share_with`, `uid_owner`, `file_source`, `path`, `file_target`, `permissions`, `stime`, `expiration`, `token`'; From d275725e233092f21247a62bde3e46a352d5a1b5 Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Sat, 29 Dec 2012 13:43:44 -0500 Subject: [PATCH 127/272] No longer need to create folders locally for external storage mount points --- apps/files_external/lib/config.php | 41 ------------------------------ 1 file changed, 41 deletions(-) diff --git a/apps/files_external/lib/config.php b/apps/files_external/lib/config.php index 0e0bc56ad0..323e4060a4 100755 --- a/apps/files_external/lib/config.php +++ b/apps/files_external/lib/config.php @@ -178,22 +178,6 @@ class OC_Mount_Config { return $personal; } - /** - * Add directory for mount point to the filesystem - * @param OC_Fileview instance $view - * @param string path to mount point - */ - private static function addMountPointDirectory($view, $path) { - $dir = ''; - foreach ( explode('/', $path) as $pathPart) { - $dir = $dir.'/'.$pathPart; - if ( !$view->file_exists($dir)) { - $view->mkdir($dir); - } - } - } - - /** * Add a mount point to the filesystem * @param string Mount point @@ -216,33 +200,8 @@ class OC_Mount_Config { if ($applicable != OCP\User::getUser() || $class == '\OC\Files\Storage\Local') { return false; } - $view = new \OC\Files\View('/'.OCP\User::getUser().'/files'); - self::addMountPointDirectory($view, ltrim($mountPoint, '/')); $mountPoint = '/'.$applicable.'/files/'.ltrim($mountPoint, '/'); } else { - $view = new \OC\Files\View('/'); - switch ($mountType) { - case 'user': - if ($applicable == "all") { - $users = OCP\User::getUsers(); - foreach ( $users as $user ) { - $path = $user.'/files/'.ltrim($mountPoint, '/'); - self::addMountPointDirectory($view, $path); - } - } else { - $path = $applicable.'/files/'.ltrim($mountPoint, '/'); - self::addMountPointDirectory($view, $path); - } - break; - case 'group' : - $groupMembers = OC_Group::usersInGroups(array($applicable)); - foreach ( $groupMembers as $user ) { - $path = $user.'/files/'.ltrim($mountPoint, '/'); - self::addMountPointDirectory($view, $path); - } - break; - } - $mountPoint = '/$user/files/'.ltrim($mountPoint, '/'); } $mount = array($applicable => array($mountPoint => array('class' => $class, 'options' => $classOptions))); From 2c23e143d33e231e62dccb450bf1f1b0f3938ccd Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Sun, 30 Dec 2012 16:32:55 -0500 Subject: [PATCH 128/272] Store etags in the file cache --- db_structure.xml | 8 ++++++++ lib/files/cache/cache.php | 10 +++++----- lib/files/cache/scanner.php | 1 + lib/util.php | 2 +- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/db_structure.xml b/db_structure.xml index aa0916264c..7c67ca78f4 100644 --- a/db_structure.xml +++ b/db_structure.xml @@ -189,6 +189,14 @@ 4 + + etag + text + + true + 250 + + storage_path_hash true diff --git a/lib/files/cache/cache.php b/lib/files/cache/cache.php index 3ebae9baa5..d105f865eb 100644 --- a/lib/files/cache/cache.php +++ b/lib/files/cache/cache.php @@ -76,7 +76,7 @@ class Cache { $params = array($file); } $query = \OC_DB::prepare( - 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted` + 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted`, `etag` FROM `*PREFIX*filecache` ' . $where); $result = $query->execute($params); $data = $result->fetchRow(); @@ -107,7 +107,7 @@ class Cache { $fileId = $this->getId($folder); if ($fileId > -1) { $query = \OC_DB::prepare( - 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted` + 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted`, `etag` FROM `*PREFIX*filecache` WHERE parent = ? ORDER BY `name` ASC'); $result = $query->execute(array($fileId)); return $result->fetchAll(); @@ -180,7 +180,7 @@ class Cache { * @return array */ static function buildParts(array $data) { - $fields = array('path', 'parent', 'name', 'mimetype', 'size', 'mtime', 'encrypted'); + $fields = array('path', 'parent', 'name', 'mimetype', 'size', 'mtime', 'encrypted', 'etag'); $params = array(); $queryParts = array(); @@ -333,7 +333,7 @@ class Cache { */ public function search($pattern) { $query = \OC_DB::prepare(' - SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted` + SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted`, `etag` FROM `*PREFIX*filecache` WHERE `name` LIKE ? AND `storage` = ?' ); $result = $query->execute(array($pattern, $this->numericId)); @@ -357,7 +357,7 @@ class Cache { $where = '`mimepart` = ?'; } $query = \OC_DB::prepare(' - SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted` + SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted`, `etag` FROM `*PREFIX*filecache` WHERE ' . $where . ' AND `storage` = ?' ); $result = $query->execute(array($mimetype, $this->numericId)); diff --git a/lib/files/cache/scanner.php b/lib/files/cache/scanner.php index 4c0ec9617f..e7bfb1898e 100644 --- a/lib/files/cache/scanner.php +++ b/lib/files/cache/scanner.php @@ -58,6 +58,7 @@ class Scanner { $data['size'] = $this->storage->filesize($path); $data['permissions'] = $this->storage->getPermissions($path); } + $data['etag'] = $this->storage->getETag($path); return $data; } diff --git a/lib/util.php b/lib/util.php index c5a495234d..df26a825d1 100755 --- a/lib/util.php +++ b/lib/util.php @@ -74,7 +74,7 @@ class OC_Util { */ public static function getVersion() { // hint: We only can count up. So the internal version number of ownCloud 4.5 will be 4.90.0. This is not visible to the user - return array(4,91,04); + return array(4,91,05); } /** From a5cb7363a5031d25d3e4cbd46817930c19893143 Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Sun, 30 Dec 2012 19:18:05 -0500 Subject: [PATCH 129/272] Use etags from file cache in SabreDAV connector --- lib/connector/sabre/directory.php | 2 +- lib/connector/sabre/node.php | 12 +++++------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/lib/connector/sabre/directory.php b/lib/connector/sabre/directory.php index 8d4dd92a3d..a720157936 100644 --- a/lib/connector/sabre/directory.php +++ b/lib/connector/sabre/directory.php @@ -121,8 +121,8 @@ class OC_Connector_Sabre_Directory extends OC_Connector_Sabre_Node implements Sa $paths = array(); foreach($folder_content as $info) { $paths[] = $this->path.'/'.$info['name']; + $properties[$this->path.'/'.$info['name']][self::GETETAG_PROPERTYNAME] = $info['etag']; } - $properties = array_fill_keys($paths, array()); if(count($paths)>0) { // // the number of arguments within IN conditions are limited in most databases diff --git a/lib/connector/sabre/node.php b/lib/connector/sabre/node.php index ad08bd434f..d4023dfad7 100644 --- a/lib/connector/sabre/node.php +++ b/lib/connector/sabre/node.php @@ -190,6 +190,7 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr while( $row = $result->fetchRow()) { $this->property_cache[$row['propertyname']] = $row['propertyvalue']; } + $this->property_cache[self::GETETAG_PROPERTYNAME] = $this->getETagPropertyForPath($this->path); } // if the array was empty, we need to return everything @@ -210,14 +211,11 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr * @return string|null Returns null if the ETag can not effectively be determined */ static public function getETagPropertyForPath($path) { - $tag = \OC\Files\Filesystem::getETag($path); - if (empty($tag)) { - return null; + $data = \OC\Files\Filesystem::getFileInfo($path); + if (isset($data['etag'])) { + return $data['etag']; } - $etag = '"'.$tag.'"'; - $query = OC_DB::prepare( 'INSERT INTO `*PREFIX*properties` (`userid`,`propertypath`,`propertyname`,`propertyvalue`) VALUES(?,?,?,?)' ); - $query->execute( array( OC_User::getUser(), $path, self::GETETAG_PROPERTYNAME, $etag )); - return $etag; + return null; } /** From 83064aca51db7d8382282743ade2ab9da2a6e1b0 Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Sun, 30 Dec 2012 19:23:31 -0500 Subject: [PATCH 130/272] Remove old etag code --- lib/connector/sabre/node.php | 24 ------------------------ lib/files/filesystem.php | 21 --------------------- lib/files/view.php | 2 -- lib/filesystem.php | 7 ------- 4 files changed, 54 deletions(-) diff --git a/lib/connector/sabre/node.php b/lib/connector/sabre/node.php index d4023dfad7..dd8ae152ff 100644 --- a/lib/connector/sabre/node.php +++ b/lib/connector/sabre/node.php @@ -218,28 +218,4 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr return null; } - /** - * @brief Remove the ETag from the cache. - * @param string $path Path of the file - */ - static public function removeETagPropertyForPath($path) { - // remove tags from this and parent paths - $paths = array(); - while ($path != '/' && $path != '.' && $path != '' && $path != '\\') { - $paths[] = $path; - $path = dirname($path); - } - if (empty($paths)) { - return; - } - $paths[] = $path; - $path_placeholders = join(',', array_fill(0, count($paths), '?')); - $query = OC_DB::prepare( 'DELETE FROM `*PREFIX*properties`' - .' WHERE `userid` = ?' - .' AND `propertyname` = ?' - .' AND `propertypath` IN ('.$path_placeholders.')' - ); - $vals = array( OC_User::getUser(), self::GETETAG_PROPERTYNAME ); - $query->execute(array_merge( $vals, $paths )); - } } diff --git a/lib/files/filesystem.php b/lib/files/filesystem.php index 8183b8ff99..d62b5186cb 100644 --- a/lib/files/filesystem.php +++ b/lib/files/filesystem.php @@ -582,23 +582,6 @@ class Filesystem { return self::$defaultInstance->hasUpdated($path, $time); } - static public function removeETagHook($params, $root = false) { - if (isset($params['path'])) { - $path = $params['path']; - } else { - $path = $params['oldpath']; - } - - if ($root) { // reduce path to the required part of it (no 'username/files') - $fakeRootView = new View($root); - $count = 1; - $path = str_replace(\OC_App::getStorage("files")->getAbsolutePath(''), "", $fakeRootView->getAbsolutePath($path), $count); - } - - $path = self::normalizePath($path); - \OC_Connector_Sabre_Node::removeETagPropertyForPath($path); - } - /** * normalize a path * @@ -682,10 +665,6 @@ class Filesystem { } } -\OC_Hook::connect('OC_Filesystem', 'post_write', 'OC_Filesystem', 'removeETagHook'); -\OC_Hook::connect('OC_Filesystem', 'post_delete', 'OC_Filesystem', 'removeETagHook'); -\OC_Hook::connect('OC_Filesystem', 'post_rename', 'OC_Filesystem', 'removeETagHook'); - \OC_Hook::connect('OC_Filesystem', 'post_write', '\OC\Files\Cache\Updater', 'writeHook'); \OC_Hook::connect('OC_Filesystem', 'post_delete', '\OC\Files\Cache\Updater', 'deleteHook'); \OC_Hook::connect('OC_Filesystem', 'post_rename', '\OC\Files\Cache\Updater', 'renameHook'); diff --git a/lib/files/view.php b/lib/files/view.php index 592c484a21..77146895e6 100644 --- a/lib/files/view.php +++ b/lib/files/view.php @@ -462,8 +462,6 @@ class View { Filesystem::signal_post_write, array(Filesystem::signal_param_path => $path2) ); - } else { // no real copy, file comes from somewhere else, e.g. version rollback -> just update the file cache and the webdav properties without all the other post_write actions - Filesystem::removeETagHook(array("path" => $path2), $this->fakeRoot); } return $result; } else { diff --git a/lib/filesystem.php b/lib/filesystem.php index 20b5ab2790..57cca90230 100644 --- a/lib/filesystem.php +++ b/lib/filesystem.php @@ -398,13 +398,6 @@ class OC_Filesystem { return \OC\Files\Filesystem::hasUpdated($path, $time); } - /** - * @deprecated OC_Filesystem is replaced by \OC\Files\Filesystem - */ - static public function removeETagHook($params, $root = false) { - \OC\Files\Filesystem::removeETagHook($params, $root); - } - /** * normalize a path * From 77f12c526bf0010219e363844b588ffae27f1251 Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Sun, 30 Dec 2012 19:54:51 -0500 Subject: [PATCH 131/272] Update etags in parent folders --- lib/files/cache/updater.php | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/lib/files/cache/updater.php b/lib/files/cache/updater.php index fb9783023e..d57e4a1abe 100644 --- a/lib/files/cache/updater.php +++ b/lib/files/cache/updater.php @@ -34,6 +34,7 @@ class Updater { $scanner = new Scanner($storage); $scanner->scan($internalPath, Scanner::SCAN_SHALLOW); $cache->correctFolderSize($internalPath); + self::eTagUpdate($path); } static public function deleteUpdate($path) { @@ -45,6 +46,29 @@ class Updater { $cache = new Cache($storage); $cache->remove($internalPath); $cache->correctFolderSize($internalPath); + self::eTagUpdate($path); + } + + static public function eTagUpdate($path) { + if ($path !== '') { + $parent = dirname($path); + if ($parent === '.') { + $parent = ''; + } + /** + * @var \OC\Files\Storage\Storage $storage + * @var string $internalPath + */ + list($storage, $internalPath) = self::resolvePath($parent); + if ($storage) { + $cache = $storage->getCache(); + $id = $cache->getId($internalPath); + if ($id !== -1) { + $cache->update($id, array('etag' => $storage->getETag($internalPath))); + self::updateFolderETags($parent); + } + } + } } /** From 3ee3323b8766992a0b60bb76b909e49fc1ea76d2 Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Sun, 30 Dec 2012 20:27:38 -0500 Subject: [PATCH 132/272] Fix retrieving of cache and scanner in Updater class --- lib/files/cache/updater.php | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/lib/files/cache/updater.php b/lib/files/cache/updater.php index fb9783023e..c8c96a97ee 100644 --- a/lib/files/cache/updater.php +++ b/lib/files/cache/updater.php @@ -30,10 +30,12 @@ class Updater { * @var string $internalPath */ list($storage, $internalPath) = self::resolvePath($path); - $cache = new Cache($storage); - $scanner = new Scanner($storage); - $scanner->scan($internalPath, Scanner::SCAN_SHALLOW); - $cache->correctFolderSize($internalPath); + if ($storage) { + $cache = $storage->getCache(); + $scanner = $storage->getScanner(); + $scanner->scan($internalPath, Scanner::SCAN_SHALLOW); + $cache->correctFolderSize($internalPath); + } } static public function deleteUpdate($path) { @@ -42,9 +44,11 @@ class Updater { * @var string $internalPath */ list($storage, $internalPath) = self::resolvePath($path); - $cache = new Cache($storage); - $cache->remove($internalPath); - $cache->correctFolderSize($internalPath); + if ($storage) { + $cache = $storage->getCache(); + $cache->remove($internalPath); + $cache->correctFolderSize($internalPath); + } } /** From 96e08a1d962b42782d03721009970cbab352eec2 Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Sun, 30 Dec 2012 21:23:17 -0500 Subject: [PATCH 133/272] Fix function name --- lib/files/cache/updater.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/files/cache/updater.php b/lib/files/cache/updater.php index d57e4a1abe..7fc9bd382f 100644 --- a/lib/files/cache/updater.php +++ b/lib/files/cache/updater.php @@ -65,7 +65,7 @@ class Updater { $id = $cache->getId($internalPath); if ($id !== -1) { $cache->update($id, array('etag' => $storage->getETag($internalPath))); - self::updateFolderETags($parent); + self::eTagUpdate($parent); } } } From 29b82ccdf32f8a13723e7f49be0c3cbf7e64a404 Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Sun, 30 Dec 2012 21:23:54 -0500 Subject: [PATCH 134/272] Change length of etag field to 40 --- db_structure.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db_structure.xml b/db_structure.xml index 7c67ca78f4..527b53040d 100644 --- a/db_structure.xml +++ b/db_structure.xml @@ -194,7 +194,7 @@ text true - 250 + 40 From d0a50fae8317e4b4871027ee4b2940ab5443961f Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Mon, 31 Dec 2012 18:16:44 -0500 Subject: [PATCH 135/272] Fix eTagUpdate and add tests --- lib/files/cache/updater.php | 2 +- tests/lib/files/cache/updater.php | 28 ++++++++++++++++++++++------ 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/lib/files/cache/updater.php b/lib/files/cache/updater.php index 2604159009..cfc1ec731e 100644 --- a/lib/files/cache/updater.php +++ b/lib/files/cache/updater.php @@ -54,7 +54,7 @@ class Updater { } static public function eTagUpdate($path) { - if ($path !== '') { + if ($path !== '' && $path !== '/') { $parent = dirname($path); if ($parent === '.') { $parent = ''; diff --git a/tests/lib/files/cache/updater.php b/tests/lib/files/cache/updater.php index d19966c191..cad3d9d46f 100644 --- a/tests/lib/files/cache/updater.php +++ b/tests/lib/files/cache/updater.php @@ -70,14 +70,18 @@ class Updater extends \PHPUnit_Framework_TestCase { public function testWrite() { $textSize = strlen("dummy file data\n"); $imageSize = filesize(\OC::$SERVERROOT . '/core/img/logo.png'); - $cachedData = $this->cache->get(''); - $this->assertEquals(3 * $textSize + $imageSize, $cachedData['size']); + $rootCachedData = $this->cache->get(''); + $this->assertEquals(3 * $textSize + $imageSize, $rootCachedData['size']); + $fooCachedData = $this->cache->get('foo.txt'); Filesystem::file_put_contents('foo.txt', 'asd'); $cachedData = $this->cache->get('foo.txt'); $this->assertEquals(3, $cachedData['size']); + $this->assertNotEquals($fooCachedData['etag'], $cachedData['etag']); $cachedData = $this->cache->get(''); $this->assertEquals(2 * $textSize + $imageSize + 3, $cachedData['size']); + $this->assertNotEquals($rootCachedData['etag'], $cachedData['etag']); + $rootCachedData = $cachedData; $this->assertFalse($this->cache->inCache('bar.txt')); Filesystem::file_put_contents('bar.txt', 'asd'); @@ -86,38 +90,50 @@ class Updater extends \PHPUnit_Framework_TestCase { $this->assertEquals(3, $cachedData['size']); $cachedData = $this->cache->get(''); $this->assertEquals(2 * $textSize + $imageSize + 2 * 3, $cachedData['size']); + $this->assertNotEquals($rootCachedData['etag'], $cachedData['etag']); } public function testDelete() { $textSize = strlen("dummy file data\n"); $imageSize = filesize(\OC::$SERVERROOT . '/core/img/logo.png'); - $cachedData = $this->cache->get(''); - $this->assertEquals(3 * $textSize + $imageSize, $cachedData['size']); + $rootCachedData = $this->cache->get(''); + $this->assertEquals(3 * $textSize + $imageSize, $rootCachedData['size']); $this->assertTrue($this->cache->inCache('foo.txt')); Filesystem::unlink('foo.txt', 'asd'); $this->assertFalse($this->cache->inCache('foo.txt')); $cachedData = $this->cache->get(''); $this->assertEquals(2 * $textSize + $imageSize, $cachedData['size']); + $this->assertNotEquals($rootCachedData['etag'], $cachedData['etag']); + $rootCachedData = $cachedData; Filesystem::mkdir('bar_folder'); $this->assertTrue($this->cache->inCache('bar_folder')); + $cachedData = $this->cache->get(''); + $this->assertNotEquals($rootCachedData['etag'], $cachedData['etag']); + $rootCachedData = $cachedData; Filesystem::rmdir('bar_folder'); $this->assertFalse($this->cache->inCache('bar_folder')); + $cachedData = $this->cache->get(''); + $this->assertNotEquals($rootCachedData['etag'], $cachedData['etag']); } public function testRename() { $textSize = strlen("dummy file data\n"); $imageSize = filesize(\OC::$SERVERROOT . '/core/img/logo.png'); - $cachedData = $this->cache->get(''); - $this->assertEquals(3 * $textSize + $imageSize, $cachedData['size']); + $rootCachedData = $this->cache->get(''); + $this->assertEquals(3 * $textSize + $imageSize, $rootCachedData['size']); $this->assertTrue($this->cache->inCache('foo.txt')); + $fooCachedData = $this->cache->get('foo.txt'); $this->assertFalse($this->cache->inCache('bar.txt')); Filesystem::rename('foo.txt', 'bar.txt'); $this->assertFalse($this->cache->inCache('foo.txt')); $this->assertTrue($this->cache->inCache('bar.txt')); + $cachedData = $this->cache->get('foo.txt'); + $this->assertNotEquals($fooCachedData['etag'], $cachedData['etag']); $cachedData = $this->cache->get(''); $this->assertEquals(3 * $textSize + $imageSize, $cachedData['size']); + $this->assertNotEquals($rootCachedData['etag'], $cachedData['etag']); } } From f2ca7023e1ece60dc3cebc5cd770c7373f53c93a Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Tue, 1 Jan 2013 11:19:33 -0500 Subject: [PATCH 136/272] Fix Shared root problems with Watcher and Quota proxy --- apps/files_sharing/lib/cache.php | 3 +++ apps/files_sharing/lib/sharedstorage.php | 13 +++++++++++++ lib/fileproxy/quota.php | 4 +++- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/apps/files_sharing/lib/cache.php b/apps/files_sharing/lib/cache.php index a22e7af7f5..60f29ce5ee 100644 --- a/apps/files_sharing/lib/cache.php +++ b/apps/files_sharing/lib/cache.php @@ -169,6 +169,9 @@ class Shared_Cache extends Cache { * @return int, Cache::NOT_FOUND, Cache::PARTIAL, Cache::SHALLOW or Cache::COMPLETE */ public function getStatus($file) { + if ($file == '') { + return self::COMPLETE; + } if ($cache = $this->getSourceCache($file)) { return $cache->getStatus($this->files[$file]); } diff --git a/apps/files_sharing/lib/sharedstorage.php b/apps/files_sharing/lib/sharedstorage.php index cb9b36482f..3a1d7ef101 100644 --- a/apps/files_sharing/lib/sharedstorage.php +++ b/apps/files_sharing/lib/sharedstorage.php @@ -364,6 +364,9 @@ class Shared extends \OC\Files\Storage\Common { } public function free_space($path) { + if ($path == '') { + return -1; + } $source = $this->getSourcePath($path); if ($source) { list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); @@ -391,6 +394,13 @@ class Shared extends \OC\Files\Storage\Common { \OC\Files\Filesystem::mount('\OC\Files\Storage\Shared', array('sharedFolder' => '/Shared'), $user_dir.'/Shared/'); } + public function hasUpdated($path, $time) { + if ($path == '') { + return false; + } + return $this->filemtime($path) > $time; + } + public function getCache() { return new \OC\Files\Cache\Shared_Cache($this); } @@ -404,6 +414,9 @@ class Shared extends \OC\Files\Storage\Common { } public function getOwner($path) { + if ($path == '') { + return false; + } $source = $this->getFile($path); if ($source) { return $source['uid_owner']; diff --git a/lib/fileproxy/quota.php b/lib/fileproxy/quota.php index 80270728ab..c333efa6cd 100644 --- a/lib/fileproxy/quota.php +++ b/lib/fileproxy/quota.php @@ -63,6 +63,9 @@ class OC_FileProxy_Quota extends OC_FileProxy{ */ list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($path); $owner=$storage->getOwner($internalPath); + if (!$owner) { + return -1; + } $totalSpace=$this->getQuota($owner); if($totalSpace==-1) { @@ -73,7 +76,6 @@ class OC_FileProxy_Quota extends OC_FileProxy{ $rootInfo=$view->getFileInfo('/'); $usedSpace=isset($rootInfo['size'])?$rootInfo['size']:0; - $usedSpace=isset($sharedInfo['size'])?$usedSpace-$sharedInfo['size']:$usedSpace; return $totalSpace-$usedSpace; } From b41189de4420ea8a48adaaf7bb59f9227124b70a Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 1 Jan 2013 18:04:29 +0100 Subject: [PATCH 137/272] Cache: allow storage backends to overwrite Watcher --- lib/files/storage/common.php | 7 +++++++ lib/files/storage/storage.php | 5 +++++ lib/files/view.php | 4 ++-- tests/lib/files/cache/watcher.php | 14 +++++++------- 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/lib/files/storage/common.php b/lib/files/storage/common.php index 3cf960d05d..ab167f2864 100644 --- a/lib/files/storage/common.php +++ b/lib/files/storage/common.php @@ -249,6 +249,13 @@ abstract class Common implements \OC\Files\Storage\Storage { return new \OC\Files\Cache\Permissions($this); } + /** + * @return \OC\Files\Cache\Watcher + */ + public function getWatcher(){ + return new \OC\Files\Cache\Watcher($this); + } + /** * get the owner of a path * @param string $path The path to get the owner diff --git a/lib/files/storage/storage.php b/lib/files/storage/storage.php index 73dcb8fe36..b603381dc9 100644 --- a/lib/files/storage/storage.php +++ b/lib/files/storage/storage.php @@ -69,6 +69,11 @@ interface Storage{ */ public function getPermissionsCache(); + /** + * @return \OC\Files\Cache\Watcher + */ + public function getWatcher(); + /** * get the ETag for a file or folder * diff --git a/lib/files/view.php b/lib/files/view.php index 592c484a21..d3f93d39f5 100644 --- a/lib/files/view.php +++ b/lib/files/view.php @@ -685,7 +685,7 @@ class View { $scanner = $storage->getScanner(); $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW); } else { - $watcher = new \OC\Files\Cache\Watcher($storage); + $watcher = $storage->getWatcher(); $watcher->checkUpdate($internalPath); } @@ -733,7 +733,7 @@ class View { $scanner = $storage->getScanner(); $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW); } else { - $watcher = new \OC\Files\Cache\Watcher($storage); + $watcher = $storage->getWatcher(); $watcher->checkUpdate($internalPath); } diff --git a/tests/lib/files/cache/watcher.php b/tests/lib/files/cache/watcher.php index 07c8ac3640..e8a1689cab 100644 --- a/tests/lib/files/cache/watcher.php +++ b/tests/lib/files/cache/watcher.php @@ -32,7 +32,7 @@ class Watcher extends \PHPUnit_Framework_TestCase { function testWatcher() { $storage = $this->getTestStorage(); $cache = $storage->getCache(); - $updater = new \OC\Files\Cache\Watcher($storage); + $updater = $storage->getWatcher(); //set the mtime to the past so it can detect an mtime change $cache->put('', array('mtime' => 10)); @@ -66,16 +66,16 @@ class Watcher extends \PHPUnit_Framework_TestCase { public function testFileToFolder() { $storage = $this->getTestStorage(); $cache = $storage->getCache(); - $updater = new \OC\Files\Cache\Watcher($storage); + $updater = $storage->getWatcher(); //set the mtime to the past so it can detect an mtime change $cache->put('', array('mtime' => 10)); $storage->unlink('foo.txt'); - $storage->rename('folder','foo.txt'); + $storage->rename('folder', 'foo.txt'); $updater->checkUpdate(''); - $entry= $cache->get('foo.txt'); + $entry = $cache->get('foo.txt'); $this->assertEquals(-1, $entry['size']); $this->assertEquals('httpd/unix-directory', $entry['mimetype']); $this->assertFalse($cache->inCache('folder')); @@ -83,16 +83,16 @@ class Watcher extends \PHPUnit_Framework_TestCase { $storage = $this->getTestStorage(); $cache = $storage->getCache(); - $updater = new \OC\Files\Cache\Watcher($storage); + $updater = $storage->getWatcher(); //set the mtime to the past so it can detect an mtime change $cache->put('foo.txt', array('mtime' => 10)); $storage->unlink('foo.txt'); - $storage->rename('folder','foo.txt'); + $storage->rename('folder', 'foo.txt'); $updater->checkUpdate('foo.txt'); - $entry= $cache->get('foo.txt'); + $entry = $cache->get('foo.txt'); $this->assertEquals('httpd/unix-directory', $entry['mimetype']); $this->assertTrue($cache->inCache('foo.txt/bar.txt')); } From 4b65dd608a70117141c52749f4a06e6c5466ff7b Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 1 Jan 2013 18:07:10 +0100 Subject: [PATCH 138/272] Share: small phpdoc fixes --- apps/files_sharing/lib/cache.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/files_sharing/lib/cache.php b/apps/files_sharing/lib/cache.php index 60f29ce5ee..679e42d94d 100644 --- a/apps/files_sharing/lib/cache.php +++ b/apps/files_sharing/lib/cache.php @@ -36,8 +36,8 @@ class Shared_Cache extends Cache { /** * @brief Get the source cache of a shared file or folder - * @param string Shared target file path - * @return \OC\Files\Storage\Cache + * @param string $target Shared target file path + * @return \OC\Files\Cache\Cache */ private function getSourceCache($target) { $source = \OC_Share_Backend_File::getSource($target); @@ -230,7 +230,7 @@ class Shared_Cache extends Cache { * @return int[] */ public function getAll() { - return OCP\Share::getItemsSharedWith('file', \OC_Share_Backend_File::FORMAT_GET_ALL); + return \OCP\Share::getItemsSharedWith('file', \OC_Share_Backend_File::FORMAT_GET_ALL); } -} \ No newline at end of file +} From 0ac78a64110b628832d94061c78fb8f1b89405cb Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 1 Jan 2013 18:10:38 +0100 Subject: [PATCH 139/272] Share: fix cache put function --- apps/files_sharing/lib/cache.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/files_sharing/lib/cache.php b/apps/files_sharing/lib/cache.php index 679e42d94d..07c305bb95 100644 --- a/apps/files_sharing/lib/cache.php +++ b/apps/files_sharing/lib/cache.php @@ -110,7 +110,7 @@ class Shared_Cache extends Cache { */ public function put($file, array $data) { if ($cache = $this->getSourceCache($file)) { - return $cache->put($this->files[$file]); + return $cache->put($this->files[$file], $data); } return false; } From f4e4a06826459befc4a7df429fe02081a14348f3 Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Tue, 1 Jan 2013 11:26:04 -0500 Subject: [PATCH 140/272] Forgot to select storage from filecache --- lib/public/share.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/public/share.php b/lib/public/share.php index e438386ca3..c74960b94c 100644 --- a/lib/public/share.php +++ b/lib/public/share.php @@ -682,7 +682,7 @@ class Share { } else { if ($fileDependent) { if (($itemType == 'file' || $itemType == 'folder') && $format == \OC_Share_Backend_File::FORMAT_GET_FOLDER_CONTENTS || $format == \OC_Share_Backend_File::FORMAT_FILE_APP_ROOT) { - $select = '`*PREFIX*share`.`id`, `item_type`, `*PREFIX*share`.`parent`, `uid_owner`, `share_type`, `share_with`, `file_source`, `path`, `file_target`, `permissions`, `expiration`, `*PREFIX*filecache`.`parent` as `file_parent`, `name`, `mtime`, `mimetype`, `mimepart`, `size`, `encrypted`'; + $select = '`*PREFIX*share`.`id`, `item_type`, `*PREFIX*share`.`parent`, `uid_owner`, `share_type`, `share_with`, `file_source`, `path`, `file_target`, `permissions`, `expiration`, `storage`, `*PREFIX*filecache`.`parent` as `file_parent`, `name`, `mtime`, `mimetype`, `mimepart`, `size`, `encrypted`'; } else { $select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `item_target`, `*PREFIX*share`.`parent`, `share_type`, `share_with`, `uid_owner`, `file_source`, `path`, `file_target`, `permissions`, `stime`, `expiration`, `token`'; } From 3b67613afc6d0b74c0e49e567f5e22ec020172d3 Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Tue, 1 Jan 2013 12:43:38 -0500 Subject: [PATCH 141/272] Remove Shared_Scanner and add Shared_Watcher instead --- apps/files_sharing/appinfo/app.php | 2 +- apps/files_sharing/lib/scanner.php | 69 ------------------------ apps/files_sharing/lib/sharedstorage.php | 8 +-- 3 files changed, 5 insertions(+), 74 deletions(-) delete mode 100644 apps/files_sharing/lib/scanner.php diff --git a/apps/files_sharing/appinfo/app.php b/apps/files_sharing/appinfo/app.php index 189fd20cae..d3e05cc62d 100644 --- a/apps/files_sharing/appinfo/app.php +++ b/apps/files_sharing/appinfo/app.php @@ -5,7 +5,7 @@ OC::$CLASSPATH['OC_Share_Backend_Folder'] = 'apps/files_sharing/lib/share/folder OC::$CLASSPATH['OC\Files\Storage\Shared'] = "apps/files_sharing/lib/sharedstorage.php"; OC::$CLASSPATH['OC\Files\Cache\Shared_Cache'] = 'apps/files_sharing/lib/cache.php'; OC::$CLASSPATH['OC\Files\Cache\Shared_Permissions'] = 'apps/files_sharing/lib/permissions.php'; -OC::$CLASSPATH['OC\Files\Cache\Shared_Scanner'] = 'apps/files_sharing/lib/scanner.php'; +OC::$CLASSPATH['OC\Files\Cache\Shared_Watcher'] = 'apps/files_sharing/lib/watcher.php'; OCP\Util::connectHook('OC_Filesystem', 'setup', '\OC\Files\Storage\Shared', 'setup'); OCP\Share::registerBackend('file', 'OC_Share_Backend_File'); OCP\Share::registerBackend('folder', 'OC_Share_Backend_Folder', 'file'); diff --git a/apps/files_sharing/lib/scanner.php b/apps/files_sharing/lib/scanner.php deleted file mode 100644 index d13d2f9cbc..0000000000 --- a/apps/files_sharing/lib/scanner.php +++ /dev/null @@ -1,69 +0,0 @@ -. -*/ - -namespace OC\Files\Cache; - -class Shared_Scanner extends Scanner { - - public function __construct(\OC\Files\Storage\Storage $storage) { - - } - - /** - * get all the metadata of a file or folder - * * - * - * @param string $path - * @return array with metadata of the file - */ - public function getData($path) { - // Not a valid action for Shared Scanner - } - - /** - * scan a single file and store it in the cache - * - * @param string $file - * @return array with metadata of the scanned file - */ - public function scanFile($file) { - // Not a valid action for Shared Scanner - } - - /** - * scan all the files in a folder and store them in the cache - * - * @param string $path - * @param SCAN_RECURSIVE/SCAN_SHALLOW $recursive - * @return int the size of the scanned folder or -1 if the size is unknown at this stage - */ - public function scan($path, $recursive = self::SCAN_RECURSIVE) { - // Not a valid action for Shared Scanner - } - - /** - * walk over any folders that are not fully scanned yet and scan them - */ - public function backgroundScan() { - // Not a valid action for Shared Scanner - } - -} \ No newline at end of file diff --git a/apps/files_sharing/lib/sharedstorage.php b/apps/files_sharing/lib/sharedstorage.php index 3a1d7ef101..8504a2d894 100644 --- a/apps/files_sharing/lib/sharedstorage.php +++ b/apps/files_sharing/lib/sharedstorage.php @@ -405,14 +405,14 @@ class Shared extends \OC\Files\Storage\Common { return new \OC\Files\Cache\Shared_Cache($this); } - public function getScanner(){ - return new \OC\Files\Cache\Shared_Scanner($this); - } - public function getPermissionsCache() { return new \OC\Files\Cache\Shared_Permissions($this); } + public function getWatcher() { + return new \OC\Files\Cache\Shared_Watcher($this); + } + public function getOwner($path) { if ($path == '') { return false; From 268c7acfc47eb44ab154eb90cfbb4484bd64b296 Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Tue, 1 Jan 2013 13:16:42 -0500 Subject: [PATCH 142/272] Actually add Shared_Watcher --- apps/files_sharing/lib/watcher.php | 51 ++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 apps/files_sharing/lib/watcher.php diff --git a/apps/files_sharing/lib/watcher.php b/apps/files_sharing/lib/watcher.php new file mode 100644 index 0000000000..e67d1ee908 --- /dev/null +++ b/apps/files_sharing/lib/watcher.php @@ -0,0 +1,51 @@ +. +*/ + +namespace OC\Files\Cache; + +/** + * check the storage backends for updates and change the cache accordingly + */ +class Shared_Watcher extends Watcher { + + /** + * check $path for updates + * + * @param string $path + */ + public function checkUpdate($path) { + if ($path != '') { + parent::checkUpdate($path); + } + } + + /** + * remove deleted files in $path from the cache + * + * @param string $path + */ + public function cleanFolder($path) { + if ($path != '') { + parent::cleanFolder($path); + } + } + +} \ No newline at end of file From 04f83e3b539c6a20ce819bbe4f66c1996e7169f7 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 1 Jan 2013 20:11:39 +0100 Subject: [PATCH 143/272] Cache: optional path argument for getCache/Scanner/etc --- lib/files/storage/common.php | 11 ++++------- lib/files/storage/storage.php | 12 ++++++++---- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/lib/files/storage/common.php b/lib/files/storage/common.php index ab167f2864..e859d447f3 100644 --- a/lib/files/storage/common.php +++ b/lib/files/storage/common.php @@ -237,22 +237,19 @@ abstract class Common implements \OC\Files\Storage\Storage { return $this->filemtime($path)>$time; } - public function getCache(){ + public function getCache($path=''){ return new \OC\Files\Cache\Cache($this); } - public function getScanner(){ + public function getScanner($path=''){ return new \OC\Files\Cache\Scanner($this); } - public function getPermissionsCache(){ + public function getPermissionsCache($path=''){ return new \OC\Files\Cache\Permissions($this); } - /** - * @return \OC\Files\Cache\Watcher - */ - public function getWatcher(){ + public function getWatcher($path=''){ return new \OC\Files\Cache\Watcher($this); } diff --git a/lib/files/storage/storage.php b/lib/files/storage/storage.php index b603381dc9..2cc835236b 100644 --- a/lib/files/storage/storage.php +++ b/lib/files/storage/storage.php @@ -54,25 +54,29 @@ interface Storage{ public function hasUpdated($path,$time); /** + * @param string $path * @return \OC\Files\Cache\Cache */ - public function getCache(); + public function getCache($path=''); /** + * @param string $path * @return \OC\Files\Cache\Scanner */ - public function getScanner(); + public function getScanner($path=''); public function getOwner($path); /** + * @param string $path * @return \OC\Files\Cache\Permissions */ - public function getPermissionsCache(); + public function getPermissionsCache($path=''); /** + * @param string $path * @return \OC\Files\Cache\Watcher */ - public function getWatcher(); + public function getWatcher($path=''); /** * get the ETag for a file or folder From a164fd160fb0d3da548dfe01ec27df67d76b0b59 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 1 Jan 2013 20:19:53 +0100 Subject: [PATCH 144/272] Cache: provide path hints to getCache/etc where available --- lib/files/cache/updater.php | 6 +++--- lib/files/view.php | 28 ++++++++++++++-------------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/lib/files/cache/updater.php b/lib/files/cache/updater.php index c8c96a97ee..c14adba3e5 100644 --- a/lib/files/cache/updater.php +++ b/lib/files/cache/updater.php @@ -31,8 +31,8 @@ class Updater { */ list($storage, $internalPath) = self::resolvePath($path); if ($storage) { - $cache = $storage->getCache(); - $scanner = $storage->getScanner(); + $cache = $storage->getCache($internalPath); + $scanner = $storage->getScanner($internalPath); $scanner->scan($internalPath, Scanner::SCAN_SHALLOW); $cache->correctFolderSize($internalPath); } @@ -45,7 +45,7 @@ class Updater { */ list($storage, $internalPath) = self::resolvePath($path); if ($storage) { - $cache = $storage->getCache(); + $cache = $storage->getCache($internalPath); $cache->remove($internalPath); $cache->correctFolderSize($internalPath); } diff --git a/lib/files/view.php b/lib/files/view.php index d3f93d39f5..8558b03fe1 100644 --- a/lib/files/view.php +++ b/lib/files/view.php @@ -679,13 +679,13 @@ class View { */ list($storage, $internalPath) = Filesystem::resolvePath($path); if ($storage) { - $cache = $storage->getCache(); + $cache = $storage->getCache($internalPath); if (!$cache->inCache($internalPath)) { - $scanner = $storage->getScanner(); + $scanner = $storage->getScanner($internalPath); $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW); } else { - $watcher = $storage->getWatcher(); + $watcher = $storage->getWatcher($internalPath); $watcher->checkUpdate($internalPath); } @@ -698,14 +698,14 @@ class View { foreach ($mountPoints as $mountPoint) { $subStorage = Filesystem::getStorage($mountPoint); if ($subStorage) { - $subCache = $subStorage->getCache(); + $subCache = $subStorage->getCache(''); $rootEntry = $subCache->get(''); $data['size'] += $rootEntry['size']; } } } - $permissionsCache = $storage->getPermissionsCache(); + $permissionsCache = $storage->getPermissionsCache($internalPath); $data['permissions'] = $permissionsCache->get($data['fileid'], \OC_User::getUser()); } } @@ -727,13 +727,13 @@ class View { */ list($storage, $internalPath) = Filesystem::resolvePath($path); if ($storage) { - $cache = $storage->getCache(); + $cache = $storage->getCache($internalPath); if ($cache->getStatus($internalPath) < Cache\Cache::COMPLETE) { - $scanner = $storage->getScanner(); + $scanner = $storage->getScanner($internalPath); $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW); } else { - $watcher = $storage->getWatcher(); + $watcher = $storage->getWatcher($internalPath); $watcher->checkUpdate($internalPath); } @@ -745,7 +745,7 @@ class View { foreach ($mountPoints as $mountPoint) { $subStorage = Filesystem::getStorage($mountPoint); if ($subStorage) { - $subCache = $subStorage->getCache(); + $subCache = $subStorage->getCache(''); $rootEntry = $subCache->get(''); $relativePath = trim(substr($mountPoint, $dirLength), '/'); @@ -769,7 +769,7 @@ class View { $files[$i]['type'] = $file['mimetype'] === 'httpd/unix-directory' ? 'dir' : 'file'; $ids[] = $file['fileid']; } - $permissionsCache = $storage->getPermissionsCache(); + $permissionsCache = $storage->getPermissionsCache($internalPath); $permissions = $permissionsCache->getMultiple($ids, \OC_User::getUser()); foreach ($files as $i => $file) { @@ -812,10 +812,10 @@ class View { */ list($storage, $internalPath) = Filesystem::resolvePath($path); if ($storage) { - $cache = $storage->getCache(); + $cache = $storage->getCache($path); if (!$cache->inCache($internalPath)) { - $scanner = $storage->getScanner(); + $scanner = $storage->getScanner($internalPath); $scanner->scan($internalPath, Cache\Scanner::SCAN_SHALLOW); } @@ -857,7 +857,7 @@ class View { $mountPoint = Filesystem::getMountPoint($this->fakeRoot); $storage = Filesystem::getStorage($mountPoint); if ($storage) { - $cache = $storage->getCache(); + $cache = $storage->getCache(''); $results = $cache->$method($query); foreach ($results as $result) { @@ -871,7 +871,7 @@ class View { foreach ($mountPoints as $mountPoint) { $storage = Filesystem::getStorage($mountPoint); if ($storage) { - $cache = $storage->getCache(); + $cache = $storage->getCache(''); $relativeMountPoint = substr($mountPoint, $rootLength); $results = $cache->$method($query); From fe90130618e4ce9b1829b4ce8e87311a15f53b83 Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Tue, 1 Jan 2013 14:13:05 -0500 Subject: [PATCH 145/272] Initialize storageId variable in shared cache --- apps/files_sharing/lib/cache.php | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/files_sharing/lib/cache.php b/apps/files_sharing/lib/cache.php index 07c305bb95..0534d6dd89 100644 --- a/apps/files_sharing/lib/cache.php +++ b/apps/files_sharing/lib/cache.php @@ -48,6 +48,7 @@ class Shared_Cache extends Cache { if ($storage) { $this->files[$target] = $internalPath; $cache = $storage->getCache(); + $this->storageId = $storage->getId(); $this->numericId = $cache->getNumericStorageId(); return $cache; } From a1f7c28e28e221a6939a74eb5ae7b43c02a1d8e6 Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Tue, 1 Jan 2013 14:47:25 -0500 Subject: [PATCH 146/272] Use the source Scanner for shared storage when the path is specified --- apps/files_sharing/lib/sharedstorage.php | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/apps/files_sharing/lib/sharedstorage.php b/apps/files_sharing/lib/sharedstorage.php index 8504a2d894..3a5755c01f 100644 --- a/apps/files_sharing/lib/sharedstorage.php +++ b/apps/files_sharing/lib/sharedstorage.php @@ -401,15 +401,25 @@ class Shared extends \OC\Files\Storage\Common { return $this->filemtime($path) > $time; } - public function getCache() { + public function getCache($path = '') { return new \OC\Files\Cache\Shared_Cache($this); } - public function getPermissionsCache() { + public function getScanner($path = '') { + if ($path != '' && ($source = $this->getSourcePath($path))) { + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); + if ($storage) { + return $storage->getScanner($internalPath); + } + } + return new \OC\Files\Cache\Scanner($this); + } + + public function getPermissionsCache($path = '') { return new \OC\Files\Cache\Shared_Permissions($this); } - public function getWatcher() { + public function getWatcher($path = '') { return new \OC\Files\Cache\Shared_Watcher($this); } From a068ddff64bb6071de204b948129c4ca23c16d26 Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Wed, 2 Jan 2013 14:40:06 -0500 Subject: [PATCH 147/272] Use the sub storage's permission cache for retrieving the correct permission --- lib/files/view.php | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/lib/files/view.php b/lib/files/view.php index 8558b03fe1..9ba3eea3cf 100644 --- a/lib/files/view.php +++ b/lib/files/view.php @@ -739,6 +739,18 @@ class View { $files = $cache->getFolderContents($internalPath); //TODO: mimetype_filter + $ids = array(); + foreach ($files as $i => $file) { + $files[$i]['type'] = $file['mimetype'] === 'httpd/unix-directory' ? 'dir' : 'file'; + $ids[] = $file['fileid']; + } + + $permissionsCache = $storage->getPermissionsCache($internalPath); + $permissions = $permissionsCache->getMultiple($ids, \OC_User::getUser()); + foreach ($files as $i => $file) { + $files[$i]['permissions'] = $permissions[$file['fileid']]; + } + //add a folder for any mountpoint in this directory and add the sizes of other mountpoints to the folders $mountPoints = Filesystem::getMountPoints($path); $dirLength = strlen($path); @@ -758,24 +770,14 @@ class View { } } else { //mountpoint in this folder, add an entry for it $rootEntry['name'] = $relativePath; + $rootEntry['type'] = $rootEntry['mimetype'] === 'httpd/unix-directory' ? 'dir' : 'file'; + $subPermissionsCache = $subStorage->getPermissionsCache(''); + $rootEntry['permissions'] = $subPermissionsCache->get($rootEntry['fileid'], \OC_User::getUser()); $files[] = $rootEntry; } } } - $ids = array(); - - foreach ($files as $i => $file) { - $files[$i]['type'] = $file['mimetype'] === 'httpd/unix-directory' ? 'dir' : 'file'; - $ids[] = $file['fileid']; - } - $permissionsCache = $storage->getPermissionsCache($internalPath); - - $permissions = $permissionsCache->getMultiple($ids, \OC_User::getUser()); - foreach ($files as $i => $file) { - $files[$i]['permissions'] = $permissions[$file['fileid']]; - } - if ($mimetype_filter) { foreach ($files as $file) { if (strpos($mimetype_filter, '/')) { From 6a6d6756b7a6495d044d44c8d6081505880d96b9 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 3 Jan 2013 00:29:10 +0100 Subject: [PATCH 148/272] Cache: rename index to prevent colissions with old fscache indexes --- db_structure.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/db_structure.xml b/db_structure.xml index aa0916264c..e47cf3c629 100644 --- a/db_structure.xml +++ b/db_structure.xml @@ -190,7 +190,7 @@ - storage_path_hash + fs_storage_path_hash true storage @@ -203,7 +203,7 @@ - parent_name_hash + fs_parent_name_hash parent ascending From f144be8857e3356495acefca787637b5672f73ef Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Wed, 2 Jan 2013 19:20:34 -0500 Subject: [PATCH 149/272] Don't mount shared storage unless there are shared files --- apps/files_sharing/lib/sharedstorage.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/files_sharing/lib/sharedstorage.php b/apps/files_sharing/lib/sharedstorage.php index 3a5755c01f..24096e0c10 100644 --- a/apps/files_sharing/lib/sharedstorage.php +++ b/apps/files_sharing/lib/sharedstorage.php @@ -390,8 +390,10 @@ class Shared extends \OC\Files\Storage\Common { } public static function setup($options) { - $user_dir = $options['user_dir']; - \OC\Files\Filesystem::mount('\OC\Files\Storage\Shared', array('sharedFolder' => '/Shared'), $user_dir.'/Shared/'); + if (\OCP\Share::getItemsSharedWith('file')) { + $user_dir = $options['user_dir']; + \OC\Files\Filesystem::mount('\OC\Files\Storage\Shared', array('sharedFolder' => '/Shared'), $user_dir.'/Shared/'); + } } public function hasUpdated($path, $time) { From 38876fc98a11a917e68780357d7f4b5a146ba89a Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Thu, 3 Jan 2013 12:07:04 -0500 Subject: [PATCH 150/272] Update old storage classes names to the new namespace during mounting --- apps/files_external/lib/config.php | 12 ++++++++++++ lib/files/filesystem.php | 5 ++++- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/apps/files_external/lib/config.php b/apps/files_external/lib/config.php index 323e4060a4..d7286c52c0 100755 --- a/apps/files_external/lib/config.php +++ b/apps/files_external/lib/config.php @@ -120,6 +120,10 @@ class OC_Mount_Config { if (isset($mountPoints[self::MOUNT_TYPE_GROUP])) { foreach ($mountPoints[self::MOUNT_TYPE_GROUP] as $group => $mounts) { foreach ($mounts as $mountPoint => $mount) { + // Update old classes to new namespace + if (strpos($mount['class'], 'OC_Filestorage_') !== false) { + $mount['class'] = '\OC\Files\Storage\\'.substr($mount['class'], 15, strlen($mount['class']) - 15); + } // Remove '/$user/files/' from mount point $mountPoint = substr($mountPoint, 13); // Merge the mount point into the current mount points @@ -139,6 +143,10 @@ class OC_Mount_Config { if (isset($mountPoints[self::MOUNT_TYPE_USER])) { foreach ($mountPoints[self::MOUNT_TYPE_USER] as $user => $mounts) { foreach ($mounts as $mountPoint => $mount) { + // Update old classes to new namespace + if (strpos($mount['class'], 'OC_Filestorage_') !== false) { + $mount['class'] = '\OC\Files\Storage\\'.substr($mount['class'], 15, strlen($mount['class']) - 15); + } // Remove '/$user/files/' from mount point $mountPoint = substr($mountPoint, 13); // Merge the mount point into the current mount points @@ -169,6 +177,10 @@ class OC_Mount_Config { $personal = array(); if (isset($mountPoints[self::MOUNT_TYPE_USER][$uid])) { foreach ($mountPoints[self::MOUNT_TYPE_USER][$uid] as $mountPoint => $mount) { + // Update old classes to new namespace + if (strpos($mount['class'], 'OC_Filestorage_') !== false) { + $mount['class'] = '\OC\Files\Storage\\'.substr($mount['class'], 15, strlen($mount['class']) - 15); + } // Remove '/uid/files/' from mount point $personal[substr($mountPoint, strlen($uid) + 8)] = array('class' => $mount['class'], 'backend' => $backends[$mount['class']]['backend'], diff --git a/lib/files/filesystem.php b/lib/files/filesystem.php index 8183b8ff99..b9fd6a0376 100644 --- a/lib/files/filesystem.php +++ b/lib/files/filesystem.php @@ -364,7 +364,10 @@ class Filesystem { if (strlen($mountpoint) > 1) { $mountpoint .= '/'; } - + // Update old classes to new namespace + if (strpos($class, 'OC_Filestorage_') !== false) { + $class = '\OC\Files\Storage\\'.substr($class, 15, strlen($class) - 15); + } if ($class instanceof \OC\Files\Storage\Storage) { self::$mounts[$mountpoint] = array('class' => get_class($class), 'arguments' => $arguments); self::$storages[$mountpoint] = $class; From 1137723b2a46c37d61e7f501694c73562b21ef74 Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Thu, 3 Jan 2013 12:13:45 -0500 Subject: [PATCH 151/272] Remove unnecessary length parameter from last commit --- apps/files_external/lib/config.php | 6 +++--- lib/files/filesystem.php | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/files_external/lib/config.php b/apps/files_external/lib/config.php index d7286c52c0..3bb512493d 100755 --- a/apps/files_external/lib/config.php +++ b/apps/files_external/lib/config.php @@ -122,7 +122,7 @@ class OC_Mount_Config { foreach ($mounts as $mountPoint => $mount) { // Update old classes to new namespace if (strpos($mount['class'], 'OC_Filestorage_') !== false) { - $mount['class'] = '\OC\Files\Storage\\'.substr($mount['class'], 15, strlen($mount['class']) - 15); + $mount['class'] = '\OC\Files\Storage\\'.substr($mount['class'], 15); } // Remove '/$user/files/' from mount point $mountPoint = substr($mountPoint, 13); @@ -145,7 +145,7 @@ class OC_Mount_Config { foreach ($mounts as $mountPoint => $mount) { // Update old classes to new namespace if (strpos($mount['class'], 'OC_Filestorage_') !== false) { - $mount['class'] = '\OC\Files\Storage\\'.substr($mount['class'], 15, strlen($mount['class']) - 15); + $mount['class'] = '\OC\Files\Storage\\'.substr($mount['class'], 15); } // Remove '/$user/files/' from mount point $mountPoint = substr($mountPoint, 13); @@ -179,7 +179,7 @@ class OC_Mount_Config { foreach ($mountPoints[self::MOUNT_TYPE_USER][$uid] as $mountPoint => $mount) { // Update old classes to new namespace if (strpos($mount['class'], 'OC_Filestorage_') !== false) { - $mount['class'] = '\OC\Files\Storage\\'.substr($mount['class'], 15, strlen($mount['class']) - 15); + $mount['class'] = '\OC\Files\Storage\\'.substr($mount['class'], 15); } // Remove '/uid/files/' from mount point $personal[substr($mountPoint, strlen($uid) + 8)] = array('class' => $mount['class'], diff --git a/lib/files/filesystem.php b/lib/files/filesystem.php index b9fd6a0376..d9487bde80 100644 --- a/lib/files/filesystem.php +++ b/lib/files/filesystem.php @@ -366,7 +366,7 @@ class Filesystem { } // Update old classes to new namespace if (strpos($class, 'OC_Filestorage_') !== false) { - $class = '\OC\Files\Storage\\'.substr($class, 15, strlen($class) - 15); + $class = '\OC\Files\Storage\\'.substr($class, 15); } if ($class instanceof \OC\Files\Storage\Storage) { self::$mounts[$mountpoint] = array('class' => get_class($class), 'arguments' => $arguments); From 0ca5047da56c1b6d0614174b01d42bace85d53ee Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 7 Jan 2013 00:36:01 +0100 Subject: [PATCH 152/272] Autoload namespaced test classes --- lib/base.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/base.php b/lib/base.php index 3d3e7d59f9..80e5c5ed77 100644 --- a/lib/base.php +++ b/lib/base.php @@ -105,6 +105,8 @@ class OC $path = str_replace('\\', '/', $className) . '.php'; } elseif (strpos($className, 'Test_') === 0) { $path = 'tests/lib/' . strtolower(str_replace('_', '/', substr($className, 5)) . '.php'); + } elseif (strpos($className, 'Test\\') === 0) { + $path = 'tests/lib/' . strtolower(str_replace('\\', '/', substr($className, 5)) . '.php'); } else { return false; } From 457dc270f5147863c00b55355542298ba4d41762 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 7 Jan 2013 00:36:39 +0100 Subject: [PATCH 153/272] Fix messed up mounts --- lib/files/filesystem.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/files/filesystem.php b/lib/files/filesystem.php index d9487bde80..b3ba62c3a4 100644 --- a/lib/files/filesystem.php +++ b/lib/files/filesystem.php @@ -364,14 +364,14 @@ class Filesystem { if (strlen($mountpoint) > 1) { $mountpoint .= '/'; } - // Update old classes to new namespace - if (strpos($class, 'OC_Filestorage_') !== false) { - $class = '\OC\Files\Storage\\'.substr($class, 15); - } if ($class instanceof \OC\Files\Storage\Storage) { self::$mounts[$mountpoint] = array('class' => get_class($class), 'arguments' => $arguments); self::$storages[$mountpoint] = $class; } else { + // Update old classes to new namespace + if (strpos($class, 'OC_Filestorage_') !== false) { + $class = '\OC\Files\Storage\\'.substr($class, 15); + } self::$mounts[$mountpoint] = array('class' => $class, 'arguments' => $arguments); } } From 439578288facbae3144f131aca85a7235f622053 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 7 Jan 2013 01:03:11 +0100 Subject: [PATCH 154/272] Cache: split permission cache scanning and cache scanning --- lib/files/cache/scanner.php | 9 --------- lib/files/view.php | 30 ++++++++++++++++++++++-------- tests/lib/files/view.php | 1 + 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/lib/files/cache/scanner.php b/lib/files/cache/scanner.php index 4c0ec9617f..526d4a2aab 100644 --- a/lib/files/cache/scanner.php +++ b/lib/files/cache/scanner.php @@ -24,11 +24,6 @@ class Scanner { */ private $cache; - /** - * @var \OC\Files\Cache\Permissions $permissionsCache - */ - private $permissionsCache; - const SCAN_RECURSIVE = true; const SCAN_SHALLOW = false; @@ -36,7 +31,6 @@ class Scanner { $this->storage = $storage; $this->storageId = $this->storage->getId(); $this->cache = $storage->getCache(); - $this->permissionsCache = $storage->getPermissionsCache(); } /** @@ -53,10 +47,8 @@ class Scanner { $data['mtime'] = $this->storage->filemtime($path); if ($data['mimetype'] == 'httpd/unix-directory') { $data['size'] = -1; //unknown - $data['permissions'] = $this->storage->getPermissions($path . '/'); } else { $data['size'] = $this->storage->filesize($path); - $data['permissions'] = $this->storage->getPermissions($path); } return $data; } @@ -81,7 +73,6 @@ class Scanner { } } $id = $this->cache->put($file, $data); - $this->permissionsCache->set($id, \OC_User::getUser(), $data['permissions']); } return $data; } diff --git a/lib/files/view.php b/lib/files/view.php index 9ba3eea3cf..124345f3c6 100644 --- a/lib/files/view.php +++ b/lib/files/view.php @@ -680,6 +680,8 @@ class View { list($storage, $internalPath) = Filesystem::resolvePath($path); if ($storage) { $cache = $storage->getCache($internalPath); + $permissionsCache = $storage->getPermissionsCache($internalPath); + $user = \OC_User::getUser(); if (!$cache->inCache($internalPath)) { $scanner = $storage->getScanner($internalPath); @@ -705,8 +707,12 @@ class View { } } - $permissionsCache = $storage->getPermissionsCache($internalPath); - $data['permissions'] = $permissionsCache->get($data['fileid'], \OC_User::getUser()); + $permissions = $permissionsCache->get($data['fileid'], $user); + if ($permissions === -1) { + $permissions = $storage->getPermissions($internalPath); + $permissionsCache->set($data['fileid'], $user, $permissions); + } + $data['permissions'] = $permissions; } } return $data; @@ -728,6 +734,8 @@ class View { list($storage, $internalPath) = Filesystem::resolvePath($path); if ($storage) { $cache = $storage->getCache($internalPath); + $permissionsCache = $storage->getPermissionsCache($internalPath); + $user = \OC_User::getUser(); if ($cache->getStatus($internalPath) < Cache\Cache::COMPLETE) { $scanner = $storage->getScanner($internalPath); @@ -743,12 +751,13 @@ class View { foreach ($files as $i => $file) { $files[$i]['type'] = $file['mimetype'] === 'httpd/unix-directory' ? 'dir' : 'file'; $ids[] = $file['fileid']; - } - $permissionsCache = $storage->getPermissionsCache($internalPath); - $permissions = $permissionsCache->getMultiple($ids, \OC_User::getUser()); - foreach ($files as $i => $file) { - $files[$i]['permissions'] = $permissions[$file['fileid']]; + $permissions = $permissionsCache->get($file['fileid'], $user); + if ($permissions === -1) { + $permissions = $storage->getPermissions($file['path']); + $permissionsCache->set($file['fileid'], $user, $permissions); + } + $files[$i]['permissions'] = $permissions; } //add a folder for any mountpoint in this directory and add the sizes of other mountpoints to the folders @@ -772,7 +781,12 @@ class View { $rootEntry['name'] = $relativePath; $rootEntry['type'] = $rootEntry['mimetype'] === 'httpd/unix-directory' ? 'dir' : 'file'; $subPermissionsCache = $subStorage->getPermissionsCache(''); - $rootEntry['permissions'] = $subPermissionsCache->get($rootEntry['fileid'], \OC_User::getUser()); + $permissions = $subPermissionsCache->get($rootEntry['fileid'], $user); + if ($permissions === -1) { + $permissions = $subStorage->getPermissions($rootEntry['path']); + $subPermissionsCache->set($rootEntry['fileid'], $user, $permissions); + } + $rootEntry['permissions'] = $subPermissionsCache; $files[] = $rootEntry; } } diff --git a/tests/lib/files/view.php b/tests/lib/files/view.php index 712166ab32..4b0abc2201 100644 --- a/tests/lib/files/view.php +++ b/tests/lib/files/view.php @@ -43,6 +43,7 @@ class View extends \PHPUnit_Framework_TestCase { $cachedData = $rootView->getFileInfo('/foo.txt'); $this->assertEquals($textSize, $cachedData['size']); $this->assertEquals('text/plain', $cachedData['mimetype']); + $this->assertEquals(\OCP\PERMISSION_ALL ^ \OCP\PERMISSION_CREATE, $cachedData['permissions']); $cachedData = $rootView->getFileInfo('/'); $this->assertEquals($storageSize * 3, $cachedData['size']); From ad3badeabff9dde839827558c281a691c611cf87 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 7 Jan 2013 01:03:11 +0100 Subject: [PATCH 155/272] Cache: split permission cache scanning and cache scanning --- tests/lib/files/view.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/lib/files/view.php b/tests/lib/files/view.php index 4b0abc2201..5327114267 100644 --- a/tests/lib/files/view.php +++ b/tests/lib/files/view.php @@ -43,7 +43,7 @@ class View extends \PHPUnit_Framework_TestCase { $cachedData = $rootView->getFileInfo('/foo.txt'); $this->assertEquals($textSize, $cachedData['size']); $this->assertEquals('text/plain', $cachedData['mimetype']); - $this->assertEquals(\OCP\PERMISSION_ALL ^ \OCP\PERMISSION_CREATE, $cachedData['permissions']); + $this->assertNotEquals(-1, $cachedData['permissions']); $cachedData = $rootView->getFileInfo('/'); $this->assertEquals($storageSize * 3, $cachedData['size']); From d0377b1951a156e218ca0200340e2bcfb51ac0c8 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 7 Jan 2013 01:40:09 +0100 Subject: [PATCH 156/272] Cache: normalize mimetypes --- db_structure.xml | 44 ++++++++++++++++++++++++++--- lib/files/cache/cache.php | 59 +++++++++++++++++++++++++++++++++++---- lib/files/view.php | 2 +- lib/util.php | 2 +- 4 files changed, 96 insertions(+), 11 deletions(-) diff --git a/db_structure.xml b/db_structure.xml index e47cf3c629..3022983473 100644 --- a/db_structure.xml +++ b/db_structure.xml @@ -94,6 +94,42 @@
+ + + *dbprefix*mimetypes + + + + + id + integer + 0 + true + 1 + 4 + + + + mimetype + text + + true + 64 + + + + mimetype_id_index + true + + mimetype + ascending + + + + + +
+ *dbprefix*filecache @@ -151,18 +187,18 @@ mimetype - text + integer true - 64 + 4 mimepart - text + integer true - 32 + 4 diff --git a/lib/files/cache/cache.php b/lib/files/cache/cache.php index 3ebae9baa5..0001c2752d 100644 --- a/lib/files/cache/cache.php +++ b/lib/files/cache/cache.php @@ -36,6 +36,9 @@ class Cache { */ private $numericId; + private $mimetypeIds = array(); + private $mimetypes = array(); + /** * @param \OC\Files\Storage\Storage|string $storage */ @@ -61,6 +64,41 @@ class Cache { return $this->numericId; } + /** + * normalize mimetypes + * + * @param string $mime + * @return int + */ + public function getMimetypeId($mime) { + if (!isset($this->mimetypeIds[$mime])) { + $query = \OC_DB::prepare('SELECT `id` FROM `*PREFIX*mimetypes` WHERE `mimetype` = ?'); + $result = $query->execute(array($mime)); + if ($row = $result->fetchRow()) { + $this->mimetypeIds[$mime] = $row['id']; + } else { + $query = \OC_DB::prepare('INSERT INTO `*PREFIX*mimetypes`(`mimetype`) VALUES(?)'); + $query->execute(array($mime)); + $this->mimetypeIds[$mime] = \OC_DB::insertid('*PREFIX*mimetypes'); + } + $this->mimetypes[$this->mimetypeIds[$mime]] = $mime; + } + return $this->mimetypeIds[$mime]; + } + + public function getMimetype($id) { + if (!isset($this->mimetypes[$id])) { + $query = \OC_DB::prepare('SELECT `mimetype` FROM `*PREFIX*mimetypes` WHERE `id` = ?'); + $result = $query->execute(array($id)); + if ($row = $result->fetchRow()) { + $this->mimetypes[$id] = $row['mimetype']; + } else { + return null; + } + } + return $this->mimetypes[$id]; + } + /** * get the stored metadata of a file or folder * @@ -92,6 +130,8 @@ class Cache { $data['size'] = (int)$data['size']; $data['mtime'] = (int)$data['mtime']; $data['encrypted'] = (bool)$data['encrypted']; + $data['mimetype'] = $this->getMimetype($data['mimetype']); + $data['mimepart'] = $this->getMimetype($data['mimepart']); } return $data; @@ -110,7 +150,12 @@ class Cache { 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted` FROM `*PREFIX*filecache` WHERE parent = ? ORDER BY `name` ASC'); $result = $query->execute(array($fileId)); - return $result->fetchAll(); + $files = $result->fetchAll(); + foreach ($files as &$file) { + $file['mimetype'] = $this->getMimetype($file['mimetype']); + $file['mimepart'] = $this->getMimetype($file['mimepart']); + } + return $files; } else { return array(); } @@ -179,22 +224,23 @@ class Cache { * @param array $data * @return array */ - static function buildParts(array $data) { + function buildParts(array $data) { $fields = array('path', 'parent', 'name', 'mimetype', 'size', 'mtime', 'encrypted'); $params = array(); $queryParts = array(); foreach ($data as $name => $value) { if (array_search($name, $fields) !== false) { - $params[] = $value; - $queryParts[] = '`' . $name . '`'; if ($name === 'path') { $params[] = md5($value); $queryParts[] = '`path_hash`'; } elseif ($name === 'mimetype') { - $params[] = substr($value, 0, strpos($value, '/')); + $params[] = $this->getMimetypeId(substr($value, 0, strpos($value, '/'))); $queryParts[] = '`mimepart`'; + $value = $this->getMimetypeId($value); } + $params[] = $value; + $queryParts[] = '`' . $name . '`'; } } return array($queryParts, $params); @@ -339,6 +385,8 @@ class Cache { $result = $query->execute(array($pattern, $this->numericId)); $files = array(); while ($row = $result->fetchRow()) { + $row['mimetype'] = $this->getMimetype($row['mimetype']); + $row['mimepart'] = $this->getMimetype($row['mimepart']); $files[] = $row; } return $files; @@ -360,6 +408,7 @@ class Cache { SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted` FROM `*PREFIX*filecache` WHERE ' . $where . ' AND `storage` = ?' ); + $mimetype = $this->getMimetypeId($mimetype); $result = $query->execute(array($mimetype, $this->numericId)); return $result->fetchAll(); } diff --git a/lib/files/view.php b/lib/files/view.php index 124345f3c6..8303a080bd 100644 --- a/lib/files/view.php +++ b/lib/files/view.php @@ -693,7 +693,7 @@ class View { $data = $cache->get($internalPath); - if ($data) { + if ($data and $data['fileid']) { if ($data['mimetype'] === 'httpd/unix-directory') { //add the sizes of other mountpoints to the folder $mountPoints = Filesystem::getMountPoints($path); diff --git a/lib/util.php b/lib/util.php index 805ef6f18b..93c0d0f26d 100755 --- a/lib/util.php +++ b/lib/util.php @@ -74,7 +74,7 @@ class OC_Util { */ public static function getVersion() { // hint: We only can count up. So the internal version number of ownCloud 4.5 will be 4.90.0. This is not visible to the user - return array(4,91,04); + return array(4,91,05); } /** From 5174eda23270463f09c82db77f2f61d3496f752f Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Mon, 7 Jan 2013 15:21:38 -0500 Subject: [PATCH 157/272] Fix permissions for mount point --- lib/files/view.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/files/view.php b/lib/files/view.php index 8303a080bd..94c89603ae 100644 --- a/lib/files/view.php +++ b/lib/files/view.php @@ -786,7 +786,7 @@ class View { $permissions = $subStorage->getPermissions($rootEntry['path']); $subPermissionsCache->set($rootEntry['fileid'], $user, $permissions); } - $rootEntry['permissions'] = $subPermissionsCache; + $rootEntry['permissions'] = $permissions; $files[] = $rootEntry; } } From a7d4d042239236082d7f2c2679249ea7d48e596c Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Mon, 7 Jan 2013 15:27:22 -0500 Subject: [PATCH 158/272] Fix mimetypes in shared cache --- apps/files_sharing/lib/cache.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/files_sharing/lib/cache.php b/apps/files_sharing/lib/cache.php index 0534d6dd89..8b989db3b0 100644 --- a/apps/files_sharing/lib/cache.php +++ b/apps/files_sharing/lib/cache.php @@ -79,6 +79,8 @@ class Shared_Cache extends Cache { $data['size'] = (int)$data['size']; $data['mtime'] = (int)$data['mtime']; $data['encrypted'] = (bool)$data['encrypted']; + $data['mimetype'] = $this->getMimetype($data['mimetype']); + $data['mimepart'] = $this->getMimetype($data['mimepart']); return $data; } return false; @@ -92,7 +94,12 @@ class Shared_Cache extends Cache { */ public function getFolderContents($folder) { if ($folder == '') { - return \OCP\Share::getItemsSharedWith('file', \OC_Share_Backend_File::FORMAT_GET_FOLDER_CONTENTS); + $files = \OCP\Share::getItemsSharedWith('file', \OC_Share_Backend_File::FORMAT_GET_FOLDER_CONTENTS); + foreach ($files as &$file) { + $file['mimetype'] = $this->getMimetype($file['mimetype']); + $file['mimepart'] = $this->getMimetype($file['mimepart']); + } + return $files; } else { if ($cache = $this->getSourceCache($folder)) { return $cache->getFolderContents($this->files[$folder]); From 8f8a5bbfb750b3c9091da810749a43cada2740b2 Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Mon, 7 Jan 2013 18:17:14 -0500 Subject: [PATCH 159/272] Maked Shared_Permissions extend Permissions class --- apps/files_sharing/lib/permissions.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/files_sharing/lib/permissions.php b/apps/files_sharing/lib/permissions.php index 6eaed34b33..508c3a384f 100644 --- a/apps/files_sharing/lib/permissions.php +++ b/apps/files_sharing/lib/permissions.php @@ -20,7 +20,7 @@ */ namespace OC\Files\Cache; -class Shared_Permissions { +class Shared_Permissions extends Permissions { /** * get the permissions for a single file @@ -29,7 +29,7 @@ class Shared_Permissions { * @param string $user * @return int (-1 if file no permissions set) */ - static public function get($fileId, $user) { + public function get($fileId, $user) { if ($fileId == -1) { return \OCP\PERMISSION_READ; } @@ -48,7 +48,7 @@ class Shared_Permissions { * @param string $user * @param int $permissions */ - static public function set($fileId, $user, $permissions) { + public function set($fileId, $user, $permissions) { // Not a valid action for Shared Permissions } @@ -59,7 +59,7 @@ class Shared_Permissions { * @param string $user * @return int[] */ - static public function getMultiple($fileIds, $user) { + public function getMultiple($fileIds, $user) { if (count($fileIds) === 0) { return array(); } @@ -75,11 +75,11 @@ class Shared_Permissions { * @param int $fileId * @param string $user */ - static public function remove($fileId, $user) { + public function remove($fileId, $user) { // Not a valid action for Shared Permissions } - static public function removeMultiple($fileIds, $user) { + public function removeMultiple($fileIds, $user) { // Not a valid action for Shared Permissions } } From e8b195bf109d702402735e628b2d239b199088e5 Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Mon, 7 Jan 2013 20:52:51 -0500 Subject: [PATCH 160/272] Almost fix Shared scanner... --- apps/files_sharing/lib/cache.php | 13 +++++++++++++ apps/files_sharing/lib/permissions.php | 2 +- apps/files_sharing/lib/share/file.php | 3 +++ apps/files_sharing/lib/share/folder.php | 11 +++++++++-- apps/files_sharing/lib/sharedstorage.php | 6 ------ lib/public/share.php | 5 ++++- 6 files changed, 30 insertions(+), 10 deletions(-) diff --git a/apps/files_sharing/lib/cache.php b/apps/files_sharing/lib/cache.php index 8b989db3b0..d35a5148de 100644 --- a/apps/files_sharing/lib/cache.php +++ b/apps/files_sharing/lib/cache.php @@ -136,6 +136,19 @@ class Shared_Cache extends Cache { return -1; } + /** + * check if a file is available in the cache + * + * @param string $file + * @return bool + */ + public function inCache($file) { + if ($file == '') { + return true; + } + return parent::inCache($file); + } + /** * remove a file or folder from the cache * diff --git a/apps/files_sharing/lib/permissions.php b/apps/files_sharing/lib/permissions.php index 508c3a384f..2b068ff935 100644 --- a/apps/files_sharing/lib/permissions.php +++ b/apps/files_sharing/lib/permissions.php @@ -33,7 +33,7 @@ class Shared_Permissions extends Permissions { if ($fileId == -1) { return \OCP\PERMISSION_READ; } - $source = \OCP\Share::getItemSharedWithBySource('file', $fileId, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE); + $source = \OCP\Share::getItemSharedWithBySource('file', $fileId, \OC_Share_Backend_File::FORMAT_SHARED_STORAGE, null, true); if ($source) { return $source['permissions']; } else { diff --git a/apps/files_sharing/lib/share/file.php b/apps/files_sharing/lib/share/file.php index 5e98c455d3..6d3c55a008 100644 --- a/apps/files_sharing/lib/share/file.php +++ b/apps/files_sharing/lib/share/file.php @@ -117,6 +117,9 @@ class OC_Share_Backend_File implements OCP\Share_Backend_File_Dependent { } public static function getSource($target) { + if ($target == '') { + return false; + } $target = '/'.$target; $target = rtrim($target, '/'); $pos = strpos($target, '/', 1); diff --git a/apps/files_sharing/lib/share/folder.php b/apps/files_sharing/lib/share/folder.php index bbe4c130bd..11c8c6b1e8 100644 --- a/apps/files_sharing/lib/share/folder.php +++ b/apps/files_sharing/lib/share/folder.php @@ -24,6 +24,13 @@ class OC_Share_Backend_Folder extends OC_Share_Backend_File implements OCP\Share public function getChildren($itemSource) { $children = array(); $parents = array($itemSource); + $query = \OC_DB::prepare('SELECT `id` FROM `*PREFIX*mimetypes` WHERE `mimetype` = ?'); + $result = $query->execute(array('httpd/unix-directory')); + if ($row = $result->fetchRow()) { + $mimetype = $row['id']; + } else { + $mimetype = -1; + } while (!empty($parents)) { $parents = "'".implode("','", $parents)."'"; $query = OC_DB::prepare('SELECT `fileid`, `name`, `mimetype` FROM `*PREFIX*filecache` WHERE `parent` IN ('.$parents.')'); @@ -32,8 +39,8 @@ class OC_Share_Backend_Folder extends OC_Share_Backend_File implements OCP\Share while ($file = $result->fetchRow()) { $children[] = array('source' => $file['fileid'], 'file_path' => $file['name']); // If a child folder is found look inside it - if ($file['mimetype'] == 'httpd/unix-directory') { - $parents[] = $file['id']; + if ($file['mimetype'] == $mimetype) { + $parents[] = $file['fileid']; } } } diff --git a/apps/files_sharing/lib/sharedstorage.php b/apps/files_sharing/lib/sharedstorage.php index 24096e0c10..c8756af8ed 100644 --- a/apps/files_sharing/lib/sharedstorage.php +++ b/apps/files_sharing/lib/sharedstorage.php @@ -408,12 +408,6 @@ class Shared extends \OC\Files\Storage\Common { } public function getScanner($path = '') { - if ($path != '' && ($source = $this->getSourcePath($path))) { - list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); - if ($storage) { - return $storage->getScanner($internalPath); - } - } return new \OC\Files\Cache\Scanner($this); } diff --git a/lib/public/share.php b/lib/public/share.php index c74960b94c..7722e0b86c 100644 --- a/lib/public/share.php +++ b/lib/public/share.php @@ -756,7 +756,7 @@ class Share { $collectionItems = array(); foreach ($items as &$row) { // Return only the item instead of a 2-dimensional array - if ($limit == 1 && $row['item_type'] == $itemType && $row[$column] == $item) { + if ($limit == 1 && $row[$column] == $item && ($row['item_type'] == $itemType || $itemType == 'file')) { if ($format == self::FORMAT_NONE) { return $row; } else { @@ -823,6 +823,9 @@ class Share { if (!empty($collectionItems)) { $items = array_merge($items, $collectionItems); } + if (empty($items) && $limit == 1) { + return false; + } if ($format == self::FORMAT_NONE) { return $items; } else if ($format == self::FORMAT_STATUSES) { From 464dafd7d2627d59732c09e01986d705d604ee56 Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Wed, 9 Jan 2013 22:17:39 -0500 Subject: [PATCH 161/272] Make Google Drive storage id unique, before it was anonymousanonymous --- apps/files_external/lib/google.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/files_external/lib/google.php b/apps/files_external/lib/google.php index bbb315c491..4c485773e7 100644 --- a/apps/files_external/lib/google.php +++ b/apps/files_external/lib/google.php @@ -41,7 +41,7 @@ class Google extends \OC\Files\Storage\Common { ) { $consumer_key = isset($params['consumer_key']) ? $params['consumer_key'] : 'anonymous'; $consumer_secret = isset($params['consumer_secret']) ? $params['consumer_secret'] : 'anonymous'; - $this->id = 'google::' . $consumer_key . $consumer_secret; + $this->id = 'google::' . $params['token']; $this->consumer = new \OAuthConsumer($consumer_key, $consumer_secret); $this->oauth_token = new \OAuthToken($params['token'], $params['token_secret']); $this->sig_method = new \OAuthSignatureMethod_HMAC_SHA1(); From 106541361c3857ed8e35c6869c91faffb8ae984d Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Wed, 9 Jan 2013 22:57:42 -0500 Subject: [PATCH 162/272] Change length of mimetypes to 255, the maximum length according to RFC 4288 --- db_structure.xml | 2 +- lib/util.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/db_structure.xml b/db_structure.xml index 3022983473..7b6829aa30 100644 --- a/db_structure.xml +++ b/db_structure.xml @@ -114,7 +114,7 @@ text true - 64 + 255 diff --git a/lib/util.php b/lib/util.php index 93c0d0f26d..e814a3a32d 100755 --- a/lib/util.php +++ b/lib/util.php @@ -74,7 +74,7 @@ class OC_Util { */ public static function getVersion() { // hint: We only can count up. So the internal version number of ownCloud 4.5 will be 4.90.0. This is not visible to the user - return array(4,91,05); + return array(4,91,06); } /** From aa15fcf22f4c32026eca5ff8ae5e5df244f2c53e Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Thu, 10 Jan 2013 12:09:55 -0500 Subject: [PATCH 163/272] Scan mount points in root before adding a entry --- lib/files/view.php | 46 ++++++++++++++++++++++++++++------------------ 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/lib/files/view.php b/lib/files/view.php index 94c89603ae..703cda5123 100644 --- a/lib/files/view.php +++ b/lib/files/view.php @@ -767,27 +767,37 @@ class View { $subStorage = Filesystem::getStorage($mountPoint); if ($subStorage) { $subCache = $subStorage->getCache(''); - $rootEntry = $subCache->get(''); - $relativePath = trim(substr($mountPoint, $dirLength), '/'); - if ($pos = strpos($relativePath, '/')) { //mountpoint inside subfolder add size to the correct folder - $entryName = substr($relativePath, 0, $pos); - foreach ($files as &$entry) { - if ($entry['name'] === $entryName) { - $entry['size'] += $rootEntry['size']; + if ($subCache->getStatus('') < Cache\Cache::COMPLETE) { + $subScanner = $subStorage->getScanner(''); + $subScanner->scan('', Cache\Scanner::SCAN_SHALLOW); + } else { + $subWatcher = $subStorage->getWatcher(''); + $subWatcher->checkUpdate(''); + } + + $rootEntry = $subCache->get(''); + if ($rootEntry) { + $relativePath = trim(substr($mountPoint, $dirLength), '/'); + if ($pos = strpos($relativePath, '/')) { //mountpoint inside subfolder add size to the correct folder + $entryName = substr($relativePath, 0, $pos); + foreach ($files as &$entry) { + if ($entry['name'] === $entryName) { + $entry['size'] += $rootEntry['size']; + } } + } else { //mountpoint in this folder, add an entry for it + $rootEntry['name'] = $relativePath; + $rootEntry['type'] = $rootEntry['mimetype'] === 'httpd/unix-directory' ? 'dir' : 'file'; + $subPermissionsCache = $subStorage->getPermissionsCache(''); + $permissions = $subPermissionsCache->get($rootEntry['fileid'], $user); + if ($permissions === -1) { + $permissions = $subStorage->getPermissions($rootEntry['path']); + $subPermissionsCache->set($rootEntry['fileid'], $user, $permissions); + } + $rootEntry['permissions'] = $permissions; + $files[] = $rootEntry; } - } else { //mountpoint in this folder, add an entry for it - $rootEntry['name'] = $relativePath; - $rootEntry['type'] = $rootEntry['mimetype'] === 'httpd/unix-directory' ? 'dir' : 'file'; - $subPermissionsCache = $subStorage->getPermissionsCache(''); - $permissions = $subPermissionsCache->get($rootEntry['fileid'], $user); - if ($permissions === -1) { - $permissions = $subStorage->getPermissions($rootEntry['path']); - $subPermissionsCache->set($rootEntry['fileid'], $user, $permissions); - } - $rootEntry['permissions'] = $permissions; - $files[] = $rootEntry; } } } From 4835525c469d5ac75104e92c2dfbbb049d62890c Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Thu, 10 Jan 2013 22:28:50 -0500 Subject: [PATCH 164/272] Switch scan to scanFile for root of mount points --- lib/files/view.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/files/view.php b/lib/files/view.php index 703cda5123..fa031b7478 100644 --- a/lib/files/view.php +++ b/lib/files/view.php @@ -768,9 +768,9 @@ class View { if ($subStorage) { $subCache = $subStorage->getCache(''); - if ($subCache->getStatus('') < Cache\Cache::COMPLETE) { + if ($subCache->getStatus('') === Cache\Cache::NOT_FOUND) { $subScanner = $subStorage->getScanner(''); - $subScanner->scan('', Cache\Scanner::SCAN_SHALLOW); + $subScanner->scanFile(''); } else { $subWatcher = $subStorage->getWatcher(''); $subWatcher->checkUpdate(''); From 36cac7f924ad07738a3cb72fee06f21d07baad42 Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Thu, 10 Jan 2013 22:29:47 -0500 Subject: [PATCH 165/272] Return NOT_FOUND in shared cache --- apps/files_sharing/lib/cache.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/files_sharing/lib/cache.php b/apps/files_sharing/lib/cache.php index d35a5148de..0b187a3c3f 100644 --- a/apps/files_sharing/lib/cache.php +++ b/apps/files_sharing/lib/cache.php @@ -196,7 +196,7 @@ class Shared_Cache extends Cache { if ($cache = $this->getSourceCache($file)) { return $cache->getStatus($this->files[$file]); } - return false; + return self::NOT_FOUND; } /** From 8a63bcc1e85499335ade1df459f0c9f467161404 Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Fri, 11 Jan 2013 20:56:36 -0500 Subject: [PATCH 166/272] Don't use more entropy for etags --- lib/files/storage/common.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/files/storage/common.php b/lib/files/storage/common.php index e859d447f3..591803f044 100644 --- a/lib/files/storage/common.php +++ b/lib/files/storage/common.php @@ -274,7 +274,7 @@ abstract class Common implements \OC\Files\Storage\Storage { $hash = call_user_func($ETagFunction, $path); return $hash; }else{ - return uniqid('', true); + return uniqid(); } } } From 9e2f3a53244e353cb75f9927e1c69ef40f589db7 Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Fri, 11 Jan 2013 20:59:53 -0500 Subject: [PATCH 167/272] Remove old create etag function --- lib/connector/sabre/file.php | 9 --------- 1 file changed, 9 deletions(-) diff --git a/lib/connector/sabre/file.php b/lib/connector/sabre/file.php index 1770b49128..e0723e1230 100644 --- a/lib/connector/sabre/file.php +++ b/lib/connector/sabre/file.php @@ -101,15 +101,6 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements Sabre_D return $this->getETagPropertyForPath($this->path); } - /** - * Creates a ETag for this path. - * @param string $path Path of the file - * @return string|null Returns null if the ETag can not effectively be determined - */ - static protected function createETag($path) { - return \OC\Files\Filesystem::hash('md5', $path); - } - /** * Returns the mime-type for a file * From a00b9e0a03a7cdb1103415d33d2c3f22e86edffb Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Fri, 11 Jan 2013 21:01:28 -0500 Subject: [PATCH 168/272] Bump version --- lib/util.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/util.php b/lib/util.php index e814a3a32d..faae962859 100755 --- a/lib/util.php +++ b/lib/util.php @@ -74,7 +74,7 @@ class OC_Util { */ public static function getVersion() { // hint: We only can count up. So the internal version number of ownCloud 4.5 will be 4.90.0. This is not visible to the user - return array(4,91,06); + return array(4,91,07); } /** From b30648cb7dc258208fad4dadb297aa1d792fa7ef Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Fri, 11 Jan 2013 21:09:01 -0500 Subject: [PATCH 169/272] Don't waste time making another call since we know it doesn't exist --- lib/connector/sabre/file.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/connector/sabre/file.php b/lib/connector/sabre/file.php index e0723e1230..1c18a39174 100644 --- a/lib/connector/sabre/file.php +++ b/lib/connector/sabre/file.php @@ -98,7 +98,7 @@ class OC_Connector_Sabre_File extends OC_Connector_Sabre_Node implements Sabre_D if (isset($properties[self::GETETAG_PROPERTYNAME])) { return $properties[self::GETETAG_PROPERTYNAME]; } - return $this->getETagPropertyForPath($this->path); + return null; } /** From 094a852bff378c03c873b6e8fd94587d2a2c784e Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Fri, 11 Jan 2013 21:09:58 -0500 Subject: [PATCH 170/272] Wrap the etag in double quotes --- lib/connector/sabre/node.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/connector/sabre/node.php b/lib/connector/sabre/node.php index dd8ae152ff..93d8fc477d 100644 --- a/lib/connector/sabre/node.php +++ b/lib/connector/sabre/node.php @@ -213,7 +213,7 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr static public function getETagPropertyForPath($path) { $data = \OC\Files\Filesystem::getFileInfo($path); if (isset($data['etag'])) { - return $data['etag']; + return '"'.$data['etag'].'"'; } return null; } From 94068e5d08cba776e410d925e26037d442b5bc62 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 3 Jan 2013 00:26:13 +0100 Subject: [PATCH 171/272] Cache: show upgrade progress --- apps/files/ajax/upgrade.php | 42 ++++++++ apps/files/appinfo/app.php | 10 -- apps/files/css/files.css | 9 ++ apps/files/index.php | 72 ++++++++------ apps/files/js/upgrade.js | 17 ++++ apps/files/templates/upgrade.php | 4 + lib/files/cache/legacy.php | 73 ++++++++++++++ lib/files/cache/upgrade.php | 161 ++++++++++++++++++++++--------- 8 files changed, 305 insertions(+), 83 deletions(-) create mode 100644 apps/files/ajax/upgrade.php create mode 100644 apps/files/js/upgrade.js create mode 100644 apps/files/templates/upgrade.php create mode 100644 lib/files/cache/legacy.php diff --git a/apps/files/ajax/upgrade.php b/apps/files/ajax/upgrade.php new file mode 100644 index 0000000000..965c0073b8 --- /dev/null +++ b/apps/files/ajax/upgrade.php @@ -0,0 +1,42 @@ +hasItems()) { + OC_Hook::connect('\OC\Files\Cache\Upgrade', 'migrate_path', $listener, 'upgradePath'); + + $upgrade = new \OC\Files\Cache\Upgrade($legacy); + $count = $legacy->getCount(); + $eventSource->send('total', $count); + $upgrade->upgradePath('/' . $user . '/files'); +} +\OC\Files\Cache\Upgrade::upgradeDone($user); +$eventSource->send('done', true); +$eventSource->close(); + +class UpgradeListener { + /** + * @var OC_EventSource $eventSource + */ + private $eventSource; + + private $count = 0; + private $lastSend = 0; + + public function __construct($eventSource) { + $this->eventSource = $eventSource; + } + + public function upgradePath($path) { + $this->count++; + if ($this->count > ($this->lastSend + 5)) { + $this->lastSend = $this->count; + $this->eventSource->send('count', $this->count); + } + } +} diff --git a/apps/files/appinfo/app.php b/apps/files/appinfo/app.php index 643d8ed18a..ab2f3b01a2 100644 --- a/apps/files/appinfo/app.php +++ b/apps/files/appinfo/app.php @@ -10,13 +10,3 @@ OCP\App::addNavigationEntry( array( "id" => "files_index", "name" => $l->t("Files") )); OC_Search::registerProvider('OC_Search_Provider_File'); - -if (OC_User::isLoggedIn()) { - // update OC4.5 filecache to OC5 filecache, can't do this in update.php since it needs to happen for each user individually - $cacheVersion = (int)OCP\Config::getUserValue(OC_User::getUser(), 'files', 'cache_version', 4); - if ($cacheVersion < 5) { - \OC_Log::write('files', 'updating filecache to 5.0 for user ' . OC_User::getUser(), \OC_Log::INFO); - \OC\Files\Cache\Upgrade::upgrade(); - OCP\Config::setUserValue(OC_User::getUser(), 'files', 'cache_version', 5); - } -} diff --git a/apps/files/css/files.css b/apps/files/css/files.css index 36a1e5c954..0c130efe47 100644 --- a/apps/files/css/files.css +++ b/apps/files/css/files.css @@ -122,3 +122,12 @@ a.action>img { max-height:16px; max-width:16px; vertical-align:text-bottom; } #scanning-message{ top:40%; left:40%; position:absolute; display:none; } div.crumb a{ padding:0.9em 0 0.7em 0; } + +#upgrade { + width: 400px; + position: absolute; + top: 200px; + left: 50%; + text-align: center; + margin-left: -200px; +} diff --git a/apps/files/index.php b/apps/files/index.php index 993d8b4dcf..0dce768696 100644 --- a/apps/files/index.php +++ b/apps/files/index.php @@ -28,7 +28,6 @@ OCP\User::checkLoggedIn(); OCP\Util::addStyle('files', 'files'); OCP\Util::addscript('files', 'jquery.iframe-transport'); OCP\Util::addscript('files', 'jquery.fileupload'); -OCP\Util::addscript('files', 'files'); OCP\Util::addscript('files', 'filelist'); OCP\Util::addscript('files', 'fileactions'); OCP\Util::addscript('files', 'keyboardshortcuts'); @@ -37,8 +36,8 @@ OCP\App::setActiveNavigationEntry('files_index'); // Load the files $dir = isset($_GET['dir']) ? stripslashes($_GET['dir']) : ''; // Redirect if directory does not exist -if(!\OC\Files\Filesystem::is_dir($dir . '/')) { - header('Location: '.$_SERVER['SCRIPT_NAME'].''); +if (!\OC\Files\Filesystem::is_dir($dir . '/')) { + header('Location: ' . $_SERVER['SCRIPT_NAME'] . ''); exit(); } @@ -53,16 +52,25 @@ function fileCmp($a, $b) { } $files = array(); -foreach( \OC\Files\Filesystem::getDirectoryContent( $dir ) as $i ) { - $i['date'] = OCP\Util::formatDate($i['mtime'] ); - if($i['type'] == 'file') { +$user = OC_User::getUser(); +if (\OC\Files\Cache\Upgrade::needUpgrade($user)) { //dont load anything if we need to upgrade the cache + $content = array(); + $needUpgrade = true; + $freeSpace = 0; +} else { + $content = \OC\Files\Filesystem::getDirectoryContent($dir); + $freeSpace = \OC\Files\Filesystem::free_space($dir); + $needUpgrade = false; +} +foreach ($content as $i) { + $i['date'] = OCP\Util::formatDate($i['mtime']); + if ($i['type'] == 'file') { $fileinfo = pathinfo($i['name']); $i['basename'] = $fileinfo['filename']; if (!empty($fileinfo['extension'])) { - $i['extension']='.' . $fileinfo['extension']; - } - else { - $i['extension']=''; + $i['extension'] = '.' . $fileinfo['extension']; + } else { + $i['extension'] = ''; } } $i['directory'] = $dir; @@ -74,10 +82,10 @@ usort($files, "fileCmp"); // Make breadcrumb $breadcrumb = array(); $pathtohere = ''; -foreach( explode( '/', $dir ) as $i ) { - if( $i != '' ) { +foreach (explode('/', $dir) as $i) { + if ($i != '') { $pathtohere .= '/' . $i; - $breadcrumb[] = array( 'dir' => $pathtohere, 'name' => $i ); + $breadcrumb[] = array('dir' => $pathtohere, 'name' => $i); } } @@ -94,29 +102,35 @@ $upload_max_filesize = OCP\Util::computerFileSize(ini_get('upload_max_filesize') $post_max_size = OCP\Util::computerFileSize(ini_get('post_max_size')); $maxUploadFilesize = min($upload_max_filesize, $post_max_size); -$freeSpace = \OC\Files\Filesystem::free_space($dir); -$freeSpace = max($freeSpace,0); +$freeSpace = max($freeSpace, 0); $maxUploadFilesize = min($maxUploadFilesize, $freeSpace); $permissions = OCP\PERMISSION_READ; if (\OC\Files\Filesystem::isUpdatable($dir . '/')) { - $permissions |= OCP\PERMISSION_UPDATE; + $permissions |= OCP\PERMISSION_UPDATE; } if (\OC\Files\Filesystem::isDeletable($dir . '/')) { - $permissions |= OCP\PERMISSION_DELETE; + $permissions |= OCP\PERMISSION_DELETE; } if (\OC\Files\Filesystem::isSharable($dir . '/')) { - $permissions |= OCP\PERMISSION_SHARE; + $permissions |= OCP\PERMISSION_SHARE; } -$tmpl = new OCP\Template( 'files', 'index', 'user' ); -$tmpl->assign( 'fileList', $list->fetchPage(), false ); -$tmpl->assign( 'breadcrumb', $breadcrumbNav->fetchPage(), false ); -$tmpl->assign( 'dir', \OC\Files\Filesystem::normalizePath($dir)); -$tmpl->assign( 'isCreatable', \OC\Files\Filesystem::isCreatable($dir . '/')); -$tmpl->assign('permissions', $permissions); -$tmpl->assign('files', $files); -$tmpl->assign('uploadMaxFilesize', $maxUploadFilesize); -$tmpl->assign('uploadMaxHumanFilesize', OCP\Util::humanFileSize($maxUploadFilesize)); -$tmpl->assign('allowZipDownload', intval(OCP\Config::getSystemValue('allowZipDownload', true))); -$tmpl->printPage(); +if ($needUpgrade) { + OCP\Util::addscript('files', 'upgrade'); + $tmpl = new OCP\Template('files', 'upgrade', 'user'); + $tmpl->printPage(); +} else { + OCP\Util::addscript('files', 'files'); + $tmpl = new OCP\Template('files', 'index', 'user'); + $tmpl->assign('fileList', $list->fetchPage(), false); + $tmpl->assign('breadcrumb', $breadcrumbNav->fetchPage(), false); + $tmpl->assign('dir', \OC\Files\Filesystem::normalizePath($dir)); + $tmpl->assign('isCreatable', \OC\Files\Filesystem::isCreatable($dir . '/')); + $tmpl->assign('permissions', $permissions); + $tmpl->assign('files', $files); + $tmpl->assign('uploadMaxFilesize', $maxUploadFilesize); + $tmpl->assign('uploadMaxHumanFilesize', OCP\Util::humanFileSize($maxUploadFilesize)); + $tmpl->assign('allowZipDownload', intval(OCP\Config::getSystemValue('allowZipDownload', true))); + $tmpl->printPage(); +} diff --git a/apps/files/js/upgrade.js b/apps/files/js/upgrade.js new file mode 100644 index 0000000000..02d57fc9e6 --- /dev/null +++ b/apps/files/js/upgrade.js @@ -0,0 +1,17 @@ +$(document).ready(function () { + var eventSource, total, bar = $('#progressbar'); + console.log('start'); + bar.progressbar({value: 0}); + eventSource = new OC.EventSource(OC.filePath('files', 'ajax', 'upgrade.php')); + eventSource.listen('total', function (count) { + total = count; + console.log(count + ' files needed to be migrated'); + }); + eventSource.listen('count', function (count) { + bar.progressbar({value: (count / total) * 100}); + console.log(count); + }); + eventSource.listen('done', function () { + document.location.reload(); + }); +}); diff --git a/apps/files/templates/upgrade.php b/apps/files/templates/upgrade.php new file mode 100644 index 0000000000..de6cc71302 --- /dev/null +++ b/apps/files/templates/upgrade.php @@ -0,0 +1,4 @@ +
+ t('Upgrading filesystem cache...');?> +
+
diff --git a/lib/files/cache/legacy.php b/lib/files/cache/legacy.php new file mode 100644 index 0000000000..ee10a1c135 --- /dev/null +++ b/lib/files/cache/legacy.php @@ -0,0 +1,73 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Files\Cache; + +/** + * Provide read only support for the old filecache + */ +class Legacy { + private $user; + + public function __construct($user) { + $this->user = $user; + } + + function getCount() { + $query = \OC_DB::prepare('SELECT COUNT(`id`) AS `count` FROM `*PREFIX*fscache` WHERE `user` = ?'); + $result = $query->execute(array($this->user)); + if ($row = $result->fetchRow()) { + return $row['count']; + } else { + return 0; + } + } + + /** + * check if a legacy cache is present and holds items + * + * @return bool + */ + function hasItems() { + try { + $query = \OC_DB::prepare('SELECT `id` FROM `*PREFIX*fscache` WHERE `user` = ? LIMIT 1'); + } catch (\Exception $e) { + return false; + } + try { + $result = $query->execute(array($this->user)); + } catch (\Exception $e) { + return false; + } + return (bool)$result->fetchRow(); + } + + /** + * @param string|int $path + * @return array + */ + function get($path) { + if (is_numeric($path)) { + $query = \OC_DB::prepare('SELECT * FROM `*PREFIX*fscache` WHERE `id` = ?'); + } else { + $query = \OC_DB::prepare('SELECT * FROM `*PREFIX*fscache` WHERE `path` = ?'); + } + $result = $query->execute(array($path)); + return $result->fetchRow(); + } + + /** + * @param int $id + * @return array + */ + function getChildren($id) { + $query = \OC_DB::prepare('SELECT * FROM `*PREFIX*fscache` WHERE `parent` = ?'); + $result = $query->execute(array($id)); + return $result->fetchAll(); + } +} diff --git a/lib/files/cache/upgrade.php b/lib/files/cache/upgrade.php index 77db4c2339..1032e0a844 100644 --- a/lib/files/cache/upgrade.php +++ b/lib/files/cache/upgrade.php @@ -9,62 +9,102 @@ namespace OC\Files\Cache; class Upgrade { - static $permissionsCaches = array(); + /** + * @var Legacy $legacy + */ + private $legacy; - static $numericIds = array(); + private $permissionsCaches = array(); - static function upgrade() { - $insertQuery = \OC_DB::prepare('INSERT INTO `*PREFIX*filecache`( `fileid`, `storage`, `path`, `path_hash`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted` ) - VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'); + private $numericIds = array(); - try { - $oldEntriesQuery = \OC_DB::prepare('SELECT * FROM `*PREFIX*fscache` ORDER BY `id` ASC'); //sort ascending to ensure the parent gets inserted before a child - } catch (\Exception $e) { - return; - } - try { - $oldEntriesResult = $oldEntriesQuery->execute(); - } catch (\Exception $e) { - return; - } - if (!$oldEntriesResult) { + private $mimeTypeIds = array(); + + /** + * @param Legacy $legacy + */ + public function __construct($legacy) { + $this->legacy = $legacy; + } + + /** + * Preform a shallow upgrade + * + * @param string $path + * @param int $mode + */ + function upgradePath($path, $mode = Scanner::SCAN_RECURSIVE) { + if (!$this->legacy->hasItems()) { return; } + \OC_Hook::emit('\OC\Files\Cache\Upgrade', 'migrate_path', $path); - $checkExistingQuery = \OC_DB::prepare('SELECT `fileid` FROM `*PREFIX*filecache` WHERE `fileid` = ?'); + if ($row = $this->legacy->get($path)) { + $data = $this->getNewData($row); + $this->insert($data); - while ($row = $oldEntriesResult->fetchRow()) { - if ($checkExistingQuery->execute(array($row['id']))->fetchRow()) { - continue; + $children = $this->legacy->getChildren($data['id']); + foreach ($children as $child) { + if ($mode == Scanner::SCAN_SHALLOW) { + $childData = $this->getNewData($child); + \OC_Hook::emit('\OC\Files\Cache\Upgrade', 'migrate_path', $child['path']); + $this->insert($childData); + } else { + $this->upgradePath($child['path']); + } } - - list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($row['path']); - /** - * @var \OC\Files\Storage\Storage $storage - * @var string $internalPath; - */ - $pathHash = md5($internalPath); - $storageId = self::getNumericId($storage); - $parentId = ($internalPath === '') ? -1 : $row['parent']; - - $insertQuery->execute(array($row['id'], $storageId, $internalPath, $pathHash, $parentId, $row['name'], $row['mimetype'], $row['mimepart'], $row['size'], $row['mtime'], $row['encrypted'])); - - $permissions = ($row['writable']) ? \OCP\PERMISSION_ALL : \OCP\PERMISSION_READ; - $permissionsCache = self::getPermissionsCache($storage); - $permissionsCache->set($row['id'], $row['user'], $permissions); } } + /** + * @param array $data the data for the new cache + */ + function insert($data) { + $insertQuery = \OC_DB::prepare('INSERT INTO `*PREFIX*filecache` + ( `fileid`, `storage`, `path`, `path_hash`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted` ) + VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)'); + + $insertQuery->execute(array($data['id'], $data['storage'], $data['path'], $data['path_hash'], $data['parent'], $data['name'], + $data['mimetype'], $data['mimepart'], $data['size'], $data['mtime'], $data['encrypted'])); + + $permissionsCache = $this->getPermissionsCache($data['storage_object']); + $permissionsCache->set($data['id'], $data['user'], $data['permissions']); + } + + /** + * get the new data array from the old one + * + * @param array $data the data from the old cache + * @return array + */ + function getNewData($data) { + $newData = $data; + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($data['path']); + /** + * @var \OC\Files\Storage\Storage $storage + * @var string $internalPath; + */ + $newData['path_hash'] = md5($internalPath); + $newData['path'] = $internalPath; + $newData['storage'] = $this->getNumericId($storage); + $newData['parent'] = ($internalPath === '') ? -1 : $data['parent']; + $newData['permissions'] = ($data['writable']) ? \OCP\PERMISSION_ALL : \OCP\PERMISSION_READ; + $newData['storage_object'] = $storage; + $newData['mimetype'] = $this->getMimetypeId($newData['mimetype'], $storage); + $newData['mimepart'] = $this->getMimetypeId($newData['mimepart'], $storage); + return $newData; + } + /** * @param \OC\Files\Storage\Storage $storage * @return Permissions */ - static function getPermissionsCache($storage) { + function getPermissionsCache($storage) { $storageId = $storage->getId(); - if (!isset(self::$permissionsCaches[$storageId])) { - self::$permissionsCaches[$storageId] = $storage->getPermissionsCache(); + if (!isset($this->permissionsCaches[$storageId])) { + $this->permissionsCaches[$storageId] = $storage->getPermissionsCache(); } - return self::$permissionsCaches[$storageId]; + return $this->permissionsCaches[$storageId]; } /** @@ -73,12 +113,45 @@ class Upgrade { * @param \OC\Files\Storage\Storage $storage * @return int */ - static function getNumericId($storage) { + function getNumericId($storage) { $storageId = $storage->getId(); - if (!isset(self::$numericIds[$storageId])) { - $cache = new Cache($storage); - self::$numericIds[$storageId] = $cache->getNumericStorageId(); + if (!isset($this->numericIds[$storageId])) { + $cache = $storage->getCache(); + $this->numericIds[$storageId] = $cache->getNumericStorageId(); } - return self::$numericIds[$storageId]; + return $this->numericIds[$storageId]; + } + + /** + * @param string $mimetype + * @param \OC\Files\Storage\Storage $storage + * @return int + */ + function getMimetypeId($mimetype, $storage) { + if (!isset($this->mimeTypeIds[$mimetype])) { + $cache = new Cache($storage); + $this->mimeTypeIds[$mimetype] = $cache->getMimetypeId($mimetype); + } + return $this->mimeTypeIds[$mimetype]; + } + + /** + * check if a cache upgrade is required for $user + * + * @param string $user + * @return bool + */ + static function needUpgrade($user) { + $cacheVersion = (int)\OCP\Config::getUserValue($user, 'files', 'cache_version', 4); + return $cacheVersion < 5; + } + + /** + * mark the filecache as upgrade + * + * @param string $user + */ + static function upgradeDone($user) { + \OCP\Config::setUserValue($user, 'files', 'cache_version', 5); } } From 7debfac0dc8f602168d8db480cfd4757b4d612b0 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 15 Jan 2013 19:11:12 +0100 Subject: [PATCH 172/272] Cache: more efficient upgrading --- apps/files/ajax/upgrade.php | 2 ++ lib/files/cache/legacy.php | 10 +++++++++- lib/files/cache/upgrade.php | 24 +++++++++++++++--------- 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/apps/files/ajax/upgrade.php b/apps/files/ajax/upgrade.php index 965c0073b8..7237b02c0b 100644 --- a/apps/files/ajax/upgrade.php +++ b/apps/files/ajax/upgrade.php @@ -10,10 +10,12 @@ $legacy = new \OC\Files\Cache\Legacy($user); if ($legacy->hasItems()) { OC_Hook::connect('\OC\Files\Cache\Upgrade', 'migrate_path', $listener, 'upgradePath'); + OC_DB::beginTransaction(); $upgrade = new \OC\Files\Cache\Upgrade($legacy); $count = $legacy->getCount(); $eventSource->send('total', $count); $upgrade->upgradePath('/' . $user . '/files'); + OC_DB::commit(); } \OC\Files\Cache\Upgrade::upgradeDone($user); $eventSource->send('done', true); diff --git a/lib/files/cache/legacy.php b/lib/files/cache/legacy.php index ee10a1c135..33d4b8e7c9 100644 --- a/lib/files/cache/legacy.php +++ b/lib/files/cache/legacy.php @@ -14,6 +14,8 @@ namespace OC\Files\Cache; class Legacy { private $user; + private $cacheHasItems = null; + public function __construct($user) { $this->user = $user; } @@ -34,17 +36,23 @@ class Legacy { * @return bool */ function hasItems() { + if (!is_null($this->cacheHasItems)) { + return $this->cacheHasItems; + } try { $query = \OC_DB::prepare('SELECT `id` FROM `*PREFIX*fscache` WHERE `user` = ? LIMIT 1'); } catch (\Exception $e) { + $this->cacheHasItems = false; return false; } try { $result = $query->execute(array($this->user)); } catch (\Exception $e) { + $this->cacheHasItems = false; return false; } - return (bool)$result->fetchRow(); + $this->cacheHasItems = (bool)$result->fetchRow(); + return $this->cacheHasItems; } /** diff --git a/lib/files/cache/upgrade.php b/lib/files/cache/upgrade.php index 1032e0a844..cd9a9e91a8 100644 --- a/lib/files/cache/upgrade.php +++ b/lib/files/cache/upgrade.php @@ -43,15 +43,21 @@ class Upgrade { $data = $this->getNewData($row); $this->insert($data); - $children = $this->legacy->getChildren($data['id']); - foreach ($children as $child) { - if ($mode == Scanner::SCAN_SHALLOW) { - $childData = $this->getNewData($child); - \OC_Hook::emit('\OC\Files\Cache\Upgrade', 'migrate_path', $child['path']); - $this->insert($childData); - } else { - $this->upgradePath($child['path']); - } + $this->upgradeChilds($data['id'], $mode); + } + } + + /** + * @param int $id + */ + function upgradeChilds($id, $mode = Scanner::SCAN_RECURSIVE) { + $children = $this->legacy->getChildren($id); + foreach ($children as $child) { + $childData = $this->getNewData($child); + \OC_Hook::emit('\OC\Files\Cache\Upgrade', 'migrate_path', $child['path']); + $this->insert($childData); + if ($mode == Scanner::SCAN_RECURSIVE) { + $this->upgradeChilds($child['id']); } } } From 73d45e79a7066b56581072c732f38ca375a4fc29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Tue, 15 Jan 2013 20:35:15 +0100 Subject: [PATCH 173/272] add trash button to web interface --- apps/files/css/files.css | 2 ++ apps/files/index.php | 1 + apps/files/js/files.js | 5 +++++ apps/files/templates/index.php | 6 +++++- 4 files changed, 13 insertions(+), 1 deletion(-) diff --git a/apps/files/css/files.css b/apps/files/css/files.css index 36a1e5c954..6355a8cde1 100644 --- a/apps/files/css/files.css +++ b/apps/files/css/files.css @@ -23,6 +23,8 @@ #new>ul>li>p { cursor:pointer; } #new>ul>li>form>input { padding:0.3em; margin:-0.3em; } +#trash { height:17px; margin:0 0 0 1em; z-index:1010; position:absolute; right:13.5em; } + #upload { height:27px; padding:0; margin-left:0.2em; overflow:hidden; } diff --git a/apps/files/index.php b/apps/files/index.php index b64bde44cc..24f58bbd07 100644 --- a/apps/files/index.php +++ b/apps/files/index.php @@ -105,6 +105,7 @@ $tmpl->assign('dir', OC_Filesystem::normalizePath($dir)); $tmpl->assign('isCreatable', OC_Filesystem::isCreatable($dir . '/')); $tmpl->assign('permissions', $permissions); $tmpl->assign('files', $files); +$tmpl->assign('trash', \OCP\App::isEnabled('files_trashbin')); $tmpl->assign('uploadMaxFilesize', $maxUploadFilesize); $tmpl->assign('uploadMaxHumanFilesize', OCP\Util::humanFileSize($maxUploadFilesize)); $tmpl->assign('allowZipDownload', intval(OCP\Config::getSystemValue('allowZipDownload', true))); diff --git a/apps/files/js/files.js b/apps/files/js/files.js index bb298431e8..c13d7a5961 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -82,6 +82,11 @@ $(document).ready(function() { $(this).parent().children('#file_upload_start').trigger('click'); return false; }); + + // Show Trash bin + $('#trash a').live('click', function() { + console.log("hello"); + }); var lastChecked; diff --git a/apps/files/templates/index.php b/apps/files/templates/index.php index 2e0772443f..f6b4c29d5a 100644 --- a/apps/files/templates/index.php +++ b/apps/files/templates/index.php @@ -35,6 +35,11 @@
+ + +
-
From c494eb79ab049d71113c786375b1ee11338e5edc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Wed, 16 Jan 2013 16:01:11 +0100 Subject: [PATCH 174/272] listen to post delete event to allow the trash bin to create a copy of the version first --- apps/files_versions/appinfo/app.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/files_versions/appinfo/app.php b/apps/files_versions/appinfo/app.php index afc0a67edb..edd0a2f702 100644 --- a/apps/files_versions/appinfo/app.php +++ b/apps/files_versions/appinfo/app.php @@ -12,5 +12,5 @@ OCP\Util::addscript('files_versions', 'versions'); // Listen to write signals OCP\Util::connectHook('OC_Filesystem', 'write', "OCA_Versions\Hooks", "write_hook"); // Listen to delete and rename signals -OCP\Util::connectHook('OC_Filesystem', 'delete', "OCA_Versions\Hooks", "remove_hook"); +OCP\Util::connectHook('OC_Filesystem', 'post-delete', "OCA_Versions\Hooks", "remove_hook"); OCP\Util::connectHook('OC_Filesystem', 'rename', "OCA_Versions\Hooks", "rename_hook"); \ No newline at end of file From 29ec00797948d98ffa5dcb0baa1518630ab3ef56 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 16 Jan 2013 19:11:33 +0100 Subject: [PATCH 175/272] Cache: dont migrate permissions, cache them on demain instead --- lib/files/cache/upgrade.php | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/lib/files/cache/upgrade.php b/lib/files/cache/upgrade.php index cd9a9e91a8..19e3d9ad57 100644 --- a/lib/files/cache/upgrade.php +++ b/lib/files/cache/upgrade.php @@ -14,8 +14,6 @@ class Upgrade { */ private $legacy; - private $permissionsCaches = array(); - private $numericIds = array(); private $mimeTypeIds = array(); @@ -72,9 +70,6 @@ class Upgrade { $insertQuery->execute(array($data['id'], $data['storage'], $data['path'], $data['path_hash'], $data['parent'], $data['name'], $data['mimetype'], $data['mimepart'], $data['size'], $data['mtime'], $data['encrypted'])); - - $permissionsCache = $this->getPermissionsCache($data['storage_object']); - $permissionsCache->set($data['id'], $data['user'], $data['permissions']); } /** @@ -101,18 +96,6 @@ class Upgrade { return $newData; } - /** - * @param \OC\Files\Storage\Storage $storage - * @return Permissions - */ - function getPermissionsCache($storage) { - $storageId = $storage->getId(); - if (!isset($this->permissionsCaches[$storageId])) { - $this->permissionsCaches[$storageId] = $storage->getPermissionsCache(); - } - return $this->permissionsCaches[$storageId]; - } - /** * get the numeric storage id * From f9c42a196f03bb193b07a8f5f8ecf42b911ef4b5 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 16 Jan 2013 21:36:04 +0100 Subject: [PATCH 176/272] Cache: no longer using this file --- lib/files/file.php | 61 ---------------------------------------------- 1 file changed, 61 deletions(-) delete mode 100644 lib/files/file.php diff --git a/lib/files/file.php b/lib/files/file.php deleted file mode 100644 index 0d33cea7ee..0000000000 --- a/lib/files/file.php +++ /dev/null @@ -1,61 +0,0 @@ - - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. - */ - -namespace OC\Files; - -/** - * representation of the location a file or folder is stored - */ - -class File{ - /** - * @var Storage\Storage $storage - */ - private $storage; - /** - * @var string internalPath - */ - private $internalPath; - - public function __construct(Storage\Storage $storage, $internalPath){ - $this->storage = $storage; - $this->internalPath = $internalPath; - } - - public static function resolve($fullPath){ - $storage = null; - $internalPath = ''; - list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($fullPath); - return new File($storage, $internalPath); - } - - /** - * get the internal path of the file inside the filestorage - * @return string - */ - public function getInternalPath(){ - return $this->internalPath; - } - - /** - * get the storage the file is stored in - * @return \OC\Files\Storage\Storage - */ - public function getStorage(){ - return $this->storage; - } - - /** - * get the id of the storage the file is stored in - * @return string - */ - public function getStorageId(){ - return $this->storage->getId(); - } - -} From bb43cf378b5e7c6a18a30d8fbb226b72f1e4eb88 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 16 Jan 2013 21:36:25 +0100 Subject: [PATCH 177/272] Files: make sure keybinds js is loaded after files js --- apps/files/index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/files/index.php b/apps/files/index.php index 0dce768696..854218eee5 100644 --- a/apps/files/index.php +++ b/apps/files/index.php @@ -30,7 +30,6 @@ OCP\Util::addscript('files', 'jquery.iframe-transport'); OCP\Util::addscript('files', 'jquery.fileupload'); OCP\Util::addscript('files', 'filelist'); OCP\Util::addscript('files', 'fileactions'); -OCP\Util::addscript('files', 'keyboardshortcuts'); OCP\App::setActiveNavigationEntry('files_index'); // Load the files @@ -122,6 +121,7 @@ if ($needUpgrade) { $tmpl->printPage(); } else { OCP\Util::addscript('files', 'files'); + OCP\Util::addscript('files', 'keyboardshortcuts'); $tmpl = new OCP\Template('files', 'index', 'user'); $tmpl->assign('fileList', $list->fetchPage(), false); $tmpl->assign('breadcrumb', $breadcrumbNav->fetchPage(), false); From 6871a150bd1309af0ca22e45487043d9640bb356 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 16 Jan 2013 21:58:17 +0100 Subject: [PATCH 178/272] Cache: use a database transition for scanning each folder gives a massive speed improvement while scanning files --- lib/files/cache/scanner.php | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/lib/files/cache/scanner.php b/lib/files/cache/scanner.php index b62a093cec..bf0ef01d6b 100644 --- a/lib/files/cache/scanner.php +++ b/lib/files/cache/scanner.php @@ -83,14 +83,19 @@ class Scanner { * * @param string $path * @param SCAN_RECURSIVE/SCAN_SHALLOW $recursive + * @param bool $onlyChilds * @return int the size of the scanned folder or -1 if the size is unknown at this stage */ - public function scan($path, $recursive = self::SCAN_RECURSIVE) { + public function scan($path, $recursive = self::SCAN_RECURSIVE, $onlyChilds = false) { \OC_Hook::emit('\OC\Files\Cache\Scanner', 'scan_folder', array('path' => $path, 'storage' => $this->storageId)); - $this->scanFile($path); + $childQueue = array(); + if (!$onlyChilds) { + $this->scanFile($path); + } $size = 0; if ($dh = $this->storage->opendir($path)) { + \OC_DB::beginTransaction(); while ($file = readdir($dh)) { if ($file !== '.' and $file !== '..') { $child = ($path) ? $path . '/' . $file : $file; @@ -98,10 +103,12 @@ class Scanner { if ($data) { if ($data['mimetype'] === 'httpd/unix-directory') { if ($recursive === self::SCAN_RECURSIVE) { - $data['size'] = $this->scan($child, self::SCAN_RECURSIVE); + $childQueue[] = $child; + $data['size'] = 0; } else { $data['size'] = -1; } + } else { } if ($data['size'] === -1) { $size = -1; @@ -111,6 +118,15 @@ class Scanner { } } } + \OC_DB::commit(); + foreach ($childQueue as $child) { + $childSize = $this->scan($child, self::SCAN_RECURSIVE, true); + if ($childSize === -1) { + $size = -1; + } else { + $size += $childSize; + } + } if ($size !== -1) { $this->cache->put($path, array('size' => $size)); } From d5ee4352539f2165eebde5dc983113849b6a1a0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Thu, 17 Jan 2013 13:17:48 +0100 Subject: [PATCH 179/272] rename "publicListView" option to "disableSharing", this is more meaningful also because it is not only useful for the public list view --- apps/files/templates/part.list.php | 6 +++--- apps/files_sharing/js/share.js | 2 +- apps/files_sharing/public.php | 2 +- apps/files_sharing/templates/public.php | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/files/templates/part.list.php b/apps/files/templates/part.list.php index dfac43d1b1..1970a80e1b 100644 --- a/apps/files/templates/part.list.php +++ b/apps/files/templates/part.list.php @@ -1,8 +1,8 @@ diff --git a/apps/files_sharing/js/share.js b/apps/files_sharing/js/share.js index 8a546d6216..59faf82e26 100644 --- a/apps/files_sharing/js/share.js +++ b/apps/files_sharing/js/share.js @@ -1,6 +1,6 @@ $(document).ready(function() { - if (typeof OC.Share !== 'undefined' && typeof FileActions !== 'undefined' && !publicListView) { + if (typeof OC.Share !== 'undefined' && typeof FileActions !== 'undefined' && !disableSharing) { FileActions.register('all', 'Share', OC.PERMISSION_READ, OC.imagePath('core', 'actions/share'), function(filename) { if ($('#dir').val() == '/') { diff --git a/apps/files_sharing/public.php b/apps/files_sharing/public.php index 487b9e7996..7e1959cd95 100644 --- a/apps/files_sharing/public.php +++ b/apps/files_sharing/public.php @@ -257,7 +257,7 @@ if ($linkItem) { $list = new OCP\Template('files', 'part.list', ''); $list->assign('files', $files, false); - $list->assign('publicListView', true); + $list->assign('disableSharing', true); $list->assign('baseURL', OCP\Util::linkToPublic('files').$urlLinkIdentifiers.'&path=', false); $list->assign('downloadURL', OCP\Util::linkToPublic('files').$urlLinkIdentifiers.'&download&path=', false); $breadcrumbNav = new OCP\Template('files', 'part.breadcrumb', '' ); diff --git a/apps/files_sharing/templates/public.php b/apps/files_sharing/templates/public.php index 647e1e08a3..bfcc521e10 100644 --- a/apps/files_sharing/templates/public.php +++ b/apps/files_sharing/templates/public.php @@ -1,8 +1,8 @@ From 82d4da0d3d0dc09c22d2cd2220d418372206099e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Fri, 18 Jan 2013 10:23:31 +0100 Subject: [PATCH 180/272] call the trash bin view --- apps/files/js/files.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/files/js/files.js b/apps/files/js/files.js index c13d7a5961..359b92a6fd 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -83,9 +83,9 @@ $(document).ready(function() { return false; }); - // Show Trash bin + // Show trash bin $('#trash a').live('click', function() { - console.log("hello"); + window.location=OC.filePath('files_trashbin', '', 'index.php'); }); var lastChecked; From 1318450791146e7a075c01d8a39df6d333b619f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Fri, 18 Jan 2013 10:51:13 +0100 Subject: [PATCH 181/272] introduce option to disable download action --- apps/files/js/fileactions.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/apps/files/js/fileactions.js b/apps/files/js/fileactions.js index 80b9c01f83..093b6204c3 100644 --- a/apps/files/js/fileactions.js +++ b/apps/files/js/fileactions.js @@ -147,15 +147,19 @@ $(document).ready(function () { } else { var downloadScope = 'file'; } - FileActions.register(downloadScope, 'Download', OC.PERMISSION_READ, function () { - return OC.imagePath('core', 'actions/download'); - }, function (filename) { - window.location = OC.filePath('files', 'ajax', 'download.php') + '?files=' + encodeURIComponent(filename) + '&dir=' + encodeURIComponent($('#dir').val()); - }); - + + if (typeof disableDownloadActions == 'undefined' || !disableDownloadActions) { + FileActions.register(downloadScope, 'Download', OC.PERMISSION_READ, function () { + return OC.imagePath('core', 'actions/download'); + }, function (filename) { + window.location = OC.filePath('files', 'ajax', 'download.php') + '?files=' + encodeURIComponent(filename) + '&dir=' + encodeURIComponent($('#dir').val()); + }); + } + $('#fileList tr').each(function(){ FileActions.display($(this).children('td.filename')); }); + }); FileActions.register('all', 'Delete', OC.PERMISSION_DELETE, function () { From d5151fa61c561544ee64472a2c778cbe4fe086f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Fri, 18 Jan 2013 13:11:29 +0100 Subject: [PATCH 182/272] first version of the trash bin app --- apps/files_trashbin/ajax/undelete.php | 13 ++ apps/files_trashbin/appinfo/app.php | 7 + apps/files_trashbin/appinfo/database.xml | 92 ++++++++++ apps/files_trashbin/appinfo/info.xml | 14 ++ apps/files_trashbin/appinfo/version | 1 + apps/files_trashbin/css/trash.css | 78 ++++++++ apps/files_trashbin/download.php | 52 ++++++ apps/files_trashbin/index.php | 55 ++++++ apps/files_trashbin/js/trash.js | 37 ++++ apps/files_trashbin/lib/hooks.php | 45 +++++ apps/files_trashbin/lib/trash.php | 190 ++++++++++++++++++++ apps/files_trashbin/templates/index.php | 36 ++++ apps/files_trashbin/templates/part.list.php | 70 ++++++++ 13 files changed, 690 insertions(+) create mode 100644 apps/files_trashbin/ajax/undelete.php create mode 100644 apps/files_trashbin/appinfo/app.php create mode 100644 apps/files_trashbin/appinfo/database.xml create mode 100644 apps/files_trashbin/appinfo/info.xml create mode 100644 apps/files_trashbin/appinfo/version create mode 100644 apps/files_trashbin/css/trash.css create mode 100644 apps/files_trashbin/download.php create mode 100644 apps/files_trashbin/index.php create mode 100644 apps/files_trashbin/js/trash.js create mode 100644 apps/files_trashbin/lib/hooks.php create mode 100644 apps/files_trashbin/lib/trash.php create mode 100644 apps/files_trashbin/templates/index.php create mode 100644 apps/files_trashbin/templates/part.list.php diff --git a/apps/files_trashbin/ajax/undelete.php b/apps/files_trashbin/ajax/undelete.php new file mode 100644 index 0000000000..c548034828 --- /dev/null +++ b/apps/files_trashbin/ajax/undelete.php @@ -0,0 +1,13 @@ + array('content'=>'foo', 'id' => 'bar'))); \ No newline at end of file diff --git a/apps/files_trashbin/appinfo/app.php b/apps/files_trashbin/appinfo/app.php new file mode 100644 index 0000000000..3741d42c78 --- /dev/null +++ b/apps/files_trashbin/appinfo/app.php @@ -0,0 +1,7 @@ + + + + *dbname* + true + false + + utf8 + +
+ + *dbprefix*files_trash + + + + + id + text + + true + 50 + + + + user + text + + true + 50 + + + + timestamp + text + + true + 12 + + + + location + text + + true + 200 + + + + type + text + + true + 4 + + + + mime + text + + true + 30 + + + + id_index + + id + ascending + + + + + timestamp_index + + timestamp + ascending + + + + + user_index + + user + ascending + + + + + +
+ + diff --git a/apps/files_trashbin/appinfo/info.xml b/apps/files_trashbin/appinfo/info.xml new file mode 100644 index 0000000000..9b48612636 --- /dev/null +++ b/apps/files_trashbin/appinfo/info.xml @@ -0,0 +1,14 @@ + + + files_trashbin + Trash + Trash bin + AGPL + Bjoern Schiessle + true + 4.9 + + + + + diff --git a/apps/files_trashbin/appinfo/version b/apps/files_trashbin/appinfo/version new file mode 100644 index 0000000000..49d59571fb --- /dev/null +++ b/apps/files_trashbin/appinfo/version @@ -0,0 +1 @@ +0.1 diff --git a/apps/files_trashbin/css/trash.css b/apps/files_trashbin/css/trash.css new file mode 100644 index 0000000000..bd6341c6fb --- /dev/null +++ b/apps/files_trashbin/css/trash.css @@ -0,0 +1,78 @@ +body { + background:#ddd; +} + +#header { + background:#1d2d44; + box-shadow:0 0 10px rgba(0,0,0,.5), inset 0 -2px 10px #222; + height:2.5em; + left:0; + line-height:2.5em; + position:fixed; + right:0; + top:0; + z-index:100; + padding:.5em; +} + +#details { + color:#fff; +} + +#header #download { + font-weight:700; + margin-left:2em; +} + +#header #download img { + padding-left:.1em; + padding-right:.3em; + vertical-align:text-bottom; +} + +#preview { + background:#eee; + border-bottom:1px solid #f8f8f8; + min-height:30em; + padding-top:2em; + text-align:center; + margin:50px auto; +} + +#noPreview { + display:none; + padding-top:5em; +} + +p.info { + color:#777; + text-align:center; + text-shadow:#fff 0 1px 0; + width:22em; + margin:2em auto; +} + +p.info a { + color:#777; + font-weight:700; +} + +#imgframe { + height:75%; + padding-bottom:2em; + width:80%; + margin:0 auto; +} + +#imgframe img { + max-height:100%; + max-width:100%; +} + +table td.filename .name { + display:block; + height:1.5em; + vertical-align:middle; + margin-left:3em; + /*font-weight:bold;*/ +} \ No newline at end of file diff --git a/apps/files_trashbin/download.php b/apps/files_trashbin/download.php new file mode 100644 index 0000000000..a987dd4fd1 --- /dev/null +++ b/apps/files_trashbin/download.php @@ -0,0 +1,52 @@ +. +* +*/ + +// Check if we are a user +OCP\User::checkLoggedIn(); + +$filename = $_GET["file"]; + +$view = new OC_FilesystemView('/'.\OCP\User::getUser().'/files_trashbin'); + +if(!$view->file_exists($filename)) { + error_log("file does not exist... " . $view->getInternalPath($filename)); + header("HTTP/1.0 404 Not Found"); + $tmpl = new OCP\Template( '', '404', 'guest' ); + $tmpl->assign('file', $filename); + $tmpl->printPage(); + exit; +} + +$ftype=$view->getMimeType( $filename ); + +header('Content-Type:'.$ftype);if ( preg_match( "/MSIE/", $_SERVER["HTTP_USER_AGENT"] ) ) { + header( 'Content-Disposition: attachment; filename="' . rawurlencode( basename($filename) ) . '"' ); +} else { + header( 'Content-Disposition: attachment; filename*=UTF-8\'\'' . rawurlencode( basename($filename) ) + . '; filename="' . rawurlencode( basename($filename) ) . '"' ); +} +OCP\Response::disableCaching(); +header('Content-Length: '. $view->filesize($filename)); + +OC_Util::obEnd(); +$view->readfile( $filename ); diff --git a/apps/files_trashbin/index.php b/apps/files_trashbin/index.php new file mode 100644 index 0000000000..28414cc1ce --- /dev/null +++ b/apps/files_trashbin/index.php @@ -0,0 +1,55 @@ +execute(array($user))->fetchAll(); + +$files = array(); +foreach ($result as $r) { + $i = array(); + $i['name'] = $r['id']; + $i['date'] = OCP\Util::formatDate($r['timestamp']); + $i['timestamp'] = $r['timestamp']; + $i['mimetype'] = $r['mime']; + $i['type'] = $r['type']; + if ($i['type'] == 'file') { + $fileinfo = pathinfo($r['id']); + $i['basename'] = $fileinfo['filename']; + $i['extension'] = isset($fileinfo['extension']) ? ('.'.$fileinfo['extension']) : ''; + } + $i['directory'] = $r['location']; + if ($i['directory'] == '/') { + $i['directory'] = ''; + } + $i['permissions'] = OCP\PERMISSION_READ; + $files[] = $i; +} + +$breadcrumbNav = new OCP\Template('files', 'part.breadcrumb', ''); +$breadcrumbNav->assign('breadcrumb', array(array('dir' => '', 'name' => 'Trash')), false); +$breadcrumbNav->assign('baseURL', OCP\Util::linkTo('files_trashbin', 'index.php') . '?dir=', false); + +$list = new OCP\Template('files_trashbin', 'part.list', ''); +$list->assign('files', $files, false); +$list->assign('baseURL', OCP\Util::linkTo('files_trashbin', 'index.php'). '?dir=', false); +$list->assign('downloadURL', OCP\Util::linkTo('files_trashbin', 'download.php') . '?file=', false); +$list->assign('disableSharing', true); +$list->assign('disableDownloadActions', true); +$tmpl->assign('breadcrumb', $breadcrumbNav->fetchPage(), false); +$tmpl->assign('fileList', $list->fetchPage(), false); +$tmpl->assign('dir', OC_Filesystem::normalizePath($view->getAbsolutePath())); + +$tmpl->printPage(); diff --git a/apps/files_trashbin/js/trash.js b/apps/files_trashbin/js/trash.js new file mode 100644 index 0000000000..37e9a4bf10 --- /dev/null +++ b/apps/files_trashbin/js/trash.js @@ -0,0 +1,37 @@ +// Override download path to files_sharing/public.php +function fileDownloadPath(dir, file) { + var url = $('#downloadURL').val(); + if (url.indexOf('&path=') != -1) { + url += '/'+file; + } + return url; +} + +$(document).ready(function() { + + if (typeof FileActions !== 'undefined') { + FileActions.register('all', 'Undelete', OC.PERMISSION_READ, '', function(filename) { + var tr=$('tr').filterAttr('data-file', filename); + console.log("tr: " + tr.attr('data-timestamp') + " name: " + name); + $.post(OC.filePath('files_trashbin','ajax','undelete.php'), + {timestamp:tr.attr('data-timestamp'),filename:tr.attr('data-filename')}, + function(result){ + if (result.status == 'success') { + return; + var date=new Date(); + FileList.addFile(name,0,date,false,hidden); + var tr=$('tr').filterAttr('data-file',name); + tr.data('mime','text/plain').data('id',result.data.id); + tr.attr('data-id', result.data.id); + getMimeIcon('text/plain',function(path){ + tr.find('td.filename').attr('style','background-image:url('+path+')'); + }); + } else { + OC.dialogs.alert(result.data.message, 'Error'); + } + }); + + }); + }; + +}); \ No newline at end of file diff --git a/apps/files_trashbin/lib/hooks.php b/apps/files_trashbin/lib/hooks.php new file mode 100644 index 0000000000..d3bee105b5 --- /dev/null +++ b/apps/files_trashbin/lib/hooks.php @@ -0,0 +1,45 @@ +. + * + */ + +/** + * This class contains all hooks. + */ + +namespace OCA_Trash; + +class Hooks { + + /** + * @brief Copy files to trash bin + * @param array + * + * This function is connected to the delete signal of OC_Filesystem + * to copy the file to the trash bin + */ + public static function remove_hook($params) { + + if ( \OCP\App::isEnabled('files_trashbin') ) { + $path = $params['path']; + Trashbin::move2trash($path); + } + } +} diff --git a/apps/files_trashbin/lib/trash.php b/apps/files_trashbin/lib/trash.php new file mode 100644 index 0000000000..384a865ce0 --- /dev/null +++ b/apps/files_trashbin/lib/trash.php @@ -0,0 +1,190 @@ +. + * + */ + +namespace OCA_Trash; + +class Trashbin { + + /** + * move file to the trash bin + * + * @param $file_path path to the deleted file/directory relative to the files root directory + */ + public static function move2trash($file_path) { + $user = \OCP\User::getUser(); + $view = new \OC_FilesystemView('/'. $user); + if (!$view->is_dir('files_trashbin')) { + $view->mkdir('files_trashbin'); + $view->mkdir("versions_trashbin"); + } + + $path_parts = pathinfo($file_path); + + $deleted = $path_parts['basename']; + $location = $path_parts['dirname']; + $timestamp = time(); + $mime = $view->getMimeType('files'.$file_path); + + if ( $view->is_dir('files'.$file_path) ) { + $type = 'dir'; + } else { + $type = 'file'; + } + + self::copy_recursive($file_path, 'files_trashbin/'.$deleted.'.d'.$timestamp, $view); + + $query = \OC_DB::prepare("INSERT INTO *PREFIX*files_trash (id,timestamp,location,type,mime,user) VALUES (?,?,?,?,?,?)"); + $result = $query->execute(array($deleted, $timestamp, $location, $type, $mime, $user)); + + if ( \OCP\App::isEnabled('files_versions') ) { + if ( $view->is_dir('files_versions'.$file_path) ) { + $view->rename('files_versions'.$file_path, 'versions_trashbin/'. $deleted.'.d'.$timestamp); + } else if ( $versions = \OCA_Versions\Storage::getVersions($file_path) ) { + foreach ($versions as $v) { + $view->rename('files_versions'.$v['path'].'.v'.$v['version'], 'versions_trashbin/'. $deleted.'.v'.$v['version'].'.d'.$timestamp); + } + } + } + + self::expire(); + } + + + /** + * restore files from trash bin + * @param $filename name of the file + * @param $timestamp time when the file was deleted + */ + public static function restore($filename, $timestamp) { + + $user = \OCP\User::getUser(); + $view = new \OC_FilesystemView('/'.$user); + + $query = \OC_DB::prepare('SELECT location,type FROM *PREFIX*files_trash WHERE user=? AND id=? AND timestamp=?'); + $result = $query->execute(array($user,$filename,$timestamp))->fetchAll(); + + if ( count($result) != 1 ) { + \OC_Log::write('files_trashbin', 'trash bin database inconsistent!', OC_Log::ERROR); + return false; + } + + $location = $result[0]['location']; + if ( $result[0]['location'] != '/' && !$view->is_dir('files'.$result[0]['location']) ) { + $location = '/'; + } + + $source = 'files_trashbin/'.$filename.'.d'.$timestamp; + $target = \OC_Filesystem::normalizePath('files/'.$location.'/'.$filename); + + $ext = self::getUniqueExtension($location, $filename, $view); + + $view->rename($source, $target.$ext); + + if ( \OCP\App::isEnabled('files_versions') ) { + if ( $result[0][type] == 'dir' ) { + $view->rename('versions_trashbin/'. $filename.'.d'.$timestamp, 'files_versions/'.$location.'/'.$filename.$ext); + } else if ( $versions = self::getVersionsFromTrash($filename, $timestamp) ) { + foreach ($versions as $v) { + $view->rename('versions_trashbin/'.$filename.'.v'.$v.'.d'.$timestamp, 'files_versions/'.$location.'/'.$filename.$ext.'.v'.$v); + } + } + } + + $query = \OC_DB::prepare('DELETE FROM *PREFIX*files_trash WHERE user=? AND id=? AND timestamp=?'); + $query->execute(array($user,$filename,$timestamp)); + + } + + /** + * clean up the trash bin + */ + private static function expire() { + //TODO: implement expire function + return true; + } + + /** + * recursive copy to copy a whole directory + * + * @param $source source path, relative to the users files directory + * @param $destination destination path relative to the users root directoy + * @param $view file view for the users root directory + * @param $location location of the source files, either "fscache" or "local" + */ + private static function copy_recursive( $source, $destination, $view, $location='fscache' ) { + if ( $view->is_dir( 'files'.$source ) ) { + $view->mkdir( $destination ); + foreach ( \OC_Files::getDirectoryContent($source) as $i ) { + $pathDir = $source.'/'.$i['name']; + if ( $view->is_dir('files'.$pathDir) ) { + self::copy_recursive($pathDir, $destination.'/'.$i['name'], $view); + } else { + $view->copy( 'files'.$pathDir, $destination . '/' . $i['name'] ); + } + } + } else { + $view->copy( 'files'.$source, $destination ); + } + } + + /** + * find all versions which belong to the file we want to restore + * @param $filename name of the file which should be restored + * @param $timestamp timestamp when the file was deleted + */ + private static function getVersionsFromTrash($filename, $timestamp) { + $view = new \OC_FilesystemView('/'.\OCP\User::getUser().'/versions_trashbin'); + $versionsName = \OCP\Config::getSystemValue('datadirectory').$view->getAbsolutePath($filename); + $versions = array(); + + // fetch for old versions + $matches = glob( $versionsName.'.v*.d'.$timestamp ); + + foreach( $matches as $ma ) { + $parts = explode( '.v', substr($ma, 0, -strlen($timestamp)-2) ); + $versions[] = ( end( $parts ) ); + } + return $versions; + } + + /** + * find unique extension for restored file if a file with the same name already exists + * @param $location where the file should be restored + * @param $filename name of the file + * @param $view filesystem view relative to users root directory + * @return string with unique extension + */ + private static function getUniqueExtension($location, $filename, $view) { + $ext = ''; + if ( $view->file_exists('files'.$location.'/'.$filename) ) { + $tmpext = '.restored'; + $ext = $tmpext; + $i = 1; + while ( $view->file_exists('files'.$location.'/'.$filename.$ext) ) { + $ext = $tmpext.$i; + $i++; + } + } + return $ext; + } + +} diff --git a/apps/files_trashbin/templates/index.php b/apps/files_trashbin/templates/index.php new file mode 100644 index 0000000000..a412379d53 --- /dev/null +++ b/apps/files_trashbin/templates/index.php @@ -0,0 +1,36 @@ + +
+ +
+
+
+ + +
t('Nothing in here. Upload something!')?>
+ + + + + + + + + + + + +
+ + t( 'Name' ); ?> + + + + Download" /> + t('Download')?> + + + + + t( 'Deleted' ); ?> +
diff --git a/apps/files_trashbin/templates/part.list.php b/apps/files_trashbin/templates/part.list.php new file mode 100644 index 0000000000..d022854330 --- /dev/null +++ b/apps/files_trashbin/templates/part.list.php @@ -0,0 +1,70 @@ + + +200) $relative_date_color = 200; + $name = str_replace('+', '%20', urlencode($file['name'])); + $name = str_replace('%2F', '/', $name); + $directory = str_replace('+', '%20', urlencode($file['directory'])); + $directory = str_replace('%2F', '/', $directory); ?> + ' + data-permissions=''> + + style="background-image:url()" + + style="background-image:url()" + + > + + + + + + + + + + + + + + + + + + + + + + + + + + Date: Fri, 18 Jan 2013 14:09:22 +0100 Subject: [PATCH 183/272] remove item in the trash bin view after successful undelete --- apps/files_trashbin/ajax/undelete.php | 9 +++--- apps/files_trashbin/js/trash.js | 12 ++----- apps/files_trashbin/lib/trash.php | 35 ++++++++++++--------- apps/files_trashbin/templates/part.list.php | 3 +- 4 files changed, 30 insertions(+), 29 deletions(-) diff --git a/apps/files_trashbin/ajax/undelete.php b/apps/files_trashbin/ajax/undelete.php index c548034828..f55629d695 100644 --- a/apps/files_trashbin/ajax/undelete.php +++ b/apps/files_trashbin/ajax/undelete.php @@ -7,7 +7,8 @@ if(!OC_User::isLoggedIn()) { $timestamp = isset( $_REQUEST['timestamp'] ) ? $_REQUEST['timestamp'] : ''; $filename = isset( $_REQUEST['filename'] ) ? trim($_REQUEST['filename'], '/\\') : ''; -OCA_Trash\Trashbin::restore($filename, $timestamp); - -//TODO: return useful data after succsessful restore operation and remove restored files from the list view -OCP\JSON::success(array("data" => array('content'=>'foo', 'id' => 'bar'))); \ No newline at end of file +if ( OCA_Trash\Trashbin::restore($filename, $timestamp) ) { + OCP\JSON::success(array("data" => array('filename'=>$filename, 'timestamp' => $timestamp))); +} else { + OCP\JSON::error(array("data" => array("message" => "Couldn't restore ".$filename))); +} diff --git a/apps/files_trashbin/js/trash.js b/apps/files_trashbin/js/trash.js index 37e9a4bf10..b9088944fd 100644 --- a/apps/files_trashbin/js/trash.js +++ b/apps/files_trashbin/js/trash.js @@ -12,20 +12,12 @@ $(document).ready(function() { if (typeof FileActions !== 'undefined') { FileActions.register('all', 'Undelete', OC.PERMISSION_READ, '', function(filename) { var tr=$('tr').filterAttr('data-file', filename); - console.log("tr: " + tr.attr('data-timestamp') + " name: " + name); $.post(OC.filePath('files_trashbin','ajax','undelete.php'), {timestamp:tr.attr('data-timestamp'),filename:tr.attr('data-filename')}, function(result){ if (result.status == 'success') { - return; - var date=new Date(); - FileList.addFile(name,0,date,false,hidden); - var tr=$('tr').filterAttr('data-file',name); - tr.data('mime','text/plain').data('id',result.data.id); - tr.attr('data-id', result.data.id); - getMimeIcon('text/plain',function(path){ - tr.find('td.filename').attr('style','background-image:url('+path+')'); - }); + var row = document.getElementById(result.data.filename+'.d'+result.data.timestamp); + row.parentNode.removeChild(row); } else { OC.dialogs.alert(result.data.message, 'Error'); } diff --git a/apps/files_trashbin/lib/trash.php b/apps/files_trashbin/lib/trash.php index 384a865ce0..322f5679b7 100644 --- a/apps/files_trashbin/lib/trash.php +++ b/apps/files_trashbin/lib/trash.php @@ -86,7 +86,8 @@ class Trashbin { \OC_Log::write('files_trashbin', 'trash bin database inconsistent!', OC_Log::ERROR); return false; } - + + // if location no longer exists, restore file in the root directory $location = $result[0]['location']; if ( $result[0]['location'] != '/' && !$view->is_dir('files'.$result[0]['location']) ) { $location = '/'; @@ -95,23 +96,29 @@ class Trashbin { $source = 'files_trashbin/'.$filename.'.d'.$timestamp; $target = \OC_Filesystem::normalizePath('files/'.$location.'/'.$filename); + // we need a extension in case a file/dir with the same name already exists $ext = self::getUniqueExtension($location, $filename, $view); - $view->rename($source, $target.$ext); - - if ( \OCP\App::isEnabled('files_versions') ) { - if ( $result[0][type] == 'dir' ) { - $view->rename('versions_trashbin/'. $filename.'.d'.$timestamp, 'files_versions/'.$location.'/'.$filename.$ext); - } else if ( $versions = self::getVersionsFromTrash($filename, $timestamp) ) { - foreach ($versions as $v) { - $view->rename('versions_trashbin/'.$filename.'.v'.$v.'.d'.$timestamp, 'files_versions/'.$location.'/'.$filename.$ext.'.v'.$v); + if( $view->rename($source, $target.$ext) ) { + + // if versioning app is enabled, copy versions from the trash bin back to the original location + if ( $return && \OCP\App::isEnabled('files_versions') ) { + if ( $result[0][type] == 'dir' ) { + $view->rename('versions_trashbin/'. $filename.'.d'.$timestamp, 'files_versions/'.$location.'/'.$filename.$ext); + } else if ( $versions = self::getVersionsFromTrash($filename, $timestamp) ) { + foreach ($versions as $v) { + $view->rename('versions_trashbin/'.$filename.'.v'.$v.'.d'.$timestamp, 'files_versions/'.$location.'/'.$filename.$ext.'.v'.$v); + } } - } + } + + $query = \OC_DB::prepare('DELETE FROM *PREFIX*files_trash WHERE user=? AND id=? AND timestamp=?'); + $query->execute(array($user,$filename,$timestamp)); + + return true; } - - $query = \OC_DB::prepare('DELETE FROM *PREFIX*files_trash WHERE user=? AND id=? AND timestamp=?'); - $query->execute(array($user,$filename,$timestamp)); - + + return false; } /** diff --git a/apps/files_trashbin/templates/part.list.php b/apps/files_trashbin/templates/part.list.php index d022854330..c9a641a2e2 100644 --- a/apps/files_trashbin/templates/part.list.php +++ b/apps/files_trashbin/templates/part.list.php @@ -24,7 +24,8 @@ $name = str_replace('%2F', '/', $name); $directory = str_replace('+', '%20', urlencode($file['directory'])); $directory = str_replace('%2F', '/', $directory); ?> - Date: Fri, 18 Jan 2013 14:11:55 +0100 Subject: [PATCH 184/272] remove unneeded function --- apps/files_trashbin/js/trash.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/apps/files_trashbin/js/trash.js b/apps/files_trashbin/js/trash.js index b9088944fd..075dc6c315 100644 --- a/apps/files_trashbin/js/trash.js +++ b/apps/files_trashbin/js/trash.js @@ -1,12 +1,3 @@ -// Override download path to files_sharing/public.php -function fileDownloadPath(dir, file) { - var url = $('#downloadURL').val(); - if (url.indexOf('&path=') != -1) { - url += '/'+file; - } - return url; -} - $(document).ready(function() { if (typeof FileActions !== 'undefined') { From 8fdcd72d7f5fa9a0f2f84c63a1bc91211265cb19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Fri, 18 Jan 2013 15:12:38 +0100 Subject: [PATCH 185/272] expire files in trash bin after 30 days --- apps/files_trashbin/lib/trash.php | 32 ++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/apps/files_trashbin/lib/trash.php b/apps/files_trashbin/lib/trash.php index 322f5679b7..1b0b9cef4b 100644 --- a/apps/files_trashbin/lib/trash.php +++ b/apps/files_trashbin/lib/trash.php @@ -23,6 +23,8 @@ namespace OCA_Trash; class Trashbin { + + const DELETEAFTER=30; // how long do we keep files in the trash bin (number of days) /** * move file to the trash bin @@ -124,9 +126,33 @@ class Trashbin { /** * clean up the trash bin */ - private static function expire() { - //TODO: implement expire function - return true; + private static function expire() { + + $view = new \OC_FilesystemView('/'.\OCP\User::getUser()); + $user = \OCP\User::getUser(); + + $query = \OC_DB::prepare('SELECT location,type,id,timestamp FROM *PREFIX*files_trash WHERE user=?'); + $result = $query->execute(array($user))->fetchAll(); + + $limit = time() - (self::DELETEAFTER * 86400); + + foreach ( $result as $r ) { + $timestamp = $r['timestamp']; + $filename = $r['id']; + if ( $r['timestamp'] < $limit ) { + $view->unlink('files_trashbin/'.$filename.'.d'.$timestamp); + if ($r['type'] == 'dir') { + $view->unlink('versions_trashbin/'.$filename.'.d'.$timestamp); + } else if ( $versions = self::getVersionsFromTrash($filename, $timestamp) ) { + foreach ($versions as $v) { + $view->unlink('versions_trashbin/'.$filename.'.v'.$v.'.d'.$timestamp); + } + } + } + } + + $query = \OC_DB::prepare('DELETE FROM *PREFIX*files_trash WHERE user=? AND timestampexecute(array($user,$limit)); } /** From adbc576439faf19515b84d0fe4bb48ba52c4450a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Fri, 18 Jan 2013 17:50:44 +0100 Subject: [PATCH 186/272] handle group restore --- apps/files_trashbin/ajax/undelete.php | 35 ++++-- apps/files_trashbin/css/trash.css | 4 +- apps/files_trashbin/js/trash.js | 135 +++++++++++++++++++++++- apps/files_trashbin/lib/trash.php | 2 +- apps/files_trashbin/templates/index.php | 9 +- 5 files changed, 166 insertions(+), 19 deletions(-) diff --git a/apps/files_trashbin/ajax/undelete.php b/apps/files_trashbin/ajax/undelete.php index f55629d695..05b5e7a5ee 100644 --- a/apps/files_trashbin/ajax/undelete.php +++ b/apps/files_trashbin/ajax/undelete.php @@ -4,11 +4,34 @@ if(!OC_User::isLoggedIn()) { exit; } -$timestamp = isset( $_REQUEST['timestamp'] ) ? $_REQUEST['timestamp'] : ''; -$filename = isset( $_REQUEST['filename'] ) ? trim($_REQUEST['filename'], '/\\') : ''; +$files = $_REQUEST['files']; +$list = explode(';', $files); + +$error = array(); + +$i = 0; +foreach ($list as $file) { + $delimiter = strrpos($file, '.d'); + $filename = substr($file, 0, $delimiter); + $timestamp = substr($file, $delimiter+2); + + if ( !OCA_Trash\Trashbin::restore($filename, $timestamp) ) { + $error[] = $filename; + } else { + $success[$i]['filename'] = $filename; + $success[$i]['timestamp'] = $timestamp; + $i++; + } -if ( OCA_Trash\Trashbin::restore($filename, $timestamp) ) { - OCP\JSON::success(array("data" => array('filename'=>$filename, 'timestamp' => $timestamp))); -} else { - OCP\JSON::error(array("data" => array("message" => "Couldn't restore ".$filename))); } + +if ( $error ) { + $filelist = ''; + foreach ( $error as $e ) { + $filelist .= $e.', '; + } + OCP\JSON::error(array("data" => array("message" => "Couldn't restore ".rtrim($filelist,', '), "success" => $success, "error" => $error))); +} else { + OCP\JSON::success(array("data" => array("success" => $success))); +} + diff --git a/apps/files_trashbin/css/trash.css b/apps/files_trashbin/css/trash.css index bd6341c6fb..e0dd8c6e93 100644 --- a/apps/files_trashbin/css/trash.css +++ b/apps/files_trashbin/css/trash.css @@ -19,12 +19,12 @@ body { color:#fff; } -#header #download { +#header #undelete { font-weight:700; margin-left:2em; } -#header #download img { +#header #undelete img { padding-left:.1em; padding-right:.3em; vertical-align:text-bottom; diff --git a/apps/files_trashbin/js/trash.js b/apps/files_trashbin/js/trash.js index 075dc6c315..8f3786f15e 100644 --- a/apps/files_trashbin/js/trash.js +++ b/apps/files_trashbin/js/trash.js @@ -1,20 +1,145 @@ + $(document).ready(function() { if (typeof FileActions !== 'undefined') { FileActions.register('all', 'Undelete', OC.PERMISSION_READ, '', function(filename) { var tr=$('tr').filterAttr('data-file', filename); $.post(OC.filePath('files_trashbin','ajax','undelete.php'), - {timestamp:tr.attr('data-timestamp'),filename:tr.attr('data-filename')}, + {files:tr.attr('data-filename')+'.d'+tr.attr('data-timestamp')}, function(result){ - if (result.status == 'success') { - var row = document.getElementById(result.data.filename+'.d'+result.data.timestamp); + for (var i = 0; i < result.data.success.length; i++) { + var row = document.getElementById(result.data.success[i].filename+'.d'+result.data.success[i].timestamp); row.parentNode.removeChild(row); - } else { + } + if (result.status != 'success') { OC.dialogs.alert(result.data.message, 'Error'); } }); }); }; + + // Sets the select_all checkbox behaviour : + $('#select_all').click(function() { + if($(this).attr('checked')){ + // Check all + $('td.filename input:checkbox').attr('checked', true); + $('td.filename input:checkbox').parent().parent().addClass('selected'); + }else{ + // Uncheck all + $('td.filename input:checkbox').attr('checked', false); + $('td.filename input:checkbox').parent().parent().removeClass('selected'); + } + procesSelection(); + }); -}); \ No newline at end of file + $('td.filename input:checkbox').live('change',function(event) { + if (event.shiftKey) { + var last = $(lastChecked).parent().parent().prevAll().length; + var first = $(this).parent().parent().prevAll().length; + var start = Math.min(first, last); + var end = Math.max(first, last); + var rows = $(this).parent().parent().parent().children('tr'); + for (var i = start; i < end; i++) { + $(rows).each(function(index) { + if (index == i) { + var checkbox = $(this).children().children('input:checkbox'); + $(checkbox).attr('checked', 'checked'); + $(checkbox).parent().parent().addClass('selected'); + } + }); + } + } + var selectedCount=$('td.filename input:checkbox:checked').length; + $(this).parent().parent().toggleClass('selected'); + if(!$(this).attr('checked')){ + $('#select_all').attr('checked',false); + }else{ + if(selectedCount==$('td.filename input:checkbox').length){ + $('#select_all').attr('checked',true); + } + } + procesSelection(); + }); + + $('.undelete').click('click',function(event) { + var fileslist=getSelectedFiles('file').join(';'); + $.post(OC.filePath('files_trashbin','ajax','undelete.php'), + {files:fileslist}, + function(result){ + for (var i = 0; i < result.data.success.length; i++) { + var row = document.getElementById(result.data.success[i].filename+'.d'+result.data.success[i].timestamp); + row.parentNode.removeChild(row); + } + if (result.status != 'success') { + OC.dialogs.alert(result.data.message, 'Error'); + } + }); + }); + + +}); + +function procesSelection(){ + var selected=getSelectedFiles(); + var selectedFiles=selected.filter(function(el){return el.type=='file'}); + var selectedFolders=selected.filter(function(el){return el.type=='dir'}); + if(selectedFiles.length==0 && selectedFolders.length==0) { + $('#headerName>span.name').text(t('files','Name')); + $('#modified').text(t('files','Deleted')); + $('table').removeClass('multiselect'); + $('.selectedActions').hide(); + } + else { + $('.selectedActions').show(); + var selection=''; + if(selectedFolders.length>0){ + if(selectedFolders.length==1){ + selection+=t('files','1 folder'); + }else{ + selection+=t('files','{count} folders',{count: selectedFolders.length}); + } + if(selectedFiles.length>0){ + selection+=' & '; + } + } + if(selectedFiles.length>0){ + if(selectedFiles.length==1){ + selection+=t('files','1 file'); + }else{ + selection+=t('files','{count} files',{count: selectedFiles.length}); + } + } + $('#headerName>span.name').text(selection); + $('#modified').text(''); + $('table').addClass('multiselect'); + } +} + +/** + * @brief get a list of selected files + * @param string property (option) the property of the file requested + * @return array + * + * possible values for property: name, mime, size and type + * if property is set, an array with that property for each file is returnd + * if it's ommited an array of objects with all properties is returned + */ +function getSelectedFiles(property){ + var elements=$('td.filename input:checkbox:checked').parent().parent(); + var files=[]; + elements.each(function(i,element){ + var file={ + name:$(element).attr('data-filename'), + file:$(element).attr('data-file'), + timestamp:$(element).attr('data-timestamp'), + type:$(element).attr('data-type') + }; + if(property){ + files.push(file[property]); + }else{ + files.push(file); + } + }); + return files; +} \ No newline at end of file diff --git a/apps/files_trashbin/lib/trash.php b/apps/files_trashbin/lib/trash.php index 1b0b9cef4b..abfcf847ac 100644 --- a/apps/files_trashbin/lib/trash.php +++ b/apps/files_trashbin/lib/trash.php @@ -77,7 +77,7 @@ class Trashbin { * @param $timestamp time when the file was deleted */ public static function restore($filename, $timestamp) { - + $user = \OCP\User::getUser(); $view = new \OC_FilesystemView('/'.$user); diff --git a/apps/files_trashbin/templates/index.php b/apps/files_trashbin/templates/index.php index a412379d53..a9cb216b1f 100644 --- a/apps/files_trashbin/templates/index.php +++ b/apps/files_trashbin/templates/index.php @@ -16,13 +16,12 @@ t( 'Name' ); ?> - - - Download + + t('Undelete')?> - From 5df6f9d14d5a05d91d6706b420c8ad9a387a9f6d Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Fri, 18 Jan 2013 23:56:47 -0500 Subject: [PATCH 187/272] Fix merge --- lib/public/share.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/public/share.php b/lib/public/share.php index 9d2a64b459..5f9ad7fc09 100644 --- a/lib/public/share.php +++ b/lib/public/share.php @@ -684,13 +684,13 @@ class Share { } else { if ($fileDependent) { if (($itemType == 'file' || $itemType == 'folder') - && $format == \OC_Share_Backend_File::FORMAT_FILE_APP + && $format == \OC_Share_Backend_File::FORMAT_GET_FOLDER_CONTENTS || $format == \OC_Share_Backend_File::FORMAT_FILE_APP_ROOT ) { $select = '`*PREFIX*share`.`id`, `item_type`, `*PREFIX*share`.`parent`, `uid_owner`, ' - .'`share_type`, `share_with`, `file_source`, `path`, `file_target`, `permissions`, ' - .'`expiration`, `name`, `ctime`, `mtime`, `mimetype`, `size`, `encrypted`, ' - .'`versioned`, `writable`'; + .'`share_type`, `share_with`, `file_source`, `path`, `file_target`, ' + .'`permissions`, `expiration`, `storage`, `*PREFIX*filecache`.`parent` as `file_parent`, ' + .'`name` `mtime`, `mimetype`, `mimepart`, `size`, `encrypted`, `etag`'; } else { $select = '`*PREFIX*share`.`id`, `item_type`, `item_source`, `item_target`, `*PREFIX*share`.`parent`, `share_type`, `share_with`, `uid_owner`, `file_source`, `path`, `file_target`, `permissions`, `stime`, `expiration`, `token`'; } From cd8d8360b0bcc07e22c4c9b86252ef1676b123dc Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Fri, 18 Jan 2013 23:57:13 -0500 Subject: [PATCH 188/272] Disable fancy folder sharing for now --- lib/public/share.php | 56 ++++++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/lib/public/share.php b/lib/public/share.php index 5f9ad7fc09..f65d272ff1 100644 --- a/lib/public/share.php +++ b/lib/public/share.php @@ -300,36 +300,36 @@ class Share { throw new \Exception($message); } // If the item is a folder, scan through the folder looking for equivalent item types - if ($itemType == 'folder') { - $parentFolder = self::put('folder', $itemSource, $shareType, $shareWith, $uidOwner, $permissions, true); - if ($parentFolder && $files = \OC\Files\Filesystem::getDirectoryContent($itemSource)) { - for ($i = 0; $i < count($files); $i++) { - $name = substr($files[$i]['name'], strpos($files[$i]['name'], $itemSource) - strlen($itemSource)); - if ($files[$i]['mimetype'] == 'httpd/unix-directory' - && $children = \OC\Files\Filesystem::getDirectoryContent($name, '/') - ) { - // Continue scanning into child folders - array_push($files, $children); - } else { - // Check file extension for an equivalent item type to convert to - $extension = strtolower(substr($itemSource, strrpos($itemSource, '.') + 1)); - foreach (self::$backends as $type => $backend) { - if (isset($backend->dependsOn) && $backend->dependsOn == 'file' && isset($backend->supportedFileExtensions) && in_array($extension, $backend->supportedFileExtensions)) { - $itemType = $type; - break; - } - } - // Pass on to put() to check if this item should be converted, the item won't be inserted into the database unless it can be converted - self::put($itemType, $name, $shareType, $shareWith, $uidOwner, $permissions, $parentFolder); - } - } - return true; - } - return false; - } else { +// if ($itemType == 'folder') { +// $parentFolder = self::put('folder', $itemSource, $shareType, $shareWith, $uidOwner, $permissions, true); +// if ($parentFolder && $files = \OC\Files\Filesystem::getDirectoryContent($itemSource)) { +// for ($i = 0; $i < count($files); $i++) { +// $name = substr($files[$i]['name'], strpos($files[$i]['name'], $itemSource) - strlen($itemSource)); +// if ($files[$i]['mimetype'] == 'httpd/unix-directory' +// && $children = \OC\Files\Filesystem::getDirectoryContent($name, '/') +// ) { +// // Continue scanning into child folders +// array_push($files, $children); +// } else { +// // Check file extension for an equivalent item type to convert to +// $extension = strtolower(substr($itemSource, strrpos($itemSource, '.') + 1)); +// foreach (self::$backends as $type => $backend) { +// if (isset($backend->dependsOn) && $backend->dependsOn == 'file' && isset($backend->supportedFileExtensions) && in_array($extension, $backend->supportedFileExtensions)) { +// $itemType = $type; +// break; +// } +// } +// // Pass on to put() to check if this item should be converted, the item won't be inserted into the database unless it can be converted +// self::put($itemType, $name, $shareType, $shareWith, $uidOwner, $permissions, $parentFolder); +// } +// } +// return true; +// } +// return false; +// } else { // Put the item into the database return self::put($itemType, $itemSource, $shareType, $shareWith, $uidOwner, $permissions); - } +// } } /** From 8ca30d244c19b33e7e3b0da247b70160a3acc44f Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Sat, 19 Jan 2013 00:02:40 -0500 Subject: [PATCH 189/272] Implement getETag() in shared storage --- apps/files_sharing/lib/sharedstorage.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/files_sharing/lib/sharedstorage.php b/apps/files_sharing/lib/sharedstorage.php index c8756af8ed..b0be6843c5 100644 --- a/apps/files_sharing/lib/sharedstorage.php +++ b/apps/files_sharing/lib/sharedstorage.php @@ -431,7 +431,14 @@ class Shared extends \OC\Files\Storage\Common { } public function getETag($path) { - + if ($path == '') { + return parent::getETag($path); + } + if ($source = $this->getSourcePath($path)) { + list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); + return $storage->getETag($internalPath); + } + return null; } } From 664f33a29c81701fbf6a7fb18eda81e1c4665ecc Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Sat, 19 Jan 2013 21:04:25 -0500 Subject: [PATCH 190/272] Update mtimes of all parent folders after change inside --- lib/files/cache/updater.php | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/lib/files/cache/updater.php b/lib/files/cache/updater.php index 8b0d383503..d04541c219 100644 --- a/lib/files/cache/updater.php +++ b/lib/files/cache/updater.php @@ -35,7 +35,7 @@ class Updater { $scanner = $storage->getScanner($internalPath); $scanner->scan($internalPath, Scanner::SCAN_SHALLOW); $cache->correctFolderSize($internalPath); - self::eTagUpdate($path); + self::correctFolder($path, $storage->filemtime($internalPath)); } } @@ -49,11 +49,17 @@ class Updater { $cache = $storage->getCache($internalPath); $cache->remove($internalPath); $cache->correctFolderSize($internalPath); - self::eTagUpdate($path); + self::correctFolder($path, time()); } } - static public function eTagUpdate($path) { + /** + * Update the mtime and ETag of all parent folders + * + * @param string $path + * @param string $time + */ + static public function correctFolder($path, $time) { if ($path !== '' && $path !== '/') { $parent = dirname($path); if ($parent === '.') { @@ -68,8 +74,8 @@ class Updater { $cache = $storage->getCache(); $id = $cache->getId($internalPath); if ($id !== -1) { - $cache->update($id, array('etag' => $storage->getETag($internalPath))); - self::eTagUpdate($parent); + $cache->update($id, array('mtime' => $time, 'etag' => $storage->getETag($internalPath))); + self::correctFolder($parent, $time); } } } From 3fd28632c470e89d7c9334379da3d8fa6faf0de4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Mon, 21 Jan 2013 13:07:43 +0100 Subject: [PATCH 191/272] allow to look into deleted directories --- apps/files/js/fileactions.js | 1 + apps/files_trashbin/index.php | 48 +++++++++++++++++++-- apps/files_trashbin/templates/part.list.php | 13 +++++- 3 files changed, 56 insertions(+), 6 deletions(-) diff --git a/apps/files/js/fileactions.js b/apps/files/js/fileactions.js index 093b6204c3..d20f9e835b 100644 --- a/apps/files/js/fileactions.js +++ b/apps/files/js/fileactions.js @@ -189,6 +189,7 @@ FileActions.register('all', 'Rename', OC.PERMISSION_UPDATE, function () { FileList.rename(filename); }); + FileActions.register('dir', 'Open', OC.PERMISSION_READ, '', function (filename) { window.location = OC.linkTo('files', 'index.php') + '?dir=' + encodeURIComponent($('#dir').val()).replace(/%2F/g, '/') + '/' + encodeURIComponent(filename); }); diff --git a/apps/files_trashbin/index.php b/apps/files_trashbin/index.php index 28414cc1ce..2925223197 100644 --- a/apps/files_trashbin/index.php +++ b/apps/files_trashbin/index.php @@ -14,8 +14,37 @@ $view = new OC_Filesystemview('/'.$user.'/files_trashbin'); OCP\Util::addStyle('files', 'files'); OCP\Util::addScript('files', 'filelist'); -$query = \OC_DB::prepare('SELECT id,location,timestamp,type,mime FROM *PREFIX*files_trash WHERE user=?'); -$result = $query->execute(array($user))->fetchAll(); +$dir = isset($_GET['dir']) ? stripslashes($_GET['dir']) : ''; + +if ($dir) { + $dirlisting = true; + $view = new \OC_FilesystemView('/'.\OCP\User::getUser().'/files_trashbin'); + $fullpath = \OCP\Config::getSystemValue('datadirectory').$view->getAbsolutePath($dir); + $dirContent = opendir($fullpath); + $i = 0; + while($entryName = readdir($dirContent)) { + if ( $entryName != '.' && $entryName != '..' ) { + $pos = strpos($dir.'/', '/', 1); + $tmp = substr($dir, 0, $pos); + $pos = strrpos($tmp, '.d'); + $timestamp = substr($tmp,$pos+2); + error_log("timestamp: $timestamp"); + $result[] = array( + 'id' => $entryName, + 'timestamp' => $timestamp, + 'mime' => $view->getMimeType($dir.'/'.$entryName), + 'type' => $view->is_dir($dir.'/'.$entryName) ? 'dir' : 'file', + 'location' => $dir, + ); + } + } + closedir($fullpath); + +} else { + $dirlisting = false; + $query = \OC_DB::prepare('SELECT id,location,timestamp,type,mime FROM *PREFIX*files_trash WHERE user=?'); + $result = $query->execute(array($user))->fetchAll(); +} $files = array(); foreach ($result as $r) { @@ -38,15 +67,26 @@ foreach ($result as $r) { $files[] = $i; } +// Make breadcrumb +$breadcrumb = array('dir' => '', 'name' => 'Trash'); +$pathtohere = ''; +foreach (explode('/', $dir) as $i) { + if ($i != '') { + $pathtohere .= '/' . $i; + $breadcrumb[] = array('dir' => $pathtohere, 'name' => $i); + } +} + $breadcrumbNav = new OCP\Template('files', 'part.breadcrumb', ''); $breadcrumbNav->assign('breadcrumb', array(array('dir' => '', 'name' => 'Trash')), false); $breadcrumbNav->assign('baseURL', OCP\Util::linkTo('files_trashbin', 'index.php') . '?dir=', false); $list = new OCP\Template('files_trashbin', 'part.list', ''); $list->assign('files', $files, false); -$list->assign('baseURL', OCP\Util::linkTo('files_trashbin', 'index.php'). '?dir=', false); -$list->assign('downloadURL', OCP\Util::linkTo('files_trashbin', 'download.php') . '?file=', false); +$list->assign('baseURL', OCP\Util::linkTo('files_trashbin', 'index.php'). '?dir='.$dir, false); +$list->assign('downloadURL', OCP\Util::linkTo('files_trashbin', 'download.php') . '?file='.$dir, false); $list->assign('disableSharing', true); +$list->assign('dirlisting', $dirlisting); $list->assign('disableDownloadActions', true); $tmpl->assign('breadcrumb', $breadcrumbNav->fetchPage(), false); $tmpl->assign('fileList', $list->fetchPage(), false); diff --git a/apps/files_trashbin/templates/part.list.php b/apps/files_trashbin/templates/part.list.php index c9a641a2e2..72359da299 100644 --- a/apps/files_trashbin/templates/part.list.php +++ b/apps/files_trashbin/templates/part.list.php @@ -40,9 +40,17 @@ > - + + + + + - + + + + + @@ -69,3 +77,4 @@ Date: Mon, 21 Jan 2013 22:01:22 +0100 Subject: [PATCH 192/272] Cache: return non numberic storage id --- lib/files/cache/cache.php | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/files/cache/cache.php b/lib/files/cache/cache.php index 5c306c2ed1..7f6d191fee 100644 --- a/lib/files/cache/cache.php +++ b/lib/files/cache/cache.php @@ -130,6 +130,7 @@ class Cache { $data['size'] = (int)$data['size']; $data['mtime'] = (int)$data['mtime']; $data['encrypted'] = (bool)$data['encrypted']; + $data['storage'] = $this->storageId; $data['mimetype'] = $this->getMimetype($data['mimetype']); $data['mimepart'] = $this->getMimetype($data['mimepart']); } From 1762378f67085cc9e8fecb287d73f1f1570cb3cc Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 21 Jan 2013 22:07:55 +0100 Subject: [PATCH 193/272] Files: don't load fileactions.js when upgrading filecache --- apps/files/index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/files/index.php b/apps/files/index.php index c10ae76e9e..b1cd40f254 100644 --- a/apps/files/index.php +++ b/apps/files/index.php @@ -30,7 +30,6 @@ OCP\Util::addscript('files', 'jquery.iframe-transport'); OCP\Util::addscript('files', 'jquery.fileupload'); OCP\Util::addscript('files', 'jquery-visibility'); OCP\Util::addscript('files', 'filelist'); -OCP\Util::addscript('files', 'fileactions'); OCP\App::setActiveNavigationEntry('files_index'); // Load the files @@ -116,6 +115,7 @@ if ($needUpgrade) { $tmpl = new OCP\Template('files', 'upgrade', 'user'); $tmpl->printPage(); } else { + OCP\Util::addscript('files', 'fileactions'); OCP\Util::addscript('files', 'files'); OCP\Util::addscript('files', 'keyboardshortcuts'); $tmpl = new OCP\Template('files', 'index', 'user'); From 8ede3290ae2da4b04f451ff3d502f0ac60ac6585 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 21 Jan 2013 22:31:44 +0100 Subject: [PATCH 194/272] Cache: add database indexes for mimetype and mimepart --- db_structure.xml | 24 ++++++++++++++++++++++++ lib/util.php | 2 +- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/db_structure.xml b/db_structure.xml index f1f0257281..479ee3259f 100644 --- a/db_structure.xml +++ b/db_structure.xml @@ -258,6 +258,30 @@ + + fs_storage_mimetype + + storage + ascending + + + mimetype + ascending + + + + + fs_storage_mimepart + + storage + ascending + + + mimepart + ascending + + + diff --git a/lib/util.php b/lib/util.php index 59566c9f4f..f947ed9c17 100755 --- a/lib/util.php +++ b/lib/util.php @@ -74,7 +74,7 @@ class OC_Util { */ public static function getVersion() { // hint: We only can count up. So the internal version number of ownCloud 4.5 will be 4.90.0. This is not visible to the user - return array(4,91,07); + return array(4,91,8); } /** From e666ce3622bed37ba6d9327aeecdf4a96dc7f5f4 Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Mon, 21 Jan 2013 23:57:26 -0500 Subject: [PATCH 195/272] Fix external storage UI for new storage namespaces --- apps/files_external/js/dropbox.js | 4 ++-- apps/files_external/js/google.js | 7 ++++--- apps/files_external/js/settings.js | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/apps/files_external/js/dropbox.js b/apps/files_external/js/dropbox.js index c1e3864070..a9a3155e67 100644 --- a/apps/files_external/js/dropbox.js +++ b/apps/files_external/js/dropbox.js @@ -1,6 +1,6 @@ $(document).ready(function() { - $('#externalStorage tbody tr.OC_Filestorage_Dropbox').each(function() { + $('#externalStorage tbody tr.\\\\OC\\\\Files\\\\Storage\\\\Dropbox').each(function() { var configured = $(this).find('[data-parameter="configured"]'); if ($(configured).val() == 'true') { $(this).find('.configuration input').attr('disabled', 'disabled'); @@ -38,7 +38,7 @@ $(document).ready(function() { $('#externalStorage tbody tr input').live('keyup', function() { var tr = $(this).parent().parent(); - if ($(tr).hasClass('OC_Filestorage_Dropbox') && $(tr).find('[data-parameter="configured"]').val() != 'true') { + if ($(tr).hasClass('\\\\OC\\\\Files\\\\Storage\\\\Dropbox') && $(tr).find('[data-parameter="configured"]').val() != 'true') { var config = $(tr).find('.configuration'); if ($(tr).find('.mountPoint input').val() != '' && $(config).find('[data-parameter="app_key"]').val() != '' && $(config).find('[data-parameter="app_secret"]').val() != '') { if ($(tr).find('.dropbox').length == 0) { diff --git a/apps/files_external/js/google.js b/apps/files_external/js/google.js index 0b3c314eb5..7d759d13da 100644 --- a/apps/files_external/js/google.js +++ b/apps/files_external/js/google.js @@ -1,6 +1,6 @@ $(document).ready(function() { - $('#externalStorage tbody tr.OC_Filestorage_Google').each(function() { + $('#externalStorage tbody tr.\\\\OC\\\\Files\\\\Storage\\\\Google').each(function() { var configured = $(this).find('[data-parameter="configured"]'); if ($(configured).val() == 'true') { $(this).find('.configuration') @@ -34,7 +34,8 @@ $(document).ready(function() { }); $('#externalStorage tbody tr').live('change', function() { - if ($(this).hasClass('OC_Filestorage_Google') && $(this).find('[data-parameter="configured"]').val() != 'true') { + console.log('hello'); + if ($(this).hasClass('\\\\OC\\\\Files\\\\Storage\\\\Google') && $(this).find('[data-parameter="configured"]').val() != 'true') { if ($(this).find('.mountPoint input').val() != '') { if ($(this).find('.google').length == 0) { $(this).find('.configuration').append(''+t('files_external', 'Grant access')+''); @@ -45,7 +46,7 @@ $(document).ready(function() { $('#externalStorage tbody tr .mountPoint input').live('keyup', function() { var tr = $(this).parent().parent(); - if ($(tr).hasClass('OC_Filestorage_Google') && $(tr).find('[data-parameter="configured"]').val() != 'true' && $(tr).find('.google').length > 0) { + if ($(tr).hasClass('\\\\OC\\\\Files\\\\Storage\\\\Google') && $(tr).find('[data-parameter="configured"]').val() != 'true' && $(tr).find('.google').length > 0) { if ($(this).val() != '') { $(tr).find('.google').show(); } else { diff --git a/apps/files_external/js/settings.js b/apps/files_external/js/settings.js index 0dc983ca8a..6cbc218008 100644 --- a/apps/files_external/js/settings.js +++ b/apps/files_external/js/settings.js @@ -100,7 +100,7 @@ $(document).ready(function() { td.append(''); } }); - if (parameters['custom'] && $('#externalStorage tbody tr.'+backendClass).length == 1) { + if (parameters['custom'] && $('#externalStorage tbody tr.'+backendClass.replace(/\\/g, '\\\\')).length == 1) { OC.addScript('files_external', parameters['custom']); } return false; From 465c100d8f5932340ea80f8a426ca7f404fc97c6 Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Tue, 22 Jan 2013 00:43:43 -0500 Subject: [PATCH 196/272] Add tests for mtime changes in folders - testRename is failing right now --- tests/lib/files/cache/updater.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/lib/files/cache/updater.php b/tests/lib/files/cache/updater.php index cad3d9d46f..8bf395698a 100644 --- a/tests/lib/files/cache/updater.php +++ b/tests/lib/files/cache/updater.php @@ -78,9 +78,11 @@ class Updater extends \PHPUnit_Framework_TestCase { $cachedData = $this->cache->get('foo.txt'); $this->assertEquals(3, $cachedData['size']); $this->assertNotEquals($fooCachedData['etag'], $cachedData['etag']); + $mtime = $cachedData['mtime']; $cachedData = $this->cache->get(''); $this->assertEquals(2 * $textSize + $imageSize + 3, $cachedData['size']); $this->assertNotEquals($rootCachedData['etag'], $cachedData['etag']); + $this->assertEquals($mtime, $rootCachedData['mtime']); $rootCachedData = $cachedData; $this->assertFalse($this->cache->inCache('bar.txt')); @@ -88,9 +90,11 @@ class Updater extends \PHPUnit_Framework_TestCase { $this->assertTrue($this->cache->inCache('bar.txt')); $cachedData = $this->cache->get('bar.txt'); $this->assertEquals(3, $cachedData['size']); + $mtime = $cachedData['mtime']; $cachedData = $this->cache->get(''); $this->assertEquals(2 * $textSize + $imageSize + 2 * 3, $cachedData['size']); $this->assertNotEquals($rootCachedData['etag'], $cachedData['etag']); + $this->assertEquals($mtime, $rootCachedData['mtime']); } public function testDelete() { @@ -105,6 +109,7 @@ class Updater extends \PHPUnit_Framework_TestCase { $cachedData = $this->cache->get(''); $this->assertEquals(2 * $textSize + $imageSize, $cachedData['size']); $this->assertNotEquals($rootCachedData['etag'], $cachedData['etag']); + $this->assertGreaterThanOrEqual($rootCachedData['mtime'], $cachedData['mtime']); $rootCachedData = $cachedData; Filesystem::mkdir('bar_folder'); @@ -116,6 +121,7 @@ class Updater extends \PHPUnit_Framework_TestCase { $this->assertFalse($this->cache->inCache('bar_folder')); $cachedData = $this->cache->get(''); $this->assertNotEquals($rootCachedData['etag'], $cachedData['etag']); + $this->assertGreaterThanOrEqual($rootCachedData['mtime'], $cachedData['mtime']); } public function testRename() { @@ -132,8 +138,10 @@ class Updater extends \PHPUnit_Framework_TestCase { $this->assertTrue($this->cache->inCache('bar.txt')); $cachedData = $this->cache->get('foo.txt'); $this->assertNotEquals($fooCachedData['etag'], $cachedData['etag']); + $mtime = $cachedData['mtime']; $cachedData = $this->cache->get(''); $this->assertEquals(3 * $textSize + $imageSize, $cachedData['size']); $this->assertNotEquals($rootCachedData['etag'], $cachedData['etag']); + $this->assertEquals($mtime, $cachedData['mtime']); } } From 2c3216266ce067c7e64361909daff4d25a6e32c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Tue, 22 Jan 2013 12:00:04 +0100 Subject: [PATCH 197/272] allow to restore single files/folder from a deleted folder --- apps/files_trashbin/ajax/undelete.php | 15 +++-- apps/files_trashbin/index.php | 15 +++-- apps/files_trashbin/js/trash.js | 8 ++- apps/files_trashbin/lib/trash.php | 74 ++++++++++++++------- apps/files_trashbin/templates/part.list.php | 16 +++-- 5 files changed, 87 insertions(+), 41 deletions(-) diff --git a/apps/files_trashbin/ajax/undelete.php b/apps/files_trashbin/ajax/undelete.php index 05b5e7a5ee..5aa6436ae5 100644 --- a/apps/files_trashbin/ajax/undelete.php +++ b/apps/files_trashbin/ajax/undelete.php @@ -5,17 +5,24 @@ if(!OC_User::isLoggedIn()) { } $files = $_REQUEST['files']; +$dirlisting = $_REQUEST['dirlisting']; $list = explode(';', $files); $error = array(); $i = 0; foreach ($list as $file) { - $delimiter = strrpos($file, '.d'); - $filename = substr($file, 0, $delimiter); - $timestamp = substr($file, $delimiter+2); + if ( $dirlisting=='0') { + $delimiter = strrpos($file, '.d'); + $filename = substr($file, 0, $delimiter); + $timestamp = substr($file, $delimiter+2); + } else { + $path_parts = pathinfo($file); + $filename = $path_parts['basename']; + $timestamp = null; + } - if ( !OCA_Trash\Trashbin::restore($filename, $timestamp) ) { + if ( !OCA_Trash\Trashbin::restore($file, $filename, $timestamp) ) { $error[] = $filename; } else { $success[$i]['filename'] = $filename; diff --git a/apps/files_trashbin/index.php b/apps/files_trashbin/index.php index 2925223197..97bb64c626 100644 --- a/apps/files_trashbin/index.php +++ b/apps/files_trashbin/index.php @@ -28,7 +28,6 @@ if ($dir) { $tmp = substr($dir, 0, $pos); $pos = strrpos($tmp, '.d'); $timestamp = substr($tmp,$pos+2); - error_log("timestamp: $timestamp"); $result[] = array( 'id' => $entryName, 'timestamp' => $timestamp, @@ -68,17 +67,23 @@ foreach ($result as $r) { } // Make breadcrumb -$breadcrumb = array('dir' => '', 'name' => 'Trash'); +$breadcrumb = array(array('dir' => '', 'name' => 'Trash')); $pathtohere = ''; foreach (explode('/', $dir) as $i) { - if ($i != '') { + if ($i != '') { + if ( preg_match('/^(.+)\.d[0-9]+$/', $i, $match) ) { + error_log("match!"); + $name = $match[1]; + } else { + $name = $i; + } $pathtohere .= '/' . $i; - $breadcrumb[] = array('dir' => $pathtohere, 'name' => $i); + $breadcrumb[] = array('dir' => $pathtohere, 'name' => $name); } } $breadcrumbNav = new OCP\Template('files', 'part.breadcrumb', ''); -$breadcrumbNav->assign('breadcrumb', array(array('dir' => '', 'name' => 'Trash')), false); +$breadcrumbNav->assign('breadcrumb', $breadcrumb, false); $breadcrumbNav->assign('baseURL', OCP\Util::linkTo('files_trashbin', 'index.php') . '?dir=', false); $list = new OCP\Template('files_trashbin', 'part.list', ''); diff --git a/apps/files_trashbin/js/trash.js b/apps/files_trashbin/js/trash.js index 8f3786f15e..355e25d97d 100644 --- a/apps/files_trashbin/js/trash.js +++ b/apps/files_trashbin/js/trash.js @@ -5,7 +5,7 @@ $(document).ready(function() { FileActions.register('all', 'Undelete', OC.PERMISSION_READ, '', function(filename) { var tr=$('tr').filterAttr('data-file', filename); $.post(OC.filePath('files_trashbin','ajax','undelete.php'), - {files:tr.attr('data-filename')+'.d'+tr.attr('data-timestamp')}, + {files:tr.attr('data-file'), dirlisting:tr.attr('data-dirlisting') }, function(result){ for (var i = 0; i < result.data.success.length; i++) { var row = document.getElementById(result.data.success[i].filename+'.d'+result.data.success[i].timestamp); @@ -64,8 +64,9 @@ $(document).ready(function() { $('.undelete').click('click',function(event) { var fileslist=getSelectedFiles('file').join(';'); + var dirlisting=getSelectedFiles('dirlisting')[0]; $.post(OC.filePath('files_trashbin','ajax','undelete.php'), - {files:fileslist}, + {files:fileslist, dirlisting:dirlisting}, function(result){ for (var i = 0; i < result.data.success.length; i++) { var row = document.getElementById(result.data.success[i].filename+'.d'+result.data.success[i].timestamp); @@ -133,7 +134,8 @@ function getSelectedFiles(property){ name:$(element).attr('data-filename'), file:$(element).attr('data-file'), timestamp:$(element).attr('data-timestamp'), - type:$(element).attr('data-type') + type:$(element).attr('data-type'), + dirlisting:$(element).attr('data-dirlisting') }; if(property){ files.push(file[property]); diff --git a/apps/files_trashbin/lib/trash.php b/apps/files_trashbin/lib/trash.php index abfcf847ac..9b891e773b 100644 --- a/apps/files_trashbin/lib/trash.php +++ b/apps/files_trashbin/lib/trash.php @@ -73,49 +73,63 @@ class Trashbin { /** * restore files from trash bin + * @param $file path to the deleted file * @param $filename name of the file * @param $timestamp time when the file was deleted */ - public static function restore($filename, $timestamp) { + public static function restore($file, $filename, $timestamp) { $user = \OCP\User::getUser(); $view = new \OC_FilesystemView('/'.$user); - $query = \OC_DB::prepare('SELECT location,type FROM *PREFIX*files_trash WHERE user=? AND id=? AND timestamp=?'); - $result = $query->execute(array($user,$filename,$timestamp))->fetchAll(); - - if ( count($result) != 1 ) { - \OC_Log::write('files_trashbin', 'trash bin database inconsistent!', OC_Log::ERROR); - return false; - } - - // if location no longer exists, restore file in the root directory - $location = $result[0]['location']; - if ( $result[0]['location'] != '/' && !$view->is_dir('files'.$result[0]['location']) ) { - $location = '/'; + if ( $timestamp ) { + $query = \OC_DB::prepare('SELECT location,type FROM *PREFIX*files_trash WHERE user=? AND id=? AND timestamp=?'); + $result = $query->execute(array($user,$filename,$timestamp))->fetchAll(); + if ( count($result) != 1 ) { + \OC_Log::write('files_trashbin', 'trash bin database inconsistent!', OC_Log::ERROR); + return false; + } + + // if location no longer exists, restore file in the root directory + $location = $result[0]['location']; + if ( $result[0]['location'] != '/' && !$view->is_dir('files'.$result[0]['location']) ) { + $location = ''; + } + } else { + $path_parts = pathinfo($filename); + $result[] = array( + 'location' => $path_parts['dirname'], + 'type' => $view->is_dir('/files_trashbin/'.$file) ? 'dir' : 'files', + ); + $location = ''; } - $source = 'files_trashbin/'.$filename.'.d'.$timestamp; + $source = \OC_Filesystem::normalizePath('files_trashbin/'.$file); $target = \OC_Filesystem::normalizePath('files/'.$location.'/'.$filename); // we need a extension in case a file/dir with the same name already exists $ext = self::getUniqueExtension($location, $filename, $view); if( $view->rename($source, $target.$ext) ) { - // if versioning app is enabled, copy versions from the trash bin back to the original location - if ( $return && \OCP\App::isEnabled('files_versions') ) { - if ( $result[0][type] == 'dir' ) { - $view->rename('versions_trashbin/'. $filename.'.d'.$timestamp, 'files_versions/'.$location.'/'.$filename.$ext); - } else if ( $versions = self::getVersionsFromTrash($filename, $timestamp) ) { + if ( \OCP\App::isEnabled('files_versions') ) { + if ( $result[0]['type'] == 'dir' ) { + $view->rename(\OC_Filesystem::normalizePath('versions_trashbin/'. $file), \OC_Filesystem::normalizePath('files_versions/'.$location.'/'.$filename.$ext)); + } else if ( $versions = self::getVersionsFromTrash($file, $timestamp) ) { foreach ($versions as $v) { - $view->rename('versions_trashbin/'.$filename.'.v'.$v.'.d'.$timestamp, 'files_versions/'.$location.'/'.$filename.$ext.'.v'.$v); + if ($timestamp ) { + $view->rename('versions_trashbin/'.$filename.'.v'.$v.'.d'.$timestamp, 'files_versions/'.$location.'/'.$filename.$ext.'.v'.$v); + } else { + $view->rename('versions_trashbin/'.$file.'.v'.$v, 'files_versions/'.$location.'/'.$filename.$ext.'.v'.$v); + } } } } - $query = \OC_DB::prepare('DELETE FROM *PREFIX*files_trash WHERE user=? AND id=? AND timestamp=?'); - $query->execute(array($user,$filename,$timestamp)); + if ( $timestamp ) { + $query = \OC_DB::prepare('DELETE FROM *PREFIX*files_trash WHERE user=? AND id=? AND timestamp=?'); + $query->execute(array($user,$filename,$timestamp)); + } return true; } @@ -189,12 +203,22 @@ class Trashbin { $versionsName = \OCP\Config::getSystemValue('datadirectory').$view->getAbsolutePath($filename); $versions = array(); + if ($timestamp ) { // fetch for old versions - $matches = glob( $versionsName.'.v*.d'.$timestamp ); + $matches = glob( $versionsName.'.v*.d'.$timestamp ); + $offset = -strlen($timestamp)-2; + } else { + $matches = glob( $versionsName.'.v*' ); + } foreach( $matches as $ma ) { - $parts = explode( '.v', substr($ma, 0, -strlen($timestamp)-2) ); - $versions[] = ( end( $parts ) ); + if ( $timestamp ) { + $parts = explode( '.v', substr($ma, 0, $offset) ); + $versions[] = ( end( $parts ) ); + } else { + $parts = explode( '.v', $ma ); + $versions[] = ( end( $parts ) ); + } } return $versions; } diff --git a/apps/files_trashbin/templates/part.list.php b/apps/files_trashbin/templates/part.list.php index 72359da299..6faddcf485 100644 --- a/apps/files_trashbin/templates/part.list.php +++ b/apps/files_trashbin/templates/part.list.php @@ -24,13 +24,21 @@ $name = str_replace('%2F', '/', $name); $directory = str_replace('+', '%20', urlencode($file['directory'])); $directory = str_replace('%2F', '/', $directory); ?> - ' + + id="" + data-file="" + data-timestamp='' + data-dirlisting=1 + + id="" + data-file="" data-timestamp='' - data-permissions=''> + data-dirlisting=0 + > style="background-image:url()" From 76bd0cffcecfdcee5b5101e50db75b6b92314423 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Tue, 22 Jan 2013 12:13:50 +0100 Subject: [PATCH 198/272] remove table row in trash bin view after undelete --- apps/files_trashbin/ajax/undelete.php | 2 +- apps/files_trashbin/js/trash.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/files_trashbin/ajax/undelete.php b/apps/files_trashbin/ajax/undelete.php index 5aa6436ae5..178d85ccf3 100644 --- a/apps/files_trashbin/ajax/undelete.php +++ b/apps/files_trashbin/ajax/undelete.php @@ -25,7 +25,7 @@ foreach ($list as $file) { if ( !OCA_Trash\Trashbin::restore($file, $filename, $timestamp) ) { $error[] = $filename; } else { - $success[$i]['filename'] = $filename; + $success[$i]['filename'] = $file; $success[$i]['timestamp'] = $timestamp; $i++; } diff --git a/apps/files_trashbin/js/trash.js b/apps/files_trashbin/js/trash.js index 355e25d97d..92a431d495 100644 --- a/apps/files_trashbin/js/trash.js +++ b/apps/files_trashbin/js/trash.js @@ -8,7 +8,7 @@ $(document).ready(function() { {files:tr.attr('data-file'), dirlisting:tr.attr('data-dirlisting') }, function(result){ for (var i = 0; i < result.data.success.length; i++) { - var row = document.getElementById(result.data.success[i].filename+'.d'+result.data.success[i].timestamp); + var row = document.getElementById(result.data.success[i].filename); row.parentNode.removeChild(row); } if (result.status != 'success') { @@ -69,7 +69,7 @@ $(document).ready(function() { {files:fileslist, dirlisting:dirlisting}, function(result){ for (var i = 0; i < result.data.success.length; i++) { - var row = document.getElementById(result.data.success[i].filename+'.d'+result.data.success[i].timestamp); + var row = document.getElementById(result.data.success[i].filename); row.parentNode.removeChild(row); } if (result.status != 'success') { From bdee0e204eed057270f787afc9aa1702147b37d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Tue, 22 Jan 2013 12:30:12 +0100 Subject: [PATCH 199/272] calculate correct relative date for trashbin file list --- apps/files_trashbin/index.php | 1 - apps/files_trashbin/templates/part.list.php | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/files_trashbin/index.php b/apps/files_trashbin/index.php index 97bb64c626..d415ef263e 100644 --- a/apps/files_trashbin/index.php +++ b/apps/files_trashbin/index.php @@ -72,7 +72,6 @@ $pathtohere = ''; foreach (explode('/', $dir) as $i) { if ($i != '') { if ( preg_match('/^(.+)\.d[0-9]+$/', $i, $match) ) { - error_log("match!"); $name = $match[1]; } else { $name = $i; diff --git a/apps/files_trashbin/templates/part.list.php b/apps/files_trashbin/templates/part.list.php index 6faddcf485..205e11c286 100644 --- a/apps/files_trashbin/templates/part.list.php +++ b/apps/files_trashbin/templates/part.list.php @@ -16,7 +16,7 @@ // the bigger the file, the darker the shade of grey; megabytes*2 $simple_size_color = intval(200-$file['size']/(1024*1024)*2); if($simple_size_color<0) $simple_size_color = 0; - $relative_deleted_date = OCP\relative_modified_date($file['mtime']); + $relative_deleted_date = OCP\relative_modified_date($file['timestamp']); // the older the file, the brighter the shade of grey; days*14 $relative_date_color = round((time()-$file['mtime'])/60/60/24*14); if($relative_date_color>200) $relative_date_color = 200; From 5c4e2ad490014113ad32c33e00e2aab01d7a3205 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Tue, 22 Jan 2013 13:19:41 +0100 Subject: [PATCH 200/272] allow admin to change retention obligation for the trash bin, default value is 180 days --- apps/files_trashbin/lib/trash.php | 7 ++++--- config/config.sample.php | 3 +++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/apps/files_trashbin/lib/trash.php b/apps/files_trashbin/lib/trash.php index 9b891e773b..0dad059022 100644 --- a/apps/files_trashbin/lib/trash.php +++ b/apps/files_trashbin/lib/trash.php @@ -24,8 +24,7 @@ namespace OCA_Trash; class Trashbin { - const DELETEAFTER=30; // how long do we keep files in the trash bin (number of days) - + const DEFAULT_RETENTION_OBLIGATION=180; // how long do we keep files in the trash bin if no other value is defined in the config file (unit: days) /** * move file to the trash bin * @@ -148,7 +147,9 @@ class Trashbin { $query = \OC_DB::prepare('SELECT location,type,id,timestamp FROM *PREFIX*files_trash WHERE user=?'); $result = $query->execute(array($user))->fetchAll(); - $limit = time() - (self::DELETEAFTER * 86400); + $retention_obligation = \OC_Config::getValue('trashbin_retention_obligation', self::DEFAULT_RETENTION_OBLIGATION); + + $limit = time() - ($retention_obligation * 86400); foreach ( $result as $r ) { $timestamp = $r['timestamp']; diff --git a/config/config.sample.php b/config/config.sample.php index b1655d0283..373466c08d 100644 --- a/config/config.sample.php +++ b/config/config.sample.php @@ -92,6 +92,9 @@ $CONFIG = array( /* Password to use for sendmail mail, depends on mail_smtpauth if this is used */ "mail_smtppassword" => "", +/* how long should ownCloud keep delteted files in the trash bin, default value: 180 days */ +'trashbin_retention_obligation' => 180, + /* Check 3rdparty apps for malicious code fragments */ "appcodechecker" => "", From 9d77317de82cb5ea3e3e8e2a3d13122134e0ca0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Tue, 15 Jan 2013 20:35:15 +0100 Subject: [PATCH 201/272] add trash button to web interface --- apps/files/css/files.css | 4 +++- apps/files/index.php | 1 + apps/files/js/files.js | 5 +++++ apps/files/templates/index.php | 6 +++++- 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/apps/files/css/files.css b/apps/files/css/files.css index 0c97b009b8..f9406f5865 100644 --- a/apps/files/css/files.css +++ b/apps/files/css/files.css @@ -23,7 +23,9 @@ #new>ul>li>p { cursor:pointer; } #new>ul>li>form>input { padding:0.3em; margin:-0.3em; } -#upload { +#trash { height:17px; margin:0 0 0 1em; z-index:1010; position:absolute; right:13.5em; } + +#upload { height:27px; padding:0; margin-left:0.2em; overflow:hidden; } #upload a { diff --git a/apps/files/index.php b/apps/files/index.php index 1c4b7fbd49..8861be7228 100644 --- a/apps/files/index.php +++ b/apps/files/index.php @@ -100,6 +100,7 @@ $tmpl->assign('dir', OC_Filesystem::normalizePath($dir)); $tmpl->assign('isCreatable', OC_Filesystem::isCreatable($dir . '/')); $tmpl->assign('permissions', $permissions); $tmpl->assign('files', $files); +$tmpl->assign('trash', \OCP\App::isEnabled('files_trashbin')); $tmpl->assign('uploadMaxFilesize', $maxUploadFilesize); $tmpl->assign('uploadMaxHumanFilesize', OCP\Util::humanFileSize($maxUploadFilesize)); $tmpl->assign('allowZipDownload', intval(OCP\Config::getSystemValue('allowZipDownload', true))); diff --git a/apps/files/js/files.js b/apps/files/js/files.js index c1abe20515..9f5169fea5 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -99,6 +99,11 @@ $(document).ready(function() { $(this).parent().children('#file_upload_start').trigger('click'); return false; }); + + // Show Trash bin + $('#trash a').live('click', function() { + console.log("hello"); + }); var lastChecked; diff --git a/apps/files/templates/index.php b/apps/files/templates/index.php index 2e0772443f..f6b4c29d5a 100644 --- a/apps/files/templates/index.php +++ b/apps/files/templates/index.php @@ -35,6 +35,11 @@ + + +
-
From f15deaf55635b730693b1137582c87fcfcaffcf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Wed, 16 Jan 2013 16:01:11 +0100 Subject: [PATCH 202/272] listen to post delete event to allow the trash bin to create a copy of the version first --- apps/files_versions/appinfo/app.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/files_versions/appinfo/app.php b/apps/files_versions/appinfo/app.php index afc0a67edb..edd0a2f702 100644 --- a/apps/files_versions/appinfo/app.php +++ b/apps/files_versions/appinfo/app.php @@ -12,5 +12,5 @@ OCP\Util::addscript('files_versions', 'versions'); // Listen to write signals OCP\Util::connectHook('OC_Filesystem', 'write', "OCA_Versions\Hooks", "write_hook"); // Listen to delete and rename signals -OCP\Util::connectHook('OC_Filesystem', 'delete', "OCA_Versions\Hooks", "remove_hook"); +OCP\Util::connectHook('OC_Filesystem', 'post-delete', "OCA_Versions\Hooks", "remove_hook"); OCP\Util::connectHook('OC_Filesystem', 'rename', "OCA_Versions\Hooks", "rename_hook"); \ No newline at end of file From 9bacb0e1792fac4a8f9728aec76692fd50fbcd64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Thu, 17 Jan 2013 13:17:48 +0100 Subject: [PATCH 203/272] rename "publicListView" option to "disableSharing", this is more meaningful also because it is not only useful for the public list view --- apps/files/templates/part.list.php | 6 +++--- apps/files_sharing/js/share.js | 3 +-- apps/files_sharing/public.php | 2 +- apps/files_sharing/templates/public.php | 6 +++--- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/apps/files/templates/part.list.php b/apps/files/templates/part.list.php index dfac43d1b1..1970a80e1b 100644 --- a/apps/files/templates/part.list.php +++ b/apps/files/templates/part.list.php @@ -1,8 +1,8 @@ diff --git a/apps/files_sharing/js/share.js b/apps/files_sharing/js/share.js index a46d017980..0dae4daa08 100644 --- a/apps/files_sharing/js/share.js +++ b/apps/files_sharing/js/share.js @@ -1,7 +1,6 @@ $(document).ready(function() { - if (typeof OC.Share !== 'undefined' && typeof FileActions !== 'undefined' && !publicListView) { - + if (typeof OC.Share !== 'undefined' && typeof FileActions !== 'undefined' && !disableSharing) { FileActions.register('all', 'Share', OC.PERMISSION_READ, OC.imagePath('core', 'actions/share'), function(filename) { if ($('#dir').val() == '/') { var item = $('#dir').val() + filename; diff --git a/apps/files_sharing/public.php b/apps/files_sharing/public.php index efd977a1b6..acd5353faf 100644 --- a/apps/files_sharing/public.php +++ b/apps/files_sharing/public.php @@ -257,7 +257,7 @@ if ($linkItem) { $list = new OCP\Template('files', 'part.list', ''); $list->assign('files', $files, false); - $list->assign('publicListView', true); + $list->assign('disableSharing', true); $list->assign('baseURL', OCP\Util::linkToPublic('files').$urlLinkIdentifiers.'&path=', false); $list->assign('downloadURL', OCP\Util::linkToPublic('files').$urlLinkIdentifiers.'&download&path=', false); $breadcrumbNav = new OCP\Template('files', 'part.breadcrumb', '' ); diff --git a/apps/files_sharing/templates/public.php b/apps/files_sharing/templates/public.php index 647e1e08a3..bfcc521e10 100644 --- a/apps/files_sharing/templates/public.php +++ b/apps/files_sharing/templates/public.php @@ -1,8 +1,8 @@ From dfab3d3d7f0e42fde5de8a5438a97d6d428977a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Fri, 18 Jan 2013 10:23:31 +0100 Subject: [PATCH 204/272] call the trash bin view --- apps/files/js/files.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/files/js/files.js b/apps/files/js/files.js index 9f5169fea5..795e065c0f 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -100,9 +100,9 @@ $(document).ready(function() { return false; }); - // Show Trash bin + // Show trash bin $('#trash a').live('click', function() { - console.log("hello"); + window.location=OC.filePath('files_trashbin', '', 'index.php'); }); var lastChecked; From 8259768732e3456c5cc54cfe43975513881b6124 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Fri, 18 Jan 2013 10:51:13 +0100 Subject: [PATCH 205/272] introduce option to disable download action --- apps/files/js/fileactions.js | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/apps/files/js/fileactions.js b/apps/files/js/fileactions.js index f5ee363a4c..5eac62440f 100644 --- a/apps/files/js/fileactions.js +++ b/apps/files/js/fileactions.js @@ -147,15 +147,19 @@ $(document).ready(function () { } else { var downloadScope = 'file'; } - FileActions.register(downloadScope, 'Download', OC.PERMISSION_READ, function () { - return OC.imagePath('core', 'actions/download'); - }, function (filename) { - window.location = OC.filePath('files', 'ajax', 'download.php') + '?files=' + encodeURIComponent(filename) + '&dir=' + encodeURIComponent($('#dir').val()); - }); - + + if (typeof disableDownloadActions == 'undefined' || !disableDownloadActions) { + FileActions.register(downloadScope, 'Download', OC.PERMISSION_READ, function () { + return OC.imagePath('core', 'actions/download'); + }, function (filename) { + window.location = OC.filePath('files', 'ajax', 'download.php') + '?files=' + encodeURIComponent(filename) + '&dir=' + encodeURIComponent($('#dir').val()); + }); + } + $('#fileList tr').each(function(){ FileActions.display($(this).children('td.filename')); }); + }); FileActions.register('all', 'Delete', OC.PERMISSION_DELETE, function () { From d60522893713df7359d0e02c841f35a65d2186aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Fri, 18 Jan 2013 13:11:29 +0100 Subject: [PATCH 206/272] first version of the trash bin app --- apps/files_trashbin/ajax/undelete.php | 13 ++ apps/files_trashbin/appinfo/app.php | 7 + apps/files_trashbin/appinfo/database.xml | 92 ++++++++++ apps/files_trashbin/appinfo/info.xml | 14 ++ apps/files_trashbin/appinfo/version | 1 + apps/files_trashbin/css/trash.css | 78 ++++++++ apps/files_trashbin/download.php | 52 ++++++ apps/files_trashbin/index.php | 55 ++++++ apps/files_trashbin/js/trash.js | 37 ++++ apps/files_trashbin/lib/hooks.php | 45 +++++ apps/files_trashbin/lib/trash.php | 190 ++++++++++++++++++++ apps/files_trashbin/templates/index.php | 36 ++++ apps/files_trashbin/templates/part.list.php | 70 ++++++++ 13 files changed, 690 insertions(+) create mode 100644 apps/files_trashbin/ajax/undelete.php create mode 100644 apps/files_trashbin/appinfo/app.php create mode 100644 apps/files_trashbin/appinfo/database.xml create mode 100644 apps/files_trashbin/appinfo/info.xml create mode 100644 apps/files_trashbin/appinfo/version create mode 100644 apps/files_trashbin/css/trash.css create mode 100644 apps/files_trashbin/download.php create mode 100644 apps/files_trashbin/index.php create mode 100644 apps/files_trashbin/js/trash.js create mode 100644 apps/files_trashbin/lib/hooks.php create mode 100644 apps/files_trashbin/lib/trash.php create mode 100644 apps/files_trashbin/templates/index.php create mode 100644 apps/files_trashbin/templates/part.list.php diff --git a/apps/files_trashbin/ajax/undelete.php b/apps/files_trashbin/ajax/undelete.php new file mode 100644 index 0000000000..c548034828 --- /dev/null +++ b/apps/files_trashbin/ajax/undelete.php @@ -0,0 +1,13 @@ + array('content'=>'foo', 'id' => 'bar'))); \ No newline at end of file diff --git a/apps/files_trashbin/appinfo/app.php b/apps/files_trashbin/appinfo/app.php new file mode 100644 index 0000000000..3741d42c78 --- /dev/null +++ b/apps/files_trashbin/appinfo/app.php @@ -0,0 +1,7 @@ + + + + *dbname* + true + false + + utf8 + + + + *dbprefix*files_trash + + + + + id + text + + true + 50 + + + + user + text + + true + 50 + + + + timestamp + text + + true + 12 + + + + location + text + + true + 200 + + + + type + text + + true + 4 + + + + mime + text + + true + 30 + + + + id_index + + id + ascending + + + + + timestamp_index + + timestamp + ascending + + + + + user_index + + user + ascending + + + + + +
+ +
diff --git a/apps/files_trashbin/appinfo/info.xml b/apps/files_trashbin/appinfo/info.xml new file mode 100644 index 0000000000..9b48612636 --- /dev/null +++ b/apps/files_trashbin/appinfo/info.xml @@ -0,0 +1,14 @@ + + + files_trashbin + Trash + Trash bin + AGPL + Bjoern Schiessle + true + 4.9 + + + + + diff --git a/apps/files_trashbin/appinfo/version b/apps/files_trashbin/appinfo/version new file mode 100644 index 0000000000..49d59571fb --- /dev/null +++ b/apps/files_trashbin/appinfo/version @@ -0,0 +1 @@ +0.1 diff --git a/apps/files_trashbin/css/trash.css b/apps/files_trashbin/css/trash.css new file mode 100644 index 0000000000..bd6341c6fb --- /dev/null +++ b/apps/files_trashbin/css/trash.css @@ -0,0 +1,78 @@ +body { + background:#ddd; +} + +#header { + background:#1d2d44; + box-shadow:0 0 10px rgba(0,0,0,.5), inset 0 -2px 10px #222; + height:2.5em; + left:0; + line-height:2.5em; + position:fixed; + right:0; + top:0; + z-index:100; + padding:.5em; +} + +#details { + color:#fff; +} + +#header #download { + font-weight:700; + margin-left:2em; +} + +#header #download img { + padding-left:.1em; + padding-right:.3em; + vertical-align:text-bottom; +} + +#preview { + background:#eee; + border-bottom:1px solid #f8f8f8; + min-height:30em; + padding-top:2em; + text-align:center; + margin:50px auto; +} + +#noPreview { + display:none; + padding-top:5em; +} + +p.info { + color:#777; + text-align:center; + text-shadow:#fff 0 1px 0; + width:22em; + margin:2em auto; +} + +p.info a { + color:#777; + font-weight:700; +} + +#imgframe { + height:75%; + padding-bottom:2em; + width:80%; + margin:0 auto; +} + +#imgframe img { + max-height:100%; + max-width:100%; +} + +table td.filename .name { + display:block; + height:1.5em; + vertical-align:middle; + margin-left:3em; + /*font-weight:bold;*/ +} \ No newline at end of file diff --git a/apps/files_trashbin/download.php b/apps/files_trashbin/download.php new file mode 100644 index 0000000000..a987dd4fd1 --- /dev/null +++ b/apps/files_trashbin/download.php @@ -0,0 +1,52 @@ +. +* +*/ + +// Check if we are a user +OCP\User::checkLoggedIn(); + +$filename = $_GET["file"]; + +$view = new OC_FilesystemView('/'.\OCP\User::getUser().'/files_trashbin'); + +if(!$view->file_exists($filename)) { + error_log("file does not exist... " . $view->getInternalPath($filename)); + header("HTTP/1.0 404 Not Found"); + $tmpl = new OCP\Template( '', '404', 'guest' ); + $tmpl->assign('file', $filename); + $tmpl->printPage(); + exit; +} + +$ftype=$view->getMimeType( $filename ); + +header('Content-Type:'.$ftype);if ( preg_match( "/MSIE/", $_SERVER["HTTP_USER_AGENT"] ) ) { + header( 'Content-Disposition: attachment; filename="' . rawurlencode( basename($filename) ) . '"' ); +} else { + header( 'Content-Disposition: attachment; filename*=UTF-8\'\'' . rawurlencode( basename($filename) ) + . '; filename="' . rawurlencode( basename($filename) ) . '"' ); +} +OCP\Response::disableCaching(); +header('Content-Length: '. $view->filesize($filename)); + +OC_Util::obEnd(); +$view->readfile( $filename ); diff --git a/apps/files_trashbin/index.php b/apps/files_trashbin/index.php new file mode 100644 index 0000000000..28414cc1ce --- /dev/null +++ b/apps/files_trashbin/index.php @@ -0,0 +1,55 @@ +execute(array($user))->fetchAll(); + +$files = array(); +foreach ($result as $r) { + $i = array(); + $i['name'] = $r['id']; + $i['date'] = OCP\Util::formatDate($r['timestamp']); + $i['timestamp'] = $r['timestamp']; + $i['mimetype'] = $r['mime']; + $i['type'] = $r['type']; + if ($i['type'] == 'file') { + $fileinfo = pathinfo($r['id']); + $i['basename'] = $fileinfo['filename']; + $i['extension'] = isset($fileinfo['extension']) ? ('.'.$fileinfo['extension']) : ''; + } + $i['directory'] = $r['location']; + if ($i['directory'] == '/') { + $i['directory'] = ''; + } + $i['permissions'] = OCP\PERMISSION_READ; + $files[] = $i; +} + +$breadcrumbNav = new OCP\Template('files', 'part.breadcrumb', ''); +$breadcrumbNav->assign('breadcrumb', array(array('dir' => '', 'name' => 'Trash')), false); +$breadcrumbNav->assign('baseURL', OCP\Util::linkTo('files_trashbin', 'index.php') . '?dir=', false); + +$list = new OCP\Template('files_trashbin', 'part.list', ''); +$list->assign('files', $files, false); +$list->assign('baseURL', OCP\Util::linkTo('files_trashbin', 'index.php'). '?dir=', false); +$list->assign('downloadURL', OCP\Util::linkTo('files_trashbin', 'download.php') . '?file=', false); +$list->assign('disableSharing', true); +$list->assign('disableDownloadActions', true); +$tmpl->assign('breadcrumb', $breadcrumbNav->fetchPage(), false); +$tmpl->assign('fileList', $list->fetchPage(), false); +$tmpl->assign('dir', OC_Filesystem::normalizePath($view->getAbsolutePath())); + +$tmpl->printPage(); diff --git a/apps/files_trashbin/js/trash.js b/apps/files_trashbin/js/trash.js new file mode 100644 index 0000000000..37e9a4bf10 --- /dev/null +++ b/apps/files_trashbin/js/trash.js @@ -0,0 +1,37 @@ +// Override download path to files_sharing/public.php +function fileDownloadPath(dir, file) { + var url = $('#downloadURL').val(); + if (url.indexOf('&path=') != -1) { + url += '/'+file; + } + return url; +} + +$(document).ready(function() { + + if (typeof FileActions !== 'undefined') { + FileActions.register('all', 'Undelete', OC.PERMISSION_READ, '', function(filename) { + var tr=$('tr').filterAttr('data-file', filename); + console.log("tr: " + tr.attr('data-timestamp') + " name: " + name); + $.post(OC.filePath('files_trashbin','ajax','undelete.php'), + {timestamp:tr.attr('data-timestamp'),filename:tr.attr('data-filename')}, + function(result){ + if (result.status == 'success') { + return; + var date=new Date(); + FileList.addFile(name,0,date,false,hidden); + var tr=$('tr').filterAttr('data-file',name); + tr.data('mime','text/plain').data('id',result.data.id); + tr.attr('data-id', result.data.id); + getMimeIcon('text/plain',function(path){ + tr.find('td.filename').attr('style','background-image:url('+path+')'); + }); + } else { + OC.dialogs.alert(result.data.message, 'Error'); + } + }); + + }); + }; + +}); \ No newline at end of file diff --git a/apps/files_trashbin/lib/hooks.php b/apps/files_trashbin/lib/hooks.php new file mode 100644 index 0000000000..d3bee105b5 --- /dev/null +++ b/apps/files_trashbin/lib/hooks.php @@ -0,0 +1,45 @@ +. + * + */ + +/** + * This class contains all hooks. + */ + +namespace OCA_Trash; + +class Hooks { + + /** + * @brief Copy files to trash bin + * @param array + * + * This function is connected to the delete signal of OC_Filesystem + * to copy the file to the trash bin + */ + public static function remove_hook($params) { + + if ( \OCP\App::isEnabled('files_trashbin') ) { + $path = $params['path']; + Trashbin::move2trash($path); + } + } +} diff --git a/apps/files_trashbin/lib/trash.php b/apps/files_trashbin/lib/trash.php new file mode 100644 index 0000000000..384a865ce0 --- /dev/null +++ b/apps/files_trashbin/lib/trash.php @@ -0,0 +1,190 @@ +. + * + */ + +namespace OCA_Trash; + +class Trashbin { + + /** + * move file to the trash bin + * + * @param $file_path path to the deleted file/directory relative to the files root directory + */ + public static function move2trash($file_path) { + $user = \OCP\User::getUser(); + $view = new \OC_FilesystemView('/'. $user); + if (!$view->is_dir('files_trashbin')) { + $view->mkdir('files_trashbin'); + $view->mkdir("versions_trashbin"); + } + + $path_parts = pathinfo($file_path); + + $deleted = $path_parts['basename']; + $location = $path_parts['dirname']; + $timestamp = time(); + $mime = $view->getMimeType('files'.$file_path); + + if ( $view->is_dir('files'.$file_path) ) { + $type = 'dir'; + } else { + $type = 'file'; + } + + self::copy_recursive($file_path, 'files_trashbin/'.$deleted.'.d'.$timestamp, $view); + + $query = \OC_DB::prepare("INSERT INTO *PREFIX*files_trash (id,timestamp,location,type,mime,user) VALUES (?,?,?,?,?,?)"); + $result = $query->execute(array($deleted, $timestamp, $location, $type, $mime, $user)); + + if ( \OCP\App::isEnabled('files_versions') ) { + if ( $view->is_dir('files_versions'.$file_path) ) { + $view->rename('files_versions'.$file_path, 'versions_trashbin/'. $deleted.'.d'.$timestamp); + } else if ( $versions = \OCA_Versions\Storage::getVersions($file_path) ) { + foreach ($versions as $v) { + $view->rename('files_versions'.$v['path'].'.v'.$v['version'], 'versions_trashbin/'. $deleted.'.v'.$v['version'].'.d'.$timestamp); + } + } + } + + self::expire(); + } + + + /** + * restore files from trash bin + * @param $filename name of the file + * @param $timestamp time when the file was deleted + */ + public static function restore($filename, $timestamp) { + + $user = \OCP\User::getUser(); + $view = new \OC_FilesystemView('/'.$user); + + $query = \OC_DB::prepare('SELECT location,type FROM *PREFIX*files_trash WHERE user=? AND id=? AND timestamp=?'); + $result = $query->execute(array($user,$filename,$timestamp))->fetchAll(); + + if ( count($result) != 1 ) { + \OC_Log::write('files_trashbin', 'trash bin database inconsistent!', OC_Log::ERROR); + return false; + } + + $location = $result[0]['location']; + if ( $result[0]['location'] != '/' && !$view->is_dir('files'.$result[0]['location']) ) { + $location = '/'; + } + + $source = 'files_trashbin/'.$filename.'.d'.$timestamp; + $target = \OC_Filesystem::normalizePath('files/'.$location.'/'.$filename); + + $ext = self::getUniqueExtension($location, $filename, $view); + + $view->rename($source, $target.$ext); + + if ( \OCP\App::isEnabled('files_versions') ) { + if ( $result[0][type] == 'dir' ) { + $view->rename('versions_trashbin/'. $filename.'.d'.$timestamp, 'files_versions/'.$location.'/'.$filename.$ext); + } else if ( $versions = self::getVersionsFromTrash($filename, $timestamp) ) { + foreach ($versions as $v) { + $view->rename('versions_trashbin/'.$filename.'.v'.$v.'.d'.$timestamp, 'files_versions/'.$location.'/'.$filename.$ext.'.v'.$v); + } + } + } + + $query = \OC_DB::prepare('DELETE FROM *PREFIX*files_trash WHERE user=? AND id=? AND timestamp=?'); + $query->execute(array($user,$filename,$timestamp)); + + } + + /** + * clean up the trash bin + */ + private static function expire() { + //TODO: implement expire function + return true; + } + + /** + * recursive copy to copy a whole directory + * + * @param $source source path, relative to the users files directory + * @param $destination destination path relative to the users root directoy + * @param $view file view for the users root directory + * @param $location location of the source files, either "fscache" or "local" + */ + private static function copy_recursive( $source, $destination, $view, $location='fscache' ) { + if ( $view->is_dir( 'files'.$source ) ) { + $view->mkdir( $destination ); + foreach ( \OC_Files::getDirectoryContent($source) as $i ) { + $pathDir = $source.'/'.$i['name']; + if ( $view->is_dir('files'.$pathDir) ) { + self::copy_recursive($pathDir, $destination.'/'.$i['name'], $view); + } else { + $view->copy( 'files'.$pathDir, $destination . '/' . $i['name'] ); + } + } + } else { + $view->copy( 'files'.$source, $destination ); + } + } + + /** + * find all versions which belong to the file we want to restore + * @param $filename name of the file which should be restored + * @param $timestamp timestamp when the file was deleted + */ + private static function getVersionsFromTrash($filename, $timestamp) { + $view = new \OC_FilesystemView('/'.\OCP\User::getUser().'/versions_trashbin'); + $versionsName = \OCP\Config::getSystemValue('datadirectory').$view->getAbsolutePath($filename); + $versions = array(); + + // fetch for old versions + $matches = glob( $versionsName.'.v*.d'.$timestamp ); + + foreach( $matches as $ma ) { + $parts = explode( '.v', substr($ma, 0, -strlen($timestamp)-2) ); + $versions[] = ( end( $parts ) ); + } + return $versions; + } + + /** + * find unique extension for restored file if a file with the same name already exists + * @param $location where the file should be restored + * @param $filename name of the file + * @param $view filesystem view relative to users root directory + * @return string with unique extension + */ + private static function getUniqueExtension($location, $filename, $view) { + $ext = ''; + if ( $view->file_exists('files'.$location.'/'.$filename) ) { + $tmpext = '.restored'; + $ext = $tmpext; + $i = 1; + while ( $view->file_exists('files'.$location.'/'.$filename.$ext) ) { + $ext = $tmpext.$i; + $i++; + } + } + return $ext; + } + +} diff --git a/apps/files_trashbin/templates/index.php b/apps/files_trashbin/templates/index.php new file mode 100644 index 0000000000..a412379d53 --- /dev/null +++ b/apps/files_trashbin/templates/index.php @@ -0,0 +1,36 @@ + +
+ +
+
+
+ + +
t('Nothing in here. Upload something!')?>
+ + + + + + + + + + + + +
+ + t( 'Name' ); ?> + + + + Download" /> + t('Download')?> + + + + + t( 'Deleted' ); ?> +
diff --git a/apps/files_trashbin/templates/part.list.php b/apps/files_trashbin/templates/part.list.php new file mode 100644 index 0000000000..d022854330 --- /dev/null +++ b/apps/files_trashbin/templates/part.list.php @@ -0,0 +1,70 @@ + + +200) $relative_date_color = 200; + $name = str_replace('+', '%20', urlencode($file['name'])); + $name = str_replace('%2F', '/', $name); + $directory = str_replace('+', '%20', urlencode($file['directory'])); + $directory = str_replace('%2F', '/', $directory); ?> + ' + data-permissions=''> + + style="background-image:url()" + + style="background-image:url()" + + > + + + + + + + + + + + + + + + + + + + + + + + + + + Date: Fri, 18 Jan 2013 14:09:22 +0100 Subject: [PATCH 207/272] remove item in the trash bin view after successful undelete --- apps/files_trashbin/ajax/undelete.php | 9 +++--- apps/files_trashbin/js/trash.js | 12 ++----- apps/files_trashbin/lib/trash.php | 35 ++++++++++++--------- apps/files_trashbin/templates/part.list.php | 3 +- 4 files changed, 30 insertions(+), 29 deletions(-) diff --git a/apps/files_trashbin/ajax/undelete.php b/apps/files_trashbin/ajax/undelete.php index c548034828..f55629d695 100644 --- a/apps/files_trashbin/ajax/undelete.php +++ b/apps/files_trashbin/ajax/undelete.php @@ -7,7 +7,8 @@ if(!OC_User::isLoggedIn()) { $timestamp = isset( $_REQUEST['timestamp'] ) ? $_REQUEST['timestamp'] : ''; $filename = isset( $_REQUEST['filename'] ) ? trim($_REQUEST['filename'], '/\\') : ''; -OCA_Trash\Trashbin::restore($filename, $timestamp); - -//TODO: return useful data after succsessful restore operation and remove restored files from the list view -OCP\JSON::success(array("data" => array('content'=>'foo', 'id' => 'bar'))); \ No newline at end of file +if ( OCA_Trash\Trashbin::restore($filename, $timestamp) ) { + OCP\JSON::success(array("data" => array('filename'=>$filename, 'timestamp' => $timestamp))); +} else { + OCP\JSON::error(array("data" => array("message" => "Couldn't restore ".$filename))); +} diff --git a/apps/files_trashbin/js/trash.js b/apps/files_trashbin/js/trash.js index 37e9a4bf10..b9088944fd 100644 --- a/apps/files_trashbin/js/trash.js +++ b/apps/files_trashbin/js/trash.js @@ -12,20 +12,12 @@ $(document).ready(function() { if (typeof FileActions !== 'undefined') { FileActions.register('all', 'Undelete', OC.PERMISSION_READ, '', function(filename) { var tr=$('tr').filterAttr('data-file', filename); - console.log("tr: " + tr.attr('data-timestamp') + " name: " + name); $.post(OC.filePath('files_trashbin','ajax','undelete.php'), {timestamp:tr.attr('data-timestamp'),filename:tr.attr('data-filename')}, function(result){ if (result.status == 'success') { - return; - var date=new Date(); - FileList.addFile(name,0,date,false,hidden); - var tr=$('tr').filterAttr('data-file',name); - tr.data('mime','text/plain').data('id',result.data.id); - tr.attr('data-id', result.data.id); - getMimeIcon('text/plain',function(path){ - tr.find('td.filename').attr('style','background-image:url('+path+')'); - }); + var row = document.getElementById(result.data.filename+'.d'+result.data.timestamp); + row.parentNode.removeChild(row); } else { OC.dialogs.alert(result.data.message, 'Error'); } diff --git a/apps/files_trashbin/lib/trash.php b/apps/files_trashbin/lib/trash.php index 384a865ce0..322f5679b7 100644 --- a/apps/files_trashbin/lib/trash.php +++ b/apps/files_trashbin/lib/trash.php @@ -86,7 +86,8 @@ class Trashbin { \OC_Log::write('files_trashbin', 'trash bin database inconsistent!', OC_Log::ERROR); return false; } - + + // if location no longer exists, restore file in the root directory $location = $result[0]['location']; if ( $result[0]['location'] != '/' && !$view->is_dir('files'.$result[0]['location']) ) { $location = '/'; @@ -95,23 +96,29 @@ class Trashbin { $source = 'files_trashbin/'.$filename.'.d'.$timestamp; $target = \OC_Filesystem::normalizePath('files/'.$location.'/'.$filename); + // we need a extension in case a file/dir with the same name already exists $ext = self::getUniqueExtension($location, $filename, $view); - $view->rename($source, $target.$ext); - - if ( \OCP\App::isEnabled('files_versions') ) { - if ( $result[0][type] == 'dir' ) { - $view->rename('versions_trashbin/'. $filename.'.d'.$timestamp, 'files_versions/'.$location.'/'.$filename.$ext); - } else if ( $versions = self::getVersionsFromTrash($filename, $timestamp) ) { - foreach ($versions as $v) { - $view->rename('versions_trashbin/'.$filename.'.v'.$v.'.d'.$timestamp, 'files_versions/'.$location.'/'.$filename.$ext.'.v'.$v); + if( $view->rename($source, $target.$ext) ) { + + // if versioning app is enabled, copy versions from the trash bin back to the original location + if ( $return && \OCP\App::isEnabled('files_versions') ) { + if ( $result[0][type] == 'dir' ) { + $view->rename('versions_trashbin/'. $filename.'.d'.$timestamp, 'files_versions/'.$location.'/'.$filename.$ext); + } else if ( $versions = self::getVersionsFromTrash($filename, $timestamp) ) { + foreach ($versions as $v) { + $view->rename('versions_trashbin/'.$filename.'.v'.$v.'.d'.$timestamp, 'files_versions/'.$location.'/'.$filename.$ext.'.v'.$v); + } } - } + } + + $query = \OC_DB::prepare('DELETE FROM *PREFIX*files_trash WHERE user=? AND id=? AND timestamp=?'); + $query->execute(array($user,$filename,$timestamp)); + + return true; } - - $query = \OC_DB::prepare('DELETE FROM *PREFIX*files_trash WHERE user=? AND id=? AND timestamp=?'); - $query->execute(array($user,$filename,$timestamp)); - + + return false; } /** diff --git a/apps/files_trashbin/templates/part.list.php b/apps/files_trashbin/templates/part.list.php index d022854330..c9a641a2e2 100644 --- a/apps/files_trashbin/templates/part.list.php +++ b/apps/files_trashbin/templates/part.list.php @@ -24,7 +24,8 @@ $name = str_replace('%2F', '/', $name); $directory = str_replace('+', '%20', urlencode($file['directory'])); $directory = str_replace('%2F', '/', $directory); ?> - Date: Fri, 18 Jan 2013 14:11:55 +0100 Subject: [PATCH 208/272] remove unneeded function --- apps/files_trashbin/js/trash.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/apps/files_trashbin/js/trash.js b/apps/files_trashbin/js/trash.js index b9088944fd..075dc6c315 100644 --- a/apps/files_trashbin/js/trash.js +++ b/apps/files_trashbin/js/trash.js @@ -1,12 +1,3 @@ -// Override download path to files_sharing/public.php -function fileDownloadPath(dir, file) { - var url = $('#downloadURL').val(); - if (url.indexOf('&path=') != -1) { - url += '/'+file; - } - return url; -} - $(document).ready(function() { if (typeof FileActions !== 'undefined') { From 655a20f963e5cd0d73d2abfbcdde0a3519e85be2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Fri, 18 Jan 2013 15:12:38 +0100 Subject: [PATCH 209/272] expire files in trash bin after 30 days --- apps/files_trashbin/lib/trash.php | 32 ++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/apps/files_trashbin/lib/trash.php b/apps/files_trashbin/lib/trash.php index 322f5679b7..1b0b9cef4b 100644 --- a/apps/files_trashbin/lib/trash.php +++ b/apps/files_trashbin/lib/trash.php @@ -23,6 +23,8 @@ namespace OCA_Trash; class Trashbin { + + const DELETEAFTER=30; // how long do we keep files in the trash bin (number of days) /** * move file to the trash bin @@ -124,9 +126,33 @@ class Trashbin { /** * clean up the trash bin */ - private static function expire() { - //TODO: implement expire function - return true; + private static function expire() { + + $view = new \OC_FilesystemView('/'.\OCP\User::getUser()); + $user = \OCP\User::getUser(); + + $query = \OC_DB::prepare('SELECT location,type,id,timestamp FROM *PREFIX*files_trash WHERE user=?'); + $result = $query->execute(array($user))->fetchAll(); + + $limit = time() - (self::DELETEAFTER * 86400); + + foreach ( $result as $r ) { + $timestamp = $r['timestamp']; + $filename = $r['id']; + if ( $r['timestamp'] < $limit ) { + $view->unlink('files_trashbin/'.$filename.'.d'.$timestamp); + if ($r['type'] == 'dir') { + $view->unlink('versions_trashbin/'.$filename.'.d'.$timestamp); + } else if ( $versions = self::getVersionsFromTrash($filename, $timestamp) ) { + foreach ($versions as $v) { + $view->unlink('versions_trashbin/'.$filename.'.v'.$v.'.d'.$timestamp); + } + } + } + } + + $query = \OC_DB::prepare('DELETE FROM *PREFIX*files_trash WHERE user=? AND timestampexecute(array($user,$limit)); } /** From 1255791ef2535aac96889f6be8c8b5b163b45be6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Fri, 18 Jan 2013 17:50:44 +0100 Subject: [PATCH 210/272] handle group restore --- apps/files_trashbin/ajax/undelete.php | 35 ++++-- apps/files_trashbin/css/trash.css | 4 +- apps/files_trashbin/js/trash.js | 135 +++++++++++++++++++++++- apps/files_trashbin/lib/trash.php | 2 +- apps/files_trashbin/templates/index.php | 9 +- 5 files changed, 166 insertions(+), 19 deletions(-) diff --git a/apps/files_trashbin/ajax/undelete.php b/apps/files_trashbin/ajax/undelete.php index f55629d695..05b5e7a5ee 100644 --- a/apps/files_trashbin/ajax/undelete.php +++ b/apps/files_trashbin/ajax/undelete.php @@ -4,11 +4,34 @@ if(!OC_User::isLoggedIn()) { exit; } -$timestamp = isset( $_REQUEST['timestamp'] ) ? $_REQUEST['timestamp'] : ''; -$filename = isset( $_REQUEST['filename'] ) ? trim($_REQUEST['filename'], '/\\') : ''; +$files = $_REQUEST['files']; +$list = explode(';', $files); + +$error = array(); + +$i = 0; +foreach ($list as $file) { + $delimiter = strrpos($file, '.d'); + $filename = substr($file, 0, $delimiter); + $timestamp = substr($file, $delimiter+2); + + if ( !OCA_Trash\Trashbin::restore($filename, $timestamp) ) { + $error[] = $filename; + } else { + $success[$i]['filename'] = $filename; + $success[$i]['timestamp'] = $timestamp; + $i++; + } -if ( OCA_Trash\Trashbin::restore($filename, $timestamp) ) { - OCP\JSON::success(array("data" => array('filename'=>$filename, 'timestamp' => $timestamp))); -} else { - OCP\JSON::error(array("data" => array("message" => "Couldn't restore ".$filename))); } + +if ( $error ) { + $filelist = ''; + foreach ( $error as $e ) { + $filelist .= $e.', '; + } + OCP\JSON::error(array("data" => array("message" => "Couldn't restore ".rtrim($filelist,', '), "success" => $success, "error" => $error))); +} else { + OCP\JSON::success(array("data" => array("success" => $success))); +} + diff --git a/apps/files_trashbin/css/trash.css b/apps/files_trashbin/css/trash.css index bd6341c6fb..e0dd8c6e93 100644 --- a/apps/files_trashbin/css/trash.css +++ b/apps/files_trashbin/css/trash.css @@ -19,12 +19,12 @@ body { color:#fff; } -#header #download { +#header #undelete { font-weight:700; margin-left:2em; } -#header #download img { +#header #undelete img { padding-left:.1em; padding-right:.3em; vertical-align:text-bottom; diff --git a/apps/files_trashbin/js/trash.js b/apps/files_trashbin/js/trash.js index 075dc6c315..8f3786f15e 100644 --- a/apps/files_trashbin/js/trash.js +++ b/apps/files_trashbin/js/trash.js @@ -1,20 +1,145 @@ + $(document).ready(function() { if (typeof FileActions !== 'undefined') { FileActions.register('all', 'Undelete', OC.PERMISSION_READ, '', function(filename) { var tr=$('tr').filterAttr('data-file', filename); $.post(OC.filePath('files_trashbin','ajax','undelete.php'), - {timestamp:tr.attr('data-timestamp'),filename:tr.attr('data-filename')}, + {files:tr.attr('data-filename')+'.d'+tr.attr('data-timestamp')}, function(result){ - if (result.status == 'success') { - var row = document.getElementById(result.data.filename+'.d'+result.data.timestamp); + for (var i = 0; i < result.data.success.length; i++) { + var row = document.getElementById(result.data.success[i].filename+'.d'+result.data.success[i].timestamp); row.parentNode.removeChild(row); - } else { + } + if (result.status != 'success') { OC.dialogs.alert(result.data.message, 'Error'); } }); }); }; + + // Sets the select_all checkbox behaviour : + $('#select_all').click(function() { + if($(this).attr('checked')){ + // Check all + $('td.filename input:checkbox').attr('checked', true); + $('td.filename input:checkbox').parent().parent().addClass('selected'); + }else{ + // Uncheck all + $('td.filename input:checkbox').attr('checked', false); + $('td.filename input:checkbox').parent().parent().removeClass('selected'); + } + procesSelection(); + }); -}); \ No newline at end of file + $('td.filename input:checkbox').live('change',function(event) { + if (event.shiftKey) { + var last = $(lastChecked).parent().parent().prevAll().length; + var first = $(this).parent().parent().prevAll().length; + var start = Math.min(first, last); + var end = Math.max(first, last); + var rows = $(this).parent().parent().parent().children('tr'); + for (var i = start; i < end; i++) { + $(rows).each(function(index) { + if (index == i) { + var checkbox = $(this).children().children('input:checkbox'); + $(checkbox).attr('checked', 'checked'); + $(checkbox).parent().parent().addClass('selected'); + } + }); + } + } + var selectedCount=$('td.filename input:checkbox:checked').length; + $(this).parent().parent().toggleClass('selected'); + if(!$(this).attr('checked')){ + $('#select_all').attr('checked',false); + }else{ + if(selectedCount==$('td.filename input:checkbox').length){ + $('#select_all').attr('checked',true); + } + } + procesSelection(); + }); + + $('.undelete').click('click',function(event) { + var fileslist=getSelectedFiles('file').join(';'); + $.post(OC.filePath('files_trashbin','ajax','undelete.php'), + {files:fileslist}, + function(result){ + for (var i = 0; i < result.data.success.length; i++) { + var row = document.getElementById(result.data.success[i].filename+'.d'+result.data.success[i].timestamp); + row.parentNode.removeChild(row); + } + if (result.status != 'success') { + OC.dialogs.alert(result.data.message, 'Error'); + } + }); + }); + + +}); + +function procesSelection(){ + var selected=getSelectedFiles(); + var selectedFiles=selected.filter(function(el){return el.type=='file'}); + var selectedFolders=selected.filter(function(el){return el.type=='dir'}); + if(selectedFiles.length==0 && selectedFolders.length==0) { + $('#headerName>span.name').text(t('files','Name')); + $('#modified').text(t('files','Deleted')); + $('table').removeClass('multiselect'); + $('.selectedActions').hide(); + } + else { + $('.selectedActions').show(); + var selection=''; + if(selectedFolders.length>0){ + if(selectedFolders.length==1){ + selection+=t('files','1 folder'); + }else{ + selection+=t('files','{count} folders',{count: selectedFolders.length}); + } + if(selectedFiles.length>0){ + selection+=' & '; + } + } + if(selectedFiles.length>0){ + if(selectedFiles.length==1){ + selection+=t('files','1 file'); + }else{ + selection+=t('files','{count} files',{count: selectedFiles.length}); + } + } + $('#headerName>span.name').text(selection); + $('#modified').text(''); + $('table').addClass('multiselect'); + } +} + +/** + * @brief get a list of selected files + * @param string property (option) the property of the file requested + * @return array + * + * possible values for property: name, mime, size and type + * if property is set, an array with that property for each file is returnd + * if it's ommited an array of objects with all properties is returned + */ +function getSelectedFiles(property){ + var elements=$('td.filename input:checkbox:checked').parent().parent(); + var files=[]; + elements.each(function(i,element){ + var file={ + name:$(element).attr('data-filename'), + file:$(element).attr('data-file'), + timestamp:$(element).attr('data-timestamp'), + type:$(element).attr('data-type') + }; + if(property){ + files.push(file[property]); + }else{ + files.push(file); + } + }); + return files; +} \ No newline at end of file diff --git a/apps/files_trashbin/lib/trash.php b/apps/files_trashbin/lib/trash.php index 1b0b9cef4b..abfcf847ac 100644 --- a/apps/files_trashbin/lib/trash.php +++ b/apps/files_trashbin/lib/trash.php @@ -77,7 +77,7 @@ class Trashbin { * @param $timestamp time when the file was deleted */ public static function restore($filename, $timestamp) { - + $user = \OCP\User::getUser(); $view = new \OC_FilesystemView('/'.$user); diff --git a/apps/files_trashbin/templates/index.php b/apps/files_trashbin/templates/index.php index a412379d53..a9cb216b1f 100644 --- a/apps/files_trashbin/templates/index.php +++ b/apps/files_trashbin/templates/index.php @@ -16,13 +16,12 @@ t( 'Name' ); ?> - - - Download + + t('Undelete')?> - From 1817c7895b3db5643d255ba54cf062c3f90885ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Mon, 21 Jan 2013 13:07:43 +0100 Subject: [PATCH 211/272] allow to look into deleted directories --- apps/files/js/fileactions.js | 1 + apps/files_trashbin/index.php | 48 +++++++++++++++++++-- apps/files_trashbin/templates/part.list.php | 13 +++++- 3 files changed, 56 insertions(+), 6 deletions(-) diff --git a/apps/files/js/fileactions.js b/apps/files/js/fileactions.js index 5eac62440f..c30f1bcddd 100644 --- a/apps/files/js/fileactions.js +++ b/apps/files/js/fileactions.js @@ -189,6 +189,7 @@ FileActions.register('all', 'Rename', OC.PERMISSION_UPDATE, function () { FileList.rename(filename); }); + FileActions.register('dir', 'Open', OC.PERMISSION_READ, '', function (filename) { window.location = OC.linkTo('files', 'index.php') + '?dir=' + encodeURIComponent($('#dir').val()).replace(/%2F/g, '/') + '/' + encodeURIComponent(filename); }); diff --git a/apps/files_trashbin/index.php b/apps/files_trashbin/index.php index 28414cc1ce..2925223197 100644 --- a/apps/files_trashbin/index.php +++ b/apps/files_trashbin/index.php @@ -14,8 +14,37 @@ $view = new OC_Filesystemview('/'.$user.'/files_trashbin'); OCP\Util::addStyle('files', 'files'); OCP\Util::addScript('files', 'filelist'); -$query = \OC_DB::prepare('SELECT id,location,timestamp,type,mime FROM *PREFIX*files_trash WHERE user=?'); -$result = $query->execute(array($user))->fetchAll(); +$dir = isset($_GET['dir']) ? stripslashes($_GET['dir']) : ''; + +if ($dir) { + $dirlisting = true; + $view = new \OC_FilesystemView('/'.\OCP\User::getUser().'/files_trashbin'); + $fullpath = \OCP\Config::getSystemValue('datadirectory').$view->getAbsolutePath($dir); + $dirContent = opendir($fullpath); + $i = 0; + while($entryName = readdir($dirContent)) { + if ( $entryName != '.' && $entryName != '..' ) { + $pos = strpos($dir.'/', '/', 1); + $tmp = substr($dir, 0, $pos); + $pos = strrpos($tmp, '.d'); + $timestamp = substr($tmp,$pos+2); + error_log("timestamp: $timestamp"); + $result[] = array( + 'id' => $entryName, + 'timestamp' => $timestamp, + 'mime' => $view->getMimeType($dir.'/'.$entryName), + 'type' => $view->is_dir($dir.'/'.$entryName) ? 'dir' : 'file', + 'location' => $dir, + ); + } + } + closedir($fullpath); + +} else { + $dirlisting = false; + $query = \OC_DB::prepare('SELECT id,location,timestamp,type,mime FROM *PREFIX*files_trash WHERE user=?'); + $result = $query->execute(array($user))->fetchAll(); +} $files = array(); foreach ($result as $r) { @@ -38,15 +67,26 @@ foreach ($result as $r) { $files[] = $i; } +// Make breadcrumb +$breadcrumb = array('dir' => '', 'name' => 'Trash'); +$pathtohere = ''; +foreach (explode('/', $dir) as $i) { + if ($i != '') { + $pathtohere .= '/' . $i; + $breadcrumb[] = array('dir' => $pathtohere, 'name' => $i); + } +} + $breadcrumbNav = new OCP\Template('files', 'part.breadcrumb', ''); $breadcrumbNav->assign('breadcrumb', array(array('dir' => '', 'name' => 'Trash')), false); $breadcrumbNav->assign('baseURL', OCP\Util::linkTo('files_trashbin', 'index.php') . '?dir=', false); $list = new OCP\Template('files_trashbin', 'part.list', ''); $list->assign('files', $files, false); -$list->assign('baseURL', OCP\Util::linkTo('files_trashbin', 'index.php'). '?dir=', false); -$list->assign('downloadURL', OCP\Util::linkTo('files_trashbin', 'download.php') . '?file=', false); +$list->assign('baseURL', OCP\Util::linkTo('files_trashbin', 'index.php'). '?dir='.$dir, false); +$list->assign('downloadURL', OCP\Util::linkTo('files_trashbin', 'download.php') . '?file='.$dir, false); $list->assign('disableSharing', true); +$list->assign('dirlisting', $dirlisting); $list->assign('disableDownloadActions', true); $tmpl->assign('breadcrumb', $breadcrumbNav->fetchPage(), false); $tmpl->assign('fileList', $list->fetchPage(), false); diff --git a/apps/files_trashbin/templates/part.list.php b/apps/files_trashbin/templates/part.list.php index c9a641a2e2..72359da299 100644 --- a/apps/files_trashbin/templates/part.list.php +++ b/apps/files_trashbin/templates/part.list.php @@ -40,9 +40,17 @@ > - + + + + + - + + + + + @@ -69,3 +77,4 @@ Date: Tue, 22 Jan 2013 12:00:04 +0100 Subject: [PATCH 212/272] allow to restore single files/folder from a deleted folder --- apps/files_trashbin/ajax/undelete.php | 15 +++-- apps/files_trashbin/index.php | 15 +++-- apps/files_trashbin/js/trash.js | 8 ++- apps/files_trashbin/lib/trash.php | 74 ++++++++++++++------- apps/files_trashbin/templates/part.list.php | 16 +++-- 5 files changed, 87 insertions(+), 41 deletions(-) diff --git a/apps/files_trashbin/ajax/undelete.php b/apps/files_trashbin/ajax/undelete.php index 05b5e7a5ee..5aa6436ae5 100644 --- a/apps/files_trashbin/ajax/undelete.php +++ b/apps/files_trashbin/ajax/undelete.php @@ -5,17 +5,24 @@ if(!OC_User::isLoggedIn()) { } $files = $_REQUEST['files']; +$dirlisting = $_REQUEST['dirlisting']; $list = explode(';', $files); $error = array(); $i = 0; foreach ($list as $file) { - $delimiter = strrpos($file, '.d'); - $filename = substr($file, 0, $delimiter); - $timestamp = substr($file, $delimiter+2); + if ( $dirlisting=='0') { + $delimiter = strrpos($file, '.d'); + $filename = substr($file, 0, $delimiter); + $timestamp = substr($file, $delimiter+2); + } else { + $path_parts = pathinfo($file); + $filename = $path_parts['basename']; + $timestamp = null; + } - if ( !OCA_Trash\Trashbin::restore($filename, $timestamp) ) { + if ( !OCA_Trash\Trashbin::restore($file, $filename, $timestamp) ) { $error[] = $filename; } else { $success[$i]['filename'] = $filename; diff --git a/apps/files_trashbin/index.php b/apps/files_trashbin/index.php index 2925223197..97bb64c626 100644 --- a/apps/files_trashbin/index.php +++ b/apps/files_trashbin/index.php @@ -28,7 +28,6 @@ if ($dir) { $tmp = substr($dir, 0, $pos); $pos = strrpos($tmp, '.d'); $timestamp = substr($tmp,$pos+2); - error_log("timestamp: $timestamp"); $result[] = array( 'id' => $entryName, 'timestamp' => $timestamp, @@ -68,17 +67,23 @@ foreach ($result as $r) { } // Make breadcrumb -$breadcrumb = array('dir' => '', 'name' => 'Trash'); +$breadcrumb = array(array('dir' => '', 'name' => 'Trash')); $pathtohere = ''; foreach (explode('/', $dir) as $i) { - if ($i != '') { + if ($i != '') { + if ( preg_match('/^(.+)\.d[0-9]+$/', $i, $match) ) { + error_log("match!"); + $name = $match[1]; + } else { + $name = $i; + } $pathtohere .= '/' . $i; - $breadcrumb[] = array('dir' => $pathtohere, 'name' => $i); + $breadcrumb[] = array('dir' => $pathtohere, 'name' => $name); } } $breadcrumbNav = new OCP\Template('files', 'part.breadcrumb', ''); -$breadcrumbNav->assign('breadcrumb', array(array('dir' => '', 'name' => 'Trash')), false); +$breadcrumbNav->assign('breadcrumb', $breadcrumb, false); $breadcrumbNav->assign('baseURL', OCP\Util::linkTo('files_trashbin', 'index.php') . '?dir=', false); $list = new OCP\Template('files_trashbin', 'part.list', ''); diff --git a/apps/files_trashbin/js/trash.js b/apps/files_trashbin/js/trash.js index 8f3786f15e..355e25d97d 100644 --- a/apps/files_trashbin/js/trash.js +++ b/apps/files_trashbin/js/trash.js @@ -5,7 +5,7 @@ $(document).ready(function() { FileActions.register('all', 'Undelete', OC.PERMISSION_READ, '', function(filename) { var tr=$('tr').filterAttr('data-file', filename); $.post(OC.filePath('files_trashbin','ajax','undelete.php'), - {files:tr.attr('data-filename')+'.d'+tr.attr('data-timestamp')}, + {files:tr.attr('data-file'), dirlisting:tr.attr('data-dirlisting') }, function(result){ for (var i = 0; i < result.data.success.length; i++) { var row = document.getElementById(result.data.success[i].filename+'.d'+result.data.success[i].timestamp); @@ -64,8 +64,9 @@ $(document).ready(function() { $('.undelete').click('click',function(event) { var fileslist=getSelectedFiles('file').join(';'); + var dirlisting=getSelectedFiles('dirlisting')[0]; $.post(OC.filePath('files_trashbin','ajax','undelete.php'), - {files:fileslist}, + {files:fileslist, dirlisting:dirlisting}, function(result){ for (var i = 0; i < result.data.success.length; i++) { var row = document.getElementById(result.data.success[i].filename+'.d'+result.data.success[i].timestamp); @@ -133,7 +134,8 @@ function getSelectedFiles(property){ name:$(element).attr('data-filename'), file:$(element).attr('data-file'), timestamp:$(element).attr('data-timestamp'), - type:$(element).attr('data-type') + type:$(element).attr('data-type'), + dirlisting:$(element).attr('data-dirlisting') }; if(property){ files.push(file[property]); diff --git a/apps/files_trashbin/lib/trash.php b/apps/files_trashbin/lib/trash.php index abfcf847ac..9b891e773b 100644 --- a/apps/files_trashbin/lib/trash.php +++ b/apps/files_trashbin/lib/trash.php @@ -73,49 +73,63 @@ class Trashbin { /** * restore files from trash bin + * @param $file path to the deleted file * @param $filename name of the file * @param $timestamp time when the file was deleted */ - public static function restore($filename, $timestamp) { + public static function restore($file, $filename, $timestamp) { $user = \OCP\User::getUser(); $view = new \OC_FilesystemView('/'.$user); - $query = \OC_DB::prepare('SELECT location,type FROM *PREFIX*files_trash WHERE user=? AND id=? AND timestamp=?'); - $result = $query->execute(array($user,$filename,$timestamp))->fetchAll(); - - if ( count($result) != 1 ) { - \OC_Log::write('files_trashbin', 'trash bin database inconsistent!', OC_Log::ERROR); - return false; - } - - // if location no longer exists, restore file in the root directory - $location = $result[0]['location']; - if ( $result[0]['location'] != '/' && !$view->is_dir('files'.$result[0]['location']) ) { - $location = '/'; + if ( $timestamp ) { + $query = \OC_DB::prepare('SELECT location,type FROM *PREFIX*files_trash WHERE user=? AND id=? AND timestamp=?'); + $result = $query->execute(array($user,$filename,$timestamp))->fetchAll(); + if ( count($result) != 1 ) { + \OC_Log::write('files_trashbin', 'trash bin database inconsistent!', OC_Log::ERROR); + return false; + } + + // if location no longer exists, restore file in the root directory + $location = $result[0]['location']; + if ( $result[0]['location'] != '/' && !$view->is_dir('files'.$result[0]['location']) ) { + $location = ''; + } + } else { + $path_parts = pathinfo($filename); + $result[] = array( + 'location' => $path_parts['dirname'], + 'type' => $view->is_dir('/files_trashbin/'.$file) ? 'dir' : 'files', + ); + $location = ''; } - $source = 'files_trashbin/'.$filename.'.d'.$timestamp; + $source = \OC_Filesystem::normalizePath('files_trashbin/'.$file); $target = \OC_Filesystem::normalizePath('files/'.$location.'/'.$filename); // we need a extension in case a file/dir with the same name already exists $ext = self::getUniqueExtension($location, $filename, $view); if( $view->rename($source, $target.$ext) ) { - // if versioning app is enabled, copy versions from the trash bin back to the original location - if ( $return && \OCP\App::isEnabled('files_versions') ) { - if ( $result[0][type] == 'dir' ) { - $view->rename('versions_trashbin/'. $filename.'.d'.$timestamp, 'files_versions/'.$location.'/'.$filename.$ext); - } else if ( $versions = self::getVersionsFromTrash($filename, $timestamp) ) { + if ( \OCP\App::isEnabled('files_versions') ) { + if ( $result[0]['type'] == 'dir' ) { + $view->rename(\OC_Filesystem::normalizePath('versions_trashbin/'. $file), \OC_Filesystem::normalizePath('files_versions/'.$location.'/'.$filename.$ext)); + } else if ( $versions = self::getVersionsFromTrash($file, $timestamp) ) { foreach ($versions as $v) { - $view->rename('versions_trashbin/'.$filename.'.v'.$v.'.d'.$timestamp, 'files_versions/'.$location.'/'.$filename.$ext.'.v'.$v); + if ($timestamp ) { + $view->rename('versions_trashbin/'.$filename.'.v'.$v.'.d'.$timestamp, 'files_versions/'.$location.'/'.$filename.$ext.'.v'.$v); + } else { + $view->rename('versions_trashbin/'.$file.'.v'.$v, 'files_versions/'.$location.'/'.$filename.$ext.'.v'.$v); + } } } } - $query = \OC_DB::prepare('DELETE FROM *PREFIX*files_trash WHERE user=? AND id=? AND timestamp=?'); - $query->execute(array($user,$filename,$timestamp)); + if ( $timestamp ) { + $query = \OC_DB::prepare('DELETE FROM *PREFIX*files_trash WHERE user=? AND id=? AND timestamp=?'); + $query->execute(array($user,$filename,$timestamp)); + } return true; } @@ -189,12 +203,22 @@ class Trashbin { $versionsName = \OCP\Config::getSystemValue('datadirectory').$view->getAbsolutePath($filename); $versions = array(); + if ($timestamp ) { // fetch for old versions - $matches = glob( $versionsName.'.v*.d'.$timestamp ); + $matches = glob( $versionsName.'.v*.d'.$timestamp ); + $offset = -strlen($timestamp)-2; + } else { + $matches = glob( $versionsName.'.v*' ); + } foreach( $matches as $ma ) { - $parts = explode( '.v', substr($ma, 0, -strlen($timestamp)-2) ); - $versions[] = ( end( $parts ) ); + if ( $timestamp ) { + $parts = explode( '.v', substr($ma, 0, $offset) ); + $versions[] = ( end( $parts ) ); + } else { + $parts = explode( '.v', $ma ); + $versions[] = ( end( $parts ) ); + } } return $versions; } diff --git a/apps/files_trashbin/templates/part.list.php b/apps/files_trashbin/templates/part.list.php index 72359da299..6faddcf485 100644 --- a/apps/files_trashbin/templates/part.list.php +++ b/apps/files_trashbin/templates/part.list.php @@ -24,13 +24,21 @@ $name = str_replace('%2F', '/', $name); $directory = str_replace('+', '%20', urlencode($file['directory'])); $directory = str_replace('%2F', '/', $directory); ?> - ' + + id="" + data-file="" + data-timestamp='' + data-dirlisting=1 + + id="" + data-file="" data-timestamp='' - data-permissions=''> + data-dirlisting=0 + > style="background-image:url()" From 97e69e537441abe1082bada0c96e8947a14a2083 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Tue, 22 Jan 2013 12:13:50 +0100 Subject: [PATCH 213/272] remove table row in trash bin view after undelete --- apps/files_trashbin/ajax/undelete.php | 2 +- apps/files_trashbin/js/trash.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/files_trashbin/ajax/undelete.php b/apps/files_trashbin/ajax/undelete.php index 5aa6436ae5..178d85ccf3 100644 --- a/apps/files_trashbin/ajax/undelete.php +++ b/apps/files_trashbin/ajax/undelete.php @@ -25,7 +25,7 @@ foreach ($list as $file) { if ( !OCA_Trash\Trashbin::restore($file, $filename, $timestamp) ) { $error[] = $filename; } else { - $success[$i]['filename'] = $filename; + $success[$i]['filename'] = $file; $success[$i]['timestamp'] = $timestamp; $i++; } diff --git a/apps/files_trashbin/js/trash.js b/apps/files_trashbin/js/trash.js index 355e25d97d..92a431d495 100644 --- a/apps/files_trashbin/js/trash.js +++ b/apps/files_trashbin/js/trash.js @@ -8,7 +8,7 @@ $(document).ready(function() { {files:tr.attr('data-file'), dirlisting:tr.attr('data-dirlisting') }, function(result){ for (var i = 0; i < result.data.success.length; i++) { - var row = document.getElementById(result.data.success[i].filename+'.d'+result.data.success[i].timestamp); + var row = document.getElementById(result.data.success[i].filename); row.parentNode.removeChild(row); } if (result.status != 'success') { @@ -69,7 +69,7 @@ $(document).ready(function() { {files:fileslist, dirlisting:dirlisting}, function(result){ for (var i = 0; i < result.data.success.length; i++) { - var row = document.getElementById(result.data.success[i].filename+'.d'+result.data.success[i].timestamp); + var row = document.getElementById(result.data.success[i].filename); row.parentNode.removeChild(row); } if (result.status != 'success') { From 16d7716dee8c3b589a7365d7fc1b7917186c2dfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Tue, 22 Jan 2013 12:30:12 +0100 Subject: [PATCH 214/272] calculate correct relative date for trashbin file list --- apps/files_trashbin/index.php | 1 - apps/files_trashbin/templates/part.list.php | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/files_trashbin/index.php b/apps/files_trashbin/index.php index 97bb64c626..d415ef263e 100644 --- a/apps/files_trashbin/index.php +++ b/apps/files_trashbin/index.php @@ -72,7 +72,6 @@ $pathtohere = ''; foreach (explode('/', $dir) as $i) { if ($i != '') { if ( preg_match('/^(.+)\.d[0-9]+$/', $i, $match) ) { - error_log("match!"); $name = $match[1]; } else { $name = $i; diff --git a/apps/files_trashbin/templates/part.list.php b/apps/files_trashbin/templates/part.list.php index 6faddcf485..205e11c286 100644 --- a/apps/files_trashbin/templates/part.list.php +++ b/apps/files_trashbin/templates/part.list.php @@ -16,7 +16,7 @@ // the bigger the file, the darker the shade of grey; megabytes*2 $simple_size_color = intval(200-$file['size']/(1024*1024)*2); if($simple_size_color<0) $simple_size_color = 0; - $relative_deleted_date = OCP\relative_modified_date($file['mtime']); + $relative_deleted_date = OCP\relative_modified_date($file['timestamp']); // the older the file, the brighter the shade of grey; days*14 $relative_date_color = round((time()-$file['mtime'])/60/60/24*14); if($relative_date_color>200) $relative_date_color = 200; From b694f996d96d4abdb261977d6162d21963c5ee1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Tue, 22 Jan 2013 13:19:41 +0100 Subject: [PATCH 215/272] allow admin to change retention obligation for the trash bin, default value is 180 days --- apps/files_trashbin/lib/trash.php | 7 ++++--- config/config.sample.php | 3 +++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/apps/files_trashbin/lib/trash.php b/apps/files_trashbin/lib/trash.php index 9b891e773b..0dad059022 100644 --- a/apps/files_trashbin/lib/trash.php +++ b/apps/files_trashbin/lib/trash.php @@ -24,8 +24,7 @@ namespace OCA_Trash; class Trashbin { - const DELETEAFTER=30; // how long do we keep files in the trash bin (number of days) - + const DEFAULT_RETENTION_OBLIGATION=180; // how long do we keep files in the trash bin if no other value is defined in the config file (unit: days) /** * move file to the trash bin * @@ -148,7 +147,9 @@ class Trashbin { $query = \OC_DB::prepare('SELECT location,type,id,timestamp FROM *PREFIX*files_trash WHERE user=?'); $result = $query->execute(array($user))->fetchAll(); - $limit = time() - (self::DELETEAFTER * 86400); + $retention_obligation = \OC_Config::getValue('trashbin_retention_obligation', self::DEFAULT_RETENTION_OBLIGATION); + + $limit = time() - ($retention_obligation * 86400); foreach ( $result as $r ) { $timestamp = $r['timestamp']; diff --git a/config/config.sample.php b/config/config.sample.php index dafb536fa6..4398e36060 100644 --- a/config/config.sample.php +++ b/config/config.sample.php @@ -86,6 +86,9 @@ $CONFIG = array( /* Password to use for sendmail mail, depends on mail_smtpauth if this is used */ "mail_smtppassword" => "", +/* how long should ownCloud keep delteted files in the trash bin, default value: 180 days */ +'trashbin_retention_obligation' => 180, + /* Check 3rdparty apps for malicious code fragments */ "appcodechecker" => "", From db2b485b9681b93200bcca6b5780b78008f9c1de Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 22 Jan 2013 16:36:03 +0100 Subject: [PATCH 216/272] Cache: fix test case Updater::testRename --- tests/lib/files/cache/updater.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/lib/files/cache/updater.php b/tests/lib/files/cache/updater.php index 8bf395698a..b4f373cdc2 100644 --- a/tests/lib/files/cache/updater.php +++ b/tests/lib/files/cache/updater.php @@ -136,7 +136,7 @@ class Updater extends \PHPUnit_Framework_TestCase { Filesystem::rename('foo.txt', 'bar.txt'); $this->assertFalse($this->cache->inCache('foo.txt')); $this->assertTrue($this->cache->inCache('bar.txt')); - $cachedData = $this->cache->get('foo.txt'); + $cachedData = $this->cache->get('bar.txt'); $this->assertNotEquals($fooCachedData['etag'], $cachedData['etag']); $mtime = $cachedData['mtime']; $cachedData = $this->cache->get(''); From 16ae902e3156d782fd8f26e59cb6b2da32346ddd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Tue, 22 Jan 2013 17:43:46 +0100 Subject: [PATCH 217/272] no undo notification for deleted file, we use the trash bin instead --- apps/files/js/filelist.js | 69 +++++++++------------------------------ 1 file changed, 15 insertions(+), 54 deletions(-) diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 66697bbbf5..a9e8824964 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -277,63 +277,24 @@ var FileList={ if (FileList.lastAction) { FileList.lastAction(); } - - FileList.prepareDeletion(files); - - if (!FileList.useUndo) { - FileList.lastAction(); - } else { - // NOTE: Temporary fix to change the text to unshared for files in root of Shared folder - if ($('#dir').val() == '/Shared') { - $('#notification').html(t('files', 'unshared {files}', {'files': escapeHTML(files)})+''+t('files', 'undo')+''); - } else { - $('#notification').html(t('files', 'deleted {files}', {'files': escapeHTML(files)})+''+t('files', 'undo')+''); - } - $('#notification').fadeIn(); - } - }, - finishDelete:function(ready,sync){ - if(!FileList.deleteCanceled && FileList.deleteFiles){ - var fileNames=JSON.stringify(FileList.deleteFiles); - $.ajax({ - url: OC.filePath('files', 'ajax', 'delete.php'), - async:!sync, - type:'post', - data: {dir:$('#dir').val(),files:fileNames}, - complete: function(data){ - boolOperationFinished(data, function(){ - $('#notification').fadeOut('400'); - $.each(FileList.deleteFiles,function(index,file){ - FileList.remove(file); - }); - FileList.deleteCanceled=true; - FileList.deleteFiles=null; - FileList.lastAction = null; - if(ready){ - ready(); - } - }); - } - }); - } - }, - prepareDeletion:function(files){ if(files.substr){ files=[files]; } - $.each(files,function(index,file){ - var files = $('tr').filterAttr('data-file',file); - files.hide(); - files.find('input[type="checkbox"]').removeAttr('checked'); - files.removeClass('selected'); - }); - procesSelection(); - FileList.deleteCanceled=false; - FileList.deleteFiles=files; - FileList.lastAction = function() { - FileList.finishDelete(null, true); - }; - } + var fileNames = JSON.stringify(files); + $.post(OC.filePath('files', 'ajax', 'delete.php'), + {dir:$('#dir').val(),files:fileNames}, + function(result){ + if (result.status == 'success') { + $.each(files,function(index,file){ + var files = $('tr').filterAttr('data-file',file); + files.hide(); + files.find('input[type="checkbox"]').removeAttr('checked'); + files.removeClass('selected'); + }); + procesSelection(); + } + }); + }, }; $(document).ready(function(){ From 039bc9159793a0275e5f409f3040043190d59ec9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Tue, 22 Jan 2013 17:46:35 +0100 Subject: [PATCH 218/272] fix typo in comment --- config/config.sample.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/config.sample.php b/config/config.sample.php index 4398e36060..a02116aa4b 100644 --- a/config/config.sample.php +++ b/config/config.sample.php @@ -86,7 +86,7 @@ $CONFIG = array( /* Password to use for sendmail mail, depends on mail_smtpauth if this is used */ "mail_smtppassword" => "", -/* how long should ownCloud keep delteted files in the trash bin, default value: 180 days */ +/* How long should ownCloud keep deleted files in the trash bin, default value: 180 days */ 'trashbin_retention_obligation' => 180, /* Check 3rdparty apps for malicious code fragments */ From f919230f63aade2df7b12a9c9ab28c7cd59868d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Tue, 22 Jan 2013 17:59:33 +0100 Subject: [PATCH 219/272] remove inline JavaScript --- apps/files_trashbin/js/part.list.php | 19 +++++++++++++++++++ apps/files_trashbin/templates/part.list.php | 13 +------------ 2 files changed, 20 insertions(+), 12 deletions(-) create mode 100644 apps/files_trashbin/js/part.list.php diff --git a/apps/files_trashbin/js/part.list.php b/apps/files_trashbin/js/part.list.php new file mode 100644 index 0000000000..c4ecd1ca95 --- /dev/null +++ b/apps/files_trashbin/js/part.list.php @@ -0,0 +1,19 @@ + +* This file is licensed under the Affero General Public License version 3 or +* later. +* See the COPYING-README file. +*/ + +// Set the content type to Javascript +header("Content-type: text/javascript"); + +// Disallow caching +header("Cache-Control: no-cache, must-revalidate"); +header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); + +echo " +var disableDownloadActions = true; +var disableSharing = true; +"; diff --git a/apps/files_trashbin/templates/part.list.php b/apps/files_trashbin/templates/part.list.php index 205e11c286..c7783e8a4f 100644 --- a/apps/files_trashbin/templates/part.list.php +++ b/apps/files_trashbin/templates/part.list.php @@ -1,15 +1,4 @@ - + Date: Tue, 22 Jan 2013 20:57:15 +0100 Subject: [PATCH 220/272] Cache: make mounts overwrite existing folders, prevents duplicate entries --- lib/files/view.php | 8 ++++++++ tests/lib/files/view.php | 12 ++++++++++++ 2 files changed, 20 insertions(+) diff --git a/lib/files/view.php b/lib/files/view.php index 0602791012..c50897603e 100644 --- a/lib/files/view.php +++ b/lib/files/view.php @@ -794,6 +794,14 @@ class View { $subPermissionsCache->set($rootEntry['fileid'], $user, $permissions); } $rootEntry['permissions'] = $permissions; + + //remove any existing entry with the same name + foreach ($files as $i => $file) { + if ($file['name'] === $rootEntry['name']) { + unset($files[$i]); + break; + } + } $files[] = $rootEntry; } } diff --git a/tests/lib/files/view.php b/tests/lib/files/view.php index 5327114267..586ad17842 100644 --- a/tests/lib/files/view.php +++ b/tests/lib/files/view.php @@ -98,6 +98,18 @@ class View extends \PHPUnit_Framework_TestCase { $this->assertEquals(array(), $rootView->getDirectoryContent('/non/existing')); } + function testMountPointOverwrite() { + $storage1 = $this->getTestStorage(false); + $storage2 = $this->getTestStorage(); + $storage1->mkdir('substorage'); + \OC\Files\Filesystem::mount($storage1, array(), '/'); + \OC\Files\Filesystem::mount($storage2, array(), '/substorage'); + + $rootView = new \OC\Files\View(''); + $folderContent = $rootView->getDirectoryContent('/'); + $this->assertEquals(4, count($folderContent)); + } + function testCacheIncompleteFolder() { $storage1 = $this->getTestStorage(false); \OC\Files\Filesystem::mount($storage1, array(), '/'); From 9a1f65aab176604c90114891b4bc0346fbb7d561 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 22 Jan 2013 20:58:09 +0100 Subject: [PATCH 221/272] Some style fixes --- lib/files/filesystem.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/files/filesystem.php b/lib/files/filesystem.php index 28e8b04689..9a3d13e343 100644 --- a/lib/files/filesystem.php +++ b/lib/files/filesystem.php @@ -231,16 +231,16 @@ class Filesystem { } /** - * Initialize system and personal mount points for a user - * - * @param string $user - */ + * Initialize system and personal mount points for a user + * + * @param string $user + */ public static function initMountPoints($user = '') { if ($user == '') { $user = \OC_User::getUser(); } // Load system mount points - if (is_file(\OC::$SERVERROOT.'/config/mount.php')) { + if (is_file(\OC::$SERVERROOT . '/config/mount.php')) { $mountConfig = include 'config/mount.php'; if (isset($mountConfig['global'])) { foreach ($mountConfig['global'] as $mountPoint => $options) { @@ -277,8 +277,8 @@ class Filesystem { // Load personal mount points $root = \OC_User::getHome($user); self::mount('\OC\Files\Storage\Local', array('datadir' => $root), $user); - if (is_file($root.'/mount.php')) { - $mountConfig = include $root.'/mount.php'; + if (is_file($root . '/mount.php')) { + $mountConfig = include $root . '/mount.php'; if (isset($mountConfig['user'][$user])) { foreach ($mountConfig['user'][$user] as $mountPoint => $options) { self::mount($options['class'], $options['options'], $mountPoint); @@ -370,7 +370,7 @@ class Filesystem { } else { // Update old classes to new namespace if (strpos($class, 'OC_Filestorage_') !== false) { - $class = '\OC\Files\Storage\\'.substr($class, 15); + $class = '\OC\Files\Storage\\' . substr($class, 15); } self::$mounts[$mountpoint] = array('class' => $class, 'arguments' => $arguments); } @@ -663,7 +663,7 @@ class Filesystem { * @param string $path * @return string */ - static public function getETag($path){ + static public function getETag($path) { return self::$defaultInstance->getETag($path); } } From 6ab95e8e33e5dee48291bb3f95a86af0be8c3e70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Wed, 23 Jan 2013 11:30:42 +0100 Subject: [PATCH 222/272] code clean-up --- apps/files_trashbin/index.php | 1 + .../js/disableDefaultActions.js | 3 +++ apps/files_trashbin/js/part.list.php | 19 ------------------- apps/files_trashbin/templates/part.list.php | 2 -- 4 files changed, 4 insertions(+), 21 deletions(-) create mode 100644 apps/files_trashbin/js/disableDefaultActions.js delete mode 100644 apps/files_trashbin/js/part.list.php diff --git a/apps/files_trashbin/index.php b/apps/files_trashbin/index.php index d415ef263e..20f9cb8daf 100644 --- a/apps/files_trashbin/index.php +++ b/apps/files_trashbin/index.php @@ -5,6 +5,7 @@ OCP\User::checkLoggedIn(); OCP\Util::addStyle('files_trashbin', 'trash'); OCP\Util::addScript('files_trashbin', 'trash'); +OCP\Util::addScript('files_trashbin', 'disableDefaultActions'); OCP\Util::addScript('files', 'fileactions'); $tmpl = new OCP\Template('files_trashbin', 'index', 'user'); diff --git a/apps/files_trashbin/js/disableDefaultActions.js b/apps/files_trashbin/js/disableDefaultActions.js new file mode 100644 index 0000000000..56b95407dd --- /dev/null +++ b/apps/files_trashbin/js/disableDefaultActions.js @@ -0,0 +1,3 @@ +/* disable download and sharing actions */ +var disableDownloadActions = true; +var disableSharing = true; diff --git a/apps/files_trashbin/js/part.list.php b/apps/files_trashbin/js/part.list.php deleted file mode 100644 index c4ecd1ca95..0000000000 --- a/apps/files_trashbin/js/part.list.php +++ /dev/null @@ -1,19 +0,0 @@ - -* This file is licensed under the Affero General Public License version 3 or -* later. -* See the COPYING-README file. -*/ - -// Set the content type to Javascript -header("Content-type: text/javascript"); - -// Disallow caching -header("Cache-Control: no-cache, must-revalidate"); -header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); - -echo " -var disableDownloadActions = true; -var disableSharing = true; -"; diff --git a/apps/files_trashbin/templates/part.list.php b/apps/files_trashbin/templates/part.list.php index c7783e8a4f..7d2f09c62c 100644 --- a/apps/files_trashbin/templates/part.list.php +++ b/apps/files_trashbin/templates/part.list.php @@ -1,5 +1,3 @@ - - Date: Thu, 24 Jan 2013 15:29:22 +0100 Subject: [PATCH 223/272] restore file in the root folder if the origin folder is not writable --- apps/files_trashbin/lib/trash.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/files_trashbin/lib/trash.php b/apps/files_trashbin/lib/trash.php index 0dad059022..e81ba12758 100644 --- a/apps/files_trashbin/lib/trash.php +++ b/apps/files_trashbin/lib/trash.php @@ -91,7 +91,9 @@ class Trashbin { // if location no longer exists, restore file in the root directory $location = $result[0]['location']; - if ( $result[0]['location'] != '/' && !$view->is_dir('files'.$result[0]['location']) ) { + if ( $result[0]['location'] != '/' && + (!$view->is_dir('files'.$result[0]['location']) || + !$view->is_writable('files'.$result[0]['location'])) ) { $location = ''; } } else { From 7237ebf08b5a20fe6072a99be33f3520d4bf3871 Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Sat, 26 Jan 2013 15:16:53 -0500 Subject: [PATCH 224/272] Fix OC_Helper and change deprecated methods --- lib/helper.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/helper.php b/lib/helper.php index 1dd11cce04..c2a0d83f6b 100644 --- a/lib/helper.php +++ b/lib/helper.php @@ -760,7 +760,7 @@ class OC_Helper { $post_max_size = OCP\Util::computerFileSize(ini_get('post_max_size')); $maxUploadFilesize = min($upload_max_filesize, $post_max_size); - $freeSpace = OC_Filesystem::free_space($dir); + $freeSpace = \OC\Files\Filesystem::free_space($dir); $freeSpace = max($freeSpace, 0); return min($maxUploadFilesize, $freeSpace); @@ -790,12 +790,12 @@ class OC_Helper { * Calculate the disc space */ public static function getStorageInfo() { - $rootInfo = OC_FileCache::get(''); + $rootInfo = \OC\Files\Filesystem::getFileInfo('/'); $used = $rootInfo['size']; if ($used < 0) { $used = 0; } - $free = OC_Filesystem::free_space(); + $free = \OC\Files\Filesystem::free_space(); $total = $free + $used; if ($total == 0) { $total = 1; // prevent division by zero From 177537dbccac7c30ab42dfee56e92974e5cdf042 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sat, 26 Jan 2013 21:41:14 +0100 Subject: [PATCH 225/272] Cache: port test cases to PHPUnit --- tests/lib/files/cache/cache.php | 42 ++++++++++++------------- tests/lib/files/cache/scanner.php | 52 +++++++++++++++---------------- 2 files changed, 47 insertions(+), 47 deletions(-) diff --git a/tests/lib/files/cache/cache.php b/tests/lib/files/cache/cache.php index a2b131ac0a..96a7623dac 100644 --- a/tests/lib/files/cache/cache.php +++ b/tests/lib/files/cache/cache.php @@ -8,7 +8,7 @@ namespace Test\Files\Cache; -class Cache extends \UnitTestCase { +class Cache extends \PHPUnit_Framework_TestCase { /** * @var \OC\Files\Storage\Temporary $storage; */ @@ -26,56 +26,56 @@ class Cache extends \UnitTestCase { $data2 = array('size' => 1000, 'mtime' => 20, 'mimetype' => 'foo/file'); $this->assertFalse($this->cache->inCache($file1)); - $this->assertEqual($this->cache->get($file1), null); + $this->assertEquals($this->cache->get($file1), null); $id1 = $this->cache->put($file1, $data1); $this->assertTrue($this->cache->inCache($file1)); $cacheData1 = $this->cache->get($file1); foreach ($data1 as $key => $value) { - $this->assertEqual($value, $cacheData1[$key]); + $this->assertEquals($value, $cacheData1[$key]); } - $this->assertEqual($cacheData1['mimepart'], 'foo'); - $this->assertEqual($cacheData1['fileid'], $id1); - $this->assertEqual($id1, $this->cache->getId($file1)); + $this->assertEquals($cacheData1['mimepart'], 'foo'); + $this->assertEquals($cacheData1['fileid'], $id1); + $this->assertEquals($id1, $this->cache->getId($file1)); $this->assertFalse($this->cache->inCache($file2)); $id2 = $this->cache->put($file2, $data2); $this->assertTrue($this->cache->inCache($file2)); $cacheData2 = $this->cache->get($file2); foreach ($data2 as $key => $value) { - $this->assertEqual($value, $cacheData2[$key]); + $this->assertEquals($value, $cacheData2[$key]); } - $this->assertEqual($cacheData1['fileid'], $cacheData2['parent']); - $this->assertEqual($cacheData2['fileid'], $id2); - $this->assertEqual($id2, $this->cache->getId($file2)); - $this->assertEqual($id1, $this->cache->getParentId($file2)); + $this->assertEquals($cacheData1['fileid'], $cacheData2['parent']); + $this->assertEquals($cacheData2['fileid'], $id2); + $this->assertEquals($id2, $this->cache->getId($file2)); + $this->assertEquals($id1, $this->cache->getParentId($file2)); $newSize = 1050; $newId2 = $this->cache->put($file2, array('size' => $newSize)); $cacheData2 = $this->cache->get($file2); - $this->assertEqual($newId2, $id2); - $this->assertEqual($cacheData2['size'], $newSize); - $this->assertEqual($cacheData1, $this->cache->get($file1)); + $this->assertEquals($newId2, $id2); + $this->assertEquals($cacheData2['size'], $newSize); + $this->assertEquals($cacheData1, $this->cache->get($file1)); $this->cache->remove($file2); $this->assertFalse($this->cache->inCache($file2)); - $this->assertEqual($this->cache->get($file2), null); + $this->assertEquals($this->cache->get($file2), null); $this->assertTrue($this->cache->inCache($file1)); - $this->assertEqual($cacheData1, $this->cache->get($id1)); + $this->assertEquals($cacheData1, $this->cache->get($id1)); } public function testPartial() { $file1 = 'foo'; $this->cache->put($file1, array('size' => 10)); - $this->assertEqual(array('size' => 10), $this->cache->get($file1)); + $this->assertEquals(array('size' => 10), $this->cache->get($file1)); $this->cache->put($file1, array('mtime' => 15)); - $this->assertEqual(array('size' => 10, 'mtime' => 15), $this->cache->get($file1)); + $this->assertEquals(array('size' => 10, 'mtime' => 15), $this->cache->get($file1)); $this->cache->put($file1, array('size' => 12)); - $this->assertEqual(array('size' => 12, 'mtime' => 15), $this->cache->get($file1)); + $this->assertEquals(array('size' => 12, 'mtime' => 15), $this->cache->get($file1)); } public function testFolder() { @@ -92,11 +92,11 @@ class Cache extends \UnitTestCase { $this->cache->put($file3, $fileData['foo']); $content = $this->cache->getFolderContents($file1); - $this->assertEqual(count($content), 2); + $this->assertEquals(count($content), 2); foreach ($content as $cachedData) { $data = $fileData[$cachedData['name']]; foreach ($data as $name => $value) { - $this->assertEqual($value, $cachedData[$name]); + $this->assertEquals($value, $cachedData[$name]); } } diff --git a/tests/lib/files/cache/scanner.php b/tests/lib/files/cache/scanner.php index 6d26150d82..3885c99e6d 100644 --- a/tests/lib/files/cache/scanner.php +++ b/tests/lib/files/cache/scanner.php @@ -8,7 +8,7 @@ namespace Test\Files\Cache; -class Scanner extends \UnitTestCase { +class Scanner extends \PHPUnit_Framework_TestCase { /** * @var \OC\Files\Storage\Storage $storage */ @@ -29,20 +29,20 @@ class Scanner extends \UnitTestCase { $this->storage->file_put_contents('foo.txt', $data); $this->scanner->scanFile('foo.txt'); - $this->assertEqual($this->cache->inCache('foo.txt'), true); + $this->assertEquals($this->cache->inCache('foo.txt'), true); $cachedData = $this->cache->get('foo.txt'); - $this->assertEqual($cachedData['size'], strlen($data)); - $this->assertEqual($cachedData['mimetype'], 'text/plain'); - $this->assertNotEqual($cachedData['parent'], -1); //parent folders should be scanned automatically + $this->assertEquals($cachedData['size'], strlen($data)); + $this->assertEquals($cachedData['mimetype'], 'text/plain'); + $this->assertNotEquals($cachedData['parent'], -1); //parent folders should be scanned automatically $data = file_get_contents(\OC::$SERVERROOT . '/core/img/logo.png'); $this->storage->file_put_contents('foo.png', $data); $this->scanner->scanFile('foo.png'); - $this->assertEqual($this->cache->inCache('foo.png'), true); + $this->assertEquals($this->cache->inCache('foo.png'), true); $cachedData = $this->cache->get('foo.png'); - $this->assertEqual($cachedData['size'], strlen($data)); - $this->assertEqual($cachedData['mimetype'], 'image/png'); + $this->assertEquals($cachedData['size'], strlen($data)); + $this->assertEquals($cachedData['mimetype'], 'image/png'); } private function fillTestFolders() { @@ -58,11 +58,11 @@ class Scanner extends \UnitTestCase { $this->fillTestFolders(); $this->scanner->scan(''); - $this->assertEqual($this->cache->inCache(''), true); - $this->assertEqual($this->cache->inCache('foo.txt'), true); - $this->assertEqual($this->cache->inCache('foo.png'), true); - $this->assertEqual($this->cache->inCache('folder'), true); - $this->assertEqual($this->cache->inCache('folder/bar.txt'), true); + $this->assertEquals($this->cache->inCache(''), true); + $this->assertEquals($this->cache->inCache('foo.txt'), true); + $this->assertEquals($this->cache->inCache('foo.png'), true); + $this->assertEquals($this->cache->inCache('folder'), true); + $this->assertEquals($this->cache->inCache('folder/bar.txt'), true); $cachedDataText = $this->cache->get('foo.txt'); $cachedDataText2 = $this->cache->get('foo.txt'); @@ -70,38 +70,38 @@ class Scanner extends \UnitTestCase { $cachedDataFolder = $this->cache->get(''); $cachedDataFolder2 = $this->cache->get('folder'); - $this->assertEqual($cachedDataImage['parent'], $cachedDataText['parent']); - $this->assertEqual($cachedDataFolder['fileid'], $cachedDataImage['parent']); - $this->assertEqual($cachedDataFolder['size'], $cachedDataImage['size'] + $cachedDataText['size'] + $cachedDataText2['size']); - $this->assertEqual($cachedDataFolder2['size'], $cachedDataText2['size']); + $this->assertEquals($cachedDataImage['parent'], $cachedDataText['parent']); + $this->assertEquals($cachedDataFolder['fileid'], $cachedDataImage['parent']); + $this->assertEquals($cachedDataFolder['size'], $cachedDataImage['size'] + $cachedDataText['size'] + $cachedDataText2['size']); + $this->assertEquals($cachedDataFolder2['size'], $cachedDataText2['size']); } function testShallow() { $this->fillTestFolders(); $this->scanner->scan('', \OC\Files\Cache\Scanner::SCAN_SHALLOW); - $this->assertEqual($this->cache->inCache(''), true); - $this->assertEqual($this->cache->inCache('foo.txt'), true); - $this->assertEqual($this->cache->inCache('foo.png'), true); - $this->assertEqual($this->cache->inCache('folder'), true); - $this->assertEqual($this->cache->inCache('folder/bar.txt'), false); + $this->assertEquals($this->cache->inCache(''), true); + $this->assertEquals($this->cache->inCache('foo.txt'), true); + $this->assertEquals($this->cache->inCache('foo.png'), true); + $this->assertEquals($this->cache->inCache('folder'), true); + $this->assertEquals($this->cache->inCache('folder/bar.txt'), false); $cachedDataFolder = $this->cache->get(''); $cachedDataFolder2 = $this->cache->get('folder'); - $this->assertEqual(-1, $cachedDataFolder['size']); - $this->assertEqual(-1, $cachedDataFolder2['size']); + $this->assertEquals(-1, $cachedDataFolder['size']); + $this->assertEquals(-1, $cachedDataFolder2['size']); $this->scanner->scan('folder', \OC\Files\Cache\Scanner::SCAN_SHALLOW); $cachedDataFolder2 = $this->cache->get('folder'); - $this->assertNotEqual($cachedDataFolder2['size'], -1); + $this->assertNotEquals($cachedDataFolder2['size'], -1); $this->cache->correctFolderSize('folder'); $cachedDataFolder = $this->cache->get(''); - $this->assertNotEqual($cachedDataFolder['size'], -1); + $this->assertNotEquals($cachedDataFolder['size'], -1); } function testBackgroundScan(){ From f25db779d90814f1d54d594caf1bb830ea417b95 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sat, 26 Jan 2013 21:42:59 +0100 Subject: [PATCH 226/272] Filesystem: move mount management to it's own class --- lib/files/filesystem.php | 99 +++++------------------ lib/files/mount.php | 171 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 191 insertions(+), 79 deletions(-) create mode 100644 lib/files/mount.php diff --git a/lib/files/filesystem.php b/lib/files/filesystem.php index 9a3d13e343..048116a44b 100644 --- a/lib/files/filesystem.php +++ b/lib/files/filesystem.php @@ -30,8 +30,6 @@ namespace OC\Files; class Filesystem { - static private $storages = array(); - static private $mounts = array(); public static $loaded = false; /** * @var \OC\Files\View $defaultInstance @@ -143,19 +141,12 @@ class Filesystem { * @return string */ static public function getMountPoint($path) { - $path = self::normalizePath($path) . '/'; - \OC_Hook::emit(self::CLASSNAME, 'get_mountpoint', array('path' => $path)); - $foundMountPoint = ''; - $mountPoints = array_keys(self::$mounts); - foreach ($mountPoints as $mountpoint) { - if ($mountpoint == $path) { - return $mountpoint; - } - if (strpos($path, $mountpoint) === 0 and strlen($mountpoint) > strlen($foundMountPoint)) { - $foundMountPoint = $mountpoint; - } + $mount = Mount::find($path); + if ($mount) { + return $mount->getMountPoint(); + } else { + return ''; } - return $foundMountPoint; } /** @@ -165,18 +156,10 @@ class Filesystem { * @return string[] */ static public function getMountPoints($path) { - $path = self::normalizePath($path); - if (strlen($path) > 1) { - $path .= '/'; - } - $pathLength = strlen($path); - - $mountPoints = array_keys(self::$mounts); $result = array(); - foreach ($mountPoints as $mountPoint) { - if (substr($mountPoint, 0, $pathLength) === $path and strlen($mountPoint) > $pathLength) { - $result[] = $mountPoint; - } + $mounts = Mount::findIn($path); + foreach ($mounts as $mount) { + $result[] = $mount->getMountPoint(); } return $result; } @@ -188,11 +171,8 @@ class Filesystem { * @return \OC\Files\Storage\Storage */ public static function getStorage($mountPoint) { - if (!isset(self::$storages[$mountPoint])) { - $mount = self::$mounts[$mountPoint]; - self::$storages[$mountPoint] = self::createStorage($mount['class'], $mount['arguments']); - } - return self::$storages[$mountPoint]; + $mount = Mount::find($mountPoint); + return $mount->getStorage(); } /** @@ -202,15 +182,9 @@ class Filesystem { * @return array consisting of the storage and the internal path */ static public function resolvePath($path) { - $mountpoint = self::getMountPoint($path); - if ($mountpoint) { - $storage = self::getStorage($mountpoint); - if ($mountpoint === $path or $mountpoint . '/' === $path) { - $internalPath = ''; - } else { - $internalPath = substr($path, strlen($mountpoint)); - } - return array($storage, $internalPath); + $mount = Mount::find($path); + if ($mount) { + return array($mount->getStorage(), $mount->getInternalPath($path)); } else { return array(null, null); } @@ -291,6 +265,7 @@ class Filesystem { * fill in the correct values for $user, and $password placeholders * * @param string $input + * @param string $input * @return string */ private static function setUserVars($user, $input) { @@ -310,28 +285,7 @@ class Filesystem { * tear down the filesystem, removing all storage providers */ static public function tearDown() { - self::$storages = array(); - } - - /** - * create a new storage of a specific type - * - * @param string $type - * @param array $arguments - * @return \OC\Files\Storage\Storage - */ - static private function createStorage($class, $arguments) { - if (class_exists($class)) { - try { - return new $class($arguments); - } catch (\Exception $exception) { - \OC_Log::write('core', $exception->getMessage(), \OC_Log::ERROR); - return false; - } - } else { - \OC_Log::write('core', 'storage backend ' . $class . ' not found', \OC_Log::ERROR); - return false; - } + self::clearMounts(); } /** @@ -348,8 +302,7 @@ class Filesystem { * clear all mounts and storage backends */ public static function clearMounts() { - self::$mounts = array(); - self::$storages = array(); + Mount::clear(); } /** @@ -360,20 +313,7 @@ class Filesystem { * @param string $mountpoint */ static public function mount($class, $arguments, $mountpoint) { - $mountpoint = self::normalizePath($mountpoint); - if (strlen($mountpoint) > 1) { - $mountpoint .= '/'; - } - if ($class instanceof \OC\Files\Storage\Storage) { - self::$mounts[$mountpoint] = array('class' => get_class($class), 'arguments' => $arguments); - self::$storages[$mountpoint] = $class; - } else { - // Update old classes to new namespace - if (strpos($class, 'OC_Filestorage_') !== false) { - $class = '\OC\Files\Storage\\' . substr($class, 15); - } - self::$mounts[$mountpoint] = array('class' => $class, 'arguments' => $arguments); - } + new Mount($class, $mountpoint, $arguments); } /** @@ -578,6 +518,7 @@ class Filesystem { /** * check if a file or folder has been updated since $time * + * @param string $path * @param int $time * @return bool */ @@ -653,8 +594,8 @@ class Filesystem { * @param string $directory path under datadirectory * @return array */ - public static function getDirectoryContent($directory, $mimetype_filter = '') { - return self::$defaultInstance->getDirectoryContent($directory, $mimetype_filter); + public static function getDirectoryContent($directory) { + return self::$defaultInstance->getDirectoryContent($directory); } /** diff --git a/lib/files/mount.php b/lib/files/mount.php new file mode 100644 index 0000000000..c6746d2a33 --- /dev/null +++ b/lib/files/mount.php @@ -0,0 +1,171 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Files; + +class Mount { + static private $mounts = array(); + + /** + * @var \OC\Files\Storage\Storage $storage + */ + private $storage = null; + private $class; + private $storageId; + private $arguments = array(); + private $mountPoint; + + /** + * @param string|\OC\Files\Storage\Storage $storage + * @param string $mountpoint + * @param array $arguments + */ + public function __construct($storage, $mountpoint, $arguments = null) { + if (is_null($arguments)) { + $arguments = array(); + } + + $mountpoint = self::formatPath($mountpoint); + if ($storage instanceof \OC\Files\Storage\Storage) { + $this->class = get_class($storage); + $this->storage = $storage; + } else { + // Update old classes to new namespace + if (strpos($storage, 'OC_Filestorage_') !== false) { + $storage = '\OC\Files\Storage\\' . substr($storage, 15); + } + $this->class = $storage; + $this->arguments = $arguments; + } + $this->mountPoint = $mountpoint; + + self::$mounts[$this->mountPoint] = $this; + } + + /** + * @return string + */ + public function getMountPoint() { + return $this->mountPoint; + } + + /** + * @return \OC\Files\Storage\Storage + */ + private function createStorage() { + if (class_exists($this->class)) { + try { + return new $this->class($this->arguments); + } catch (\Exception $exception) { + \OC_Log::write('core', $exception->getMessage(), \OC_Log::ERROR); + return null; + } + } else { + \OC_Log::write('core', 'storage backend ' . $this->class . ' not found', \OC_Log::ERROR); + return null; + } + } + + /** + * @return \OC\Files\Storage\Storage + */ + public function getStorage() { + if (is_null($this->storage)) { + $this->storage = $this->createStorage(); + } + return $this->storage; + } + + /** + * @return string + */ + public function getStorageId() { + if (!$this->storageId) { + if (is_null($this->storage)) { + $this->storage = $this->createStorage(); + } + $this->storageId = $this->storage->getId(); + } + return $this->storageId; + } + + /** + * @param string $path + * @return string + */ + public function getInternalPath($path) { + if ($this->mountPoint === $path or $this->mountPoint . '/' === $path) { + $internalPath = ''; + } else { + $internalPath = substr($path, strlen($this->mountPoint)); + } + return $internalPath; + } + + /** + * @param string $path + * @return string + */ + private static function formatPath($path) { + $path = Filesystem::normalizePath($path); + if (strlen($path) > 1) { + $path .= '/'; + } + return $path; + } + + /** + * Find the mount for $path + * + * @param $path + * @return Mount + */ + public static function find($path) { + $path = self::formatPath($path); + if (isset(self::$mounts[$path])) { + return self::$mounts[$path]; + } + + \OC_Hook::emit('OC_Filesystem', 'get_mountpoint', array('path' => $path)); + $foundMountPoint = ''; + $mountPoints = array_keys(self::$mounts); + foreach ($mountPoints as $mountpoint) { + if (strpos($path, $mountpoint) === 0 and strlen($mountpoint) > strlen($foundMountPoint)) { + $foundMountPoint = $mountpoint; + } + } + if (isset(self::$mounts[$foundMountPoint])) { + return self::$mounts[$foundMountPoint]; + } else { + return null; + } + } + + /** + * Find all mounts in $path + * + * @param $path + * @return Mount[] + */ + public static function findIn($path) { + $path = self::formatPath($path); + $result = array(); + $pathLength = strlen($path); + $mountPoints = array_keys(self::$mounts); + foreach ($mountPoints as $mountPoint) { + if (substr($mountPoint, 0, $pathLength) === $path and strlen($mountPoint) > $pathLength) { + $result[] = self::$mounts[$mountPoint]; + } + } + return $result; + } + + public static function clear() { + self::$mounts = array(); + } +} From 03b947e3ece28325d3bc64319a228d66d493d182 Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Sat, 26 Jan 2013 16:11:35 -0500 Subject: [PATCH 227/272] Don't check for updates for sub storages, these scans should be done in the background --- lib/files/view.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/files/view.php b/lib/files/view.php index c50897603e..e6b52fb35b 100644 --- a/lib/files/view.php +++ b/lib/files/view.php @@ -769,9 +769,6 @@ class View { if ($subCache->getStatus('') === Cache\Cache::NOT_FOUND) { $subScanner = $subStorage->getScanner(''); $subScanner->scanFile(''); - } else { - $subWatcher = $subStorage->getWatcher(''); - $subWatcher->checkUpdate(''); } $rootEntry = $subCache->get(''); From 69f11151e90f0a17b02e58f7f94ca7f7a521f13a Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sat, 26 Jan 2013 23:49:05 +0100 Subject: [PATCH 228/272] Filesystem: add Mount::findById --- lib/files/mount.php | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/lib/files/mount.php b/lib/files/mount.php index c6746d2a33..e28b6471fe 100644 --- a/lib/files/mount.php +++ b/lib/files/mount.php @@ -9,6 +9,9 @@ namespace OC\Files; class Mount { + /** + * @var Mount[] + */ static private $mounts = array(); /** @@ -23,7 +26,7 @@ class Mount { /** * @param string|\OC\Files\Storage\Storage $storage * @param string $mountpoint - * @param array $arguments + * @param array $arguments (optional) */ public function __construct($storage, $mountpoint, $arguments = null) { if (is_null($arguments)) { @@ -168,4 +171,17 @@ class Mount { public static function clear() { self::$mounts = array(); } + + /** + * @param string $id + * @return \OC\Files\Storage\Storage + */ + public static function findById($id) { + foreach (self::$mounts as $mount) { + if ($mount->getStorageId() === $id) { + return $mount; + } + } + return null; + } } From 8c42e2de8c8be9bd5914794dbf01a10f575788d1 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sat, 26 Jan 2013 23:49:14 +0100 Subject: [PATCH 229/272] Test cases for new mount management --- tests/lib/files/mount.php | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 tests/lib/files/mount.php diff --git a/tests/lib/files/mount.php b/tests/lib/files/mount.php new file mode 100644 index 0000000000..9f16b03627 --- /dev/null +++ b/tests/lib/files/mount.php @@ -0,0 +1,37 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace Test\Files; + +use \OC\Files\Storage\Temporary; + +class Mount extends \PHPUnit_Framework_TestCase { + public function setup() { + \OC_Util::setupFS(); + \OC\Files\Mount::clear(); + } + + public function testFind() { + $this->assertNull(\OC\Files\Mount::find('/')); + + $rootMount = new \OC\Files\Mount(new Temporary(array()), '/'); + $this->assertEquals($rootMount, \OC\Files\Mount::find('/')); + $this->assertEquals($rootMount, \OC\Files\Mount::find('/foo/bar')); + + $mount = new \OC\Files\Mount(new Temporary(array()), '/foo'); + $this->assertEquals($rootMount, \OC\Files\Mount::find('/')); + $this->assertEquals($mount, \OC\Files\Mount::find('/foo/bar')); + + $this->assertEquals(1, count(\OC\Files\Mount::findIn('/'))); + new \OC\Files\Mount(new Temporary(array()), '/bar'); + $this->assertEquals(2, count(\OC\Files\Mount::findIn('/'))); + + $id = $mount->getStorageId(); + $this->assertEquals($mount, \OC\Files\Mount::findById($id)); + } +} From 9e2a066c7bf04dfcc3e2dcc539e1b33053ba70ed Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sat, 26 Jan 2013 23:59:29 +0100 Subject: [PATCH 230/272] Cache: add function to get storage id and internal path of a file by id --- lib/files/cache/cache.php | 24 ++++++++++++++++++++++++ tests/lib/files/cache/cache.php | 7 +++++++ 2 files changed, 31 insertions(+) diff --git a/lib/files/cache/cache.php b/lib/files/cache/cache.php index 7f6d191fee..69cbaea851 100644 --- a/lib/files/cache/cache.php +++ b/lib/files/cache/cache.php @@ -494,4 +494,28 @@ class Cache { return false; } } + + /** + * get the storage id of the storage for a file and the internal path of the file + * + * @return array, first element holding the storage id, second the path + */ + static public function getById($id) { + $query = \OC_DB::prepare('SELECT `storage`, `path` FROM `*PREFIX*filecache` WHERE `fileid` = ?'); + $result = $query->execute(array($id)); + if ($row = $result->fetchRow()) { + $numericId = $row['storage']; + $path = $row['path']; + } else { + return null; + } + + $query = \OC_DB::prepare('SELECT `id` FROM `*PREFIX*storages` WHERE `numeric_id` = ?'); + $result = $query->execute(array($numericId)); + if ($row = $result->fetchRow()) { + return array($row['id'], $path); + } else { + return null; + } + } } diff --git a/tests/lib/files/cache/cache.php b/tests/lib/files/cache/cache.php index 96a7623dac..c466fbb63e 100644 --- a/tests/lib/files/cache/cache.php +++ b/tests/lib/files/cache/cache.php @@ -197,6 +197,13 @@ class Cache extends \PHPUnit_Framework_TestCase { $this->assertEquals(array(), $this->cache->getFolderContents('foo')); } + function testGetById() { + $storageId = $this->storage->getId(); + $data = array('size' => 1000, 'mtime' => 20, 'mimetype' => 'foo/file'); + $id = $this->cache->put('foo', $data); + $this->assertEquals(array($storageId, 'foo'), \OC\Files\Cache\Cache::getById($id)); + } + public function tearDown() { $this->cache->clear(); } From 17bcea15855784f00791ab19984d520a773aa0ca Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sun, 27 Jan 2013 00:13:16 +0100 Subject: [PATCH 231/272] Filesystem: add View->getPath to the cache api --- lib/files/view.php | 15 +++++++++++++++ tests/lib/files/view.php | 23 +++++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/lib/files/view.php b/lib/files/view.php index e6b52fb35b..c2038f222a 100644 --- a/lib/files/view.php +++ b/lib/files/view.php @@ -932,4 +932,19 @@ class View { return null; } } + + /** + * Get the path of a file by id, relative to the view + * + * Note that the resulting path is not guarantied to be unique for the id, multiple paths can point to the same file + * + * @param int $id + * @return string + */ + public function getPath($id) { + list($storage, $internalPath) = Cache\Cache::getById($id); + $mount = Mount::findById($storage); + $fullPath = $mount->getMountPoint() . $internalPath; + return $this->getRelativePath($fullPath); + } } diff --git a/tests/lib/files/view.php b/tests/lib/files/view.php index 586ad17842..a064e44f3e 100644 --- a/tests/lib/files/view.php +++ b/tests/lib/files/view.php @@ -98,6 +98,29 @@ class View extends \PHPUnit_Framework_TestCase { $this->assertEquals(array(), $rootView->getDirectoryContent('/non/existing')); } + function testGetPath() { + $storage1 = $this->getTestStorage(); + $storage2 = $this->getTestStorage(); + $storage3 = $this->getTestStorage(); + \OC\Files\Filesystem::mount($storage1, array(), '/'); + \OC\Files\Filesystem::mount($storage2, array(), '/substorage'); + \OC\Files\Filesystem::mount($storage3, array(), '/folder/anotherstorage'); + + $rootView = new \OC\Files\View(''); + + $cachedData = $rootView->getFileInfo('/foo.txt'); + $id1 = $cachedData['fileid']; + $this->assertEquals('/foo.txt', $rootView->getPath($id1)); + + $cachedData = $rootView->getFileInfo('/substorage/foo.txt'); + $id2 = $cachedData['fileid']; + $this->assertEquals('/substorage/foo.txt', $rootView->getPath($id2)); + + $folderView = new \OC\Files\View('/substorage'); + $this->assertEquals('/foo.txt', $folderView->getPath($id2)); + $this->assertNull($folderView->getPath($id1)); + } + function testMountPointOverwrite() { $storage1 = $this->getTestStorage(false); $storage2 = $this->getTestStorage(); From 4cae1416733019fe4c7465f3e167a456fb790502 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Sun, 27 Jan 2013 00:18:15 +0100 Subject: [PATCH 232/272] Filesystem: also add getPath to \OC\Filesystem --- lib/files/filesystem.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/files/filesystem.php b/lib/files/filesystem.php index 048116a44b..262fde320a 100644 --- a/lib/files/filesystem.php +++ b/lib/files/filesystem.php @@ -598,6 +598,18 @@ class Filesystem { return self::$defaultInstance->getDirectoryContent($directory); } + /** + * Get the path of a file by id + * + * Note that the resulting path is not guarantied to be unique for the id, multiple paths can point to the same file + * + * @param int $id + * @return string + */ + public static function getPath($id) { + return self::$defaultInstance->getPath($id); + } + /** * get the ETag for a file or folder * From 577e3b11d7b486d09334f39215814638704e84b9 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 28 Jan 2013 00:59:43 +0100 Subject: [PATCH 233/272] Filesystem: return all matching mounts in Mount::findById --- lib/files/mount.php | 7 ++++--- tests/lib/files/mount.php | 8 ++++++-- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/lib/files/mount.php b/lib/files/mount.php index e28b6471fe..74ee483b1b 100644 --- a/lib/files/mount.php +++ b/lib/files/mount.php @@ -174,14 +174,15 @@ class Mount { /** * @param string $id - * @return \OC\Files\Storage\Storage + * @return \OC\Files\Storage\Storage[] */ public static function findById($id) { + $result = array(); foreach (self::$mounts as $mount) { if ($mount->getStorageId() === $id) { - return $mount; + $result[] = $mount; } } - return null; + return $result; } } diff --git a/tests/lib/files/mount.php b/tests/lib/files/mount.php index 9f16b03627..f223f0f6c5 100644 --- a/tests/lib/files/mount.php +++ b/tests/lib/files/mount.php @@ -23,7 +23,8 @@ class Mount extends \PHPUnit_Framework_TestCase { $this->assertEquals($rootMount, \OC\Files\Mount::find('/')); $this->assertEquals($rootMount, \OC\Files\Mount::find('/foo/bar')); - $mount = new \OC\Files\Mount(new Temporary(array()), '/foo'); + $storage = new Temporary(array()); + $mount = new \OC\Files\Mount($storage, '/foo'); $this->assertEquals($rootMount, \OC\Files\Mount::find('/')); $this->assertEquals($mount, \OC\Files\Mount::find('/foo/bar')); @@ -32,6 +33,9 @@ class Mount extends \PHPUnit_Framework_TestCase { $this->assertEquals(2, count(\OC\Files\Mount::findIn('/'))); $id = $mount->getStorageId(); - $this->assertEquals($mount, \OC\Files\Mount::findById($id)); + $this->assertEquals(array($mount), \OC\Files\Mount::findById($id)); + + $mount2 = new \OC\Files\Mount($storage, '/foo/bar'); + $this->assertEquals(array($mount, $mount2), \OC\Files\Mount::findById($id)); } } From 3e59a8715390d659b433797c8a0ed080518e5e7d Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 28 Jan 2013 01:02:04 +0100 Subject: [PATCH 234/272] Filesystem: use the updated Mount::findById in getPath to ensure we get the correct path if the storage is also mounted outside the view --- lib/files/view.php | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/files/view.php b/lib/files/view.php index c2038f222a..302232b513 100644 --- a/lib/files/view.php +++ b/lib/files/view.php @@ -943,8 +943,16 @@ class View { */ public function getPath($id) { list($storage, $internalPath) = Cache\Cache::getById($id); - $mount = Mount::findById($storage); - $fullPath = $mount->getMountPoint() . $internalPath; - return $this->getRelativePath($fullPath); + $mounts = Mount::findById($storage); + foreach ($mounts as $mount) { + /** + * @var \OC\Files\Mount $mount + */ + $fullPath = $mount->getMountPoint() . $internalPath; + if (!is_null($path = $this->getRelativePath($fullPath))) { + return $path; + } + } + return null; } } From f28857377dc4a34237626e144aa796b58c133dfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Mon, 28 Jan 2013 11:28:16 +0100 Subject: [PATCH 235/272] use hidden input field to disable sharing --- apps/files_trashbin/templates/part.list.php | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/files_trashbin/templates/part.list.php b/apps/files_trashbin/templates/part.list.php index 7d2f09c62c..fe8a71f44e 100644 --- a/apps/files_trashbin/templates/part.list.php +++ b/apps/files_trashbin/templates/part.list.php @@ -1,3 +1,4 @@ + Date: Mon, 28 Jan 2013 15:10:00 +0100 Subject: [PATCH 236/272] merge conflict fixed --- apps/files/js/filelist.js | 43 --------------------------------------- 1 file changed, 43 deletions(-) diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index afa2923a8c..40d75aae93 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -275,49 +275,6 @@ var FileList={ if (FileList.lastAction) { FileList.lastAction(); } -<<<<<<< HEAD -======= - - FileList.prepareDeletion(files); - - if (!FileList.useUndo) { - FileList.lastAction(); - } else { - // NOTE: Temporary fix to change the text to unshared for files in root of Shared folder - if ($('#dir').val() == '/Shared') { - OC.Notification.showHtml(t('files', 'unshared {files}', {'files': escapeHTML(files)})+''+t('files', 'undo')+''); - } else { - OC.Notification.showHtml(t('files', 'deleted {files}', {'files': escapeHTML(files)})+''+t('files', 'undo')+''); - } - } - }, - finishDelete:function(ready,sync){ - if(!FileList.deleteCanceled && FileList.deleteFiles){ - var fileNames=JSON.stringify(FileList.deleteFiles); - $.ajax({ - url: OC.filePath('files', 'ajax', 'delete.php'), - async:!sync, - type:'post', - data: {dir:$('#dir').val(),files:fileNames}, - complete: function(data){ - boolOperationFinished(data, function(){ - OC.Notification.hide(); - $.each(FileList.deleteFiles,function(index,file){ - FileList.remove(file); - }); - FileList.deleteCanceled=true; - FileList.deleteFiles=null; - FileList.lastAction = null; - if(ready){ - ready(); - } - }); - } - }); - } - }, - prepareDeletion:function(files){ ->>>>>>> origin/master if(files.substr){ files=[files]; } From c9c919da5738f963247307ca3878e06beff87ade Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 28 Jan 2013 15:34:15 +0100 Subject: [PATCH 237/272] Move streamwrappers to seperate files and put them in a namespace --- apps/files_external/lib/amazons3.php | 54 +++---- apps/files_external/lib/dropbox.php | 4 +- apps/files_external/lib/ftp.php | 2 +- apps/files_external/lib/google.php | 4 +- apps/files_external/lib/swift.php | 4 +- apps/files_external/lib/webdav.php | 6 +- apps/files_sharing/lib/sharedstorage.php | 2 +- lib/archive/tar.php | 2 +- lib/archive/zip.php | 2 +- lib/base.php | 7 +- lib/files/stream/close.php | 100 +++++++++++++ lib/files/stream/dir.php | 47 ++++++ .../stream/staticstream.php} | 136 ++---------------- tests/lib/streamwrappers.php | 76 +++++----- 14 files changed, 242 insertions(+), 204 deletions(-) create mode 100644 lib/files/stream/close.php create mode 100644 lib/files/stream/dir.php rename lib/{streamwrappers.php => files/stream/staticstream.php} (57%) diff --git a/apps/files_external/lib/amazons3.php b/apps/files_external/lib/amazons3.php index a209f0d050..494885a1dd 100644 --- a/apps/files_external/lib/amazons3.php +++ b/apps/files_external/lib/amazons3.php @@ -1,24 +1,24 @@ . -*/ + * ownCloud + * + * @author Michael Gapczynski + * @copyright 2012 Michael Gapczynski mtgap@owncloud.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see . + */ namespace OC\Files\Storage; @@ -36,7 +36,7 @@ class AmazonS3 extends \OC\Files\Storage\Common { // TODO options: storage class, encryption server side, encrypt before upload? public function __construct($params) { - $this->id = 'amazon::'.$params['key'] . md5($params['secret']); + $this->id = 'amazon::' . $params['key'] . md5($params['secret']); $this->s3 = new \AmazonS3(array('key' => $params['key'], 'secret' => $params['secret'])); $this->bucket = $params['bucket']; } @@ -51,7 +51,7 @@ class AmazonS3 extends \OC\Files\Storage\Common { return $response; // This object could be a folder, a '/' must be at the end of the path } else if (substr($path, -1) != '/') { - $response = $this->s3->get_object_metadata($this->bucket, $path.'/'); + $response = $this->s3->get_object_metadata($this->bucket, $path . '/'); if ($response) { $this->objects[$path] = $response; return $response; @@ -61,7 +61,7 @@ class AmazonS3 extends \OC\Files\Storage\Common { return false; } - public function getId(){ + public function getId() { return $this->id; } @@ -104,8 +104,8 @@ class AmazonS3 extends \OC\Files\Storage\Common { foreach ($response->body->CommonPrefixes as $object) { $files[] = basename($object->Prefix); } - \OC_FakeDirStream::$dirs['amazons3'.$path] = $files; - return opendir('fakedir://amazons3'.$path); + \OC\Files\Stream\Dir::register('amazons3' . $path, $files); + return opendir('fakedir://amazons3' . $path); } return false; } @@ -194,13 +194,13 @@ class AmazonS3 extends \OC\Files\Storage\Common { $ext = ''; } $tmpFile = \OC_Helper::tmpFile($ext); - \OC_CloseStreamWrapper::$callBacks[$tmpFile] = array($this, 'writeBack'); + \OC\Files\Stream\Close::registerCallback($tmpFile, array($this, 'writeBack')); if ($this->file_exists($path)) { $source = $this->fopen($path, 'r'); file_put_contents($tmpFile, $source); } self::$tempFiles[$tmpFile] = $path; - return fopen('close://'.$tmpFile, $mode); + return fopen('close://' . $tmpFile, $mode); } return false; } @@ -209,8 +209,8 @@ class AmazonS3 extends \OC\Files\Storage\Common { if (isset(self::$tempFiles[$tmpFile])) { $handle = fopen($tmpFile, 'r'); $response = $this->s3->create_object($this->bucket, - self::$tempFiles[$tmpFile], - array('fileUpload' => $handle)); + self::$tempFiles[$tmpFile], + array('fileUpload' => $handle)); if ($response->isOK()) { unlink($tmpFile); } diff --git a/apps/files_external/lib/dropbox.php b/apps/files_external/lib/dropbox.php index 9223b4ca87..11644e4a2c 100755 --- a/apps/files_external/lib/dropbox.php +++ b/apps/files_external/lib/dropbox.php @@ -114,7 +114,7 @@ class Dropbox extends \OC\Files\Storage\Common { foreach ($contents as $file) { $files[] = basename($file['path']); } - \OC_FakeDirStream::$dirs['dropbox'.$path] = $files; + \OC\Files\Stream\Dir::register('dropbox'.$path, $files); return opendir('fakedir://dropbox'.$path); } return false; @@ -232,7 +232,7 @@ class Dropbox extends \OC\Files\Storage\Common { $ext = ''; } $tmpFile = \OC_Helper::tmpFile($ext); - \OC_CloseStreamWrapper::$callBacks[$tmpFile] = array($this, 'writeBack'); + \OC\Files\Stream\Close::registerCallback($tmpFile, array($this, 'writeBack')); if ($this->file_exists($path)) { $source = $this->fopen($path, 'r'); file_put_contents($tmpFile, $source); diff --git a/apps/files_external/lib/ftp.php b/apps/files_external/lib/ftp.php index 973b33fbfd..9a27b63323 100644 --- a/apps/files_external/lib/ftp.php +++ b/apps/files_external/lib/ftp.php @@ -84,7 +84,7 @@ class FTP extends \OC\Files\Storage\StreamWrapper{ $ext=''; } $tmpFile=OCP\Files::tmpFile($ext); - OC_CloseStreamWrapper::$callBacks[$tmpFile]=array($this, 'writeBack'); + \OC\Files\Stream\Close::registerCallback($tmpFile, array($this, 'writeBack')); if ($this->file_exists($path)) { $this->getFile($path, $tmpFile); } diff --git a/apps/files_external/lib/google.php b/apps/files_external/lib/google.php index 4c485773e7..7396c7e3f2 100644 --- a/apps/files_external/lib/google.php +++ b/apps/files_external/lib/google.php @@ -273,7 +273,7 @@ class Google extends \OC\Files\Storage\Common { $this->entries[$name] = $entry; } } - \OC_FakeDirStream::$dirs['google'.$path] = $files; + \OC\Files\Stream\Dir::register('google'.$path, $files); return opendir('fakedir://google'.$path); } @@ -450,7 +450,7 @@ class Google extends \OC\Files\Storage\Common { $ext = ''; } $tmpFile = \OC_Helper::tmpFile($ext); - \OC_CloseStreamWrapper::$callBacks[$tmpFile] = array($this, 'writeBack'); + \OC\Files\Stream\Close::registerCallback($tmpFile, array($this, 'writeBack')); if ($this->file_exists($path)) { $source = $this->fopen($path, 'r'); file_put_contents($tmpFile, $source); diff --git a/apps/files_external/lib/swift.php b/apps/files_external/lib/swift.php index bd1f4c572a..cbf2007052 100644 --- a/apps/files_external/lib/swift.php +++ b/apps/files_external/lib/swift.php @@ -371,7 +371,7 @@ class SWIFT extends \OC\Files\Storage\Common{ $subContainers=$this->getSubContainers($container); $files=array_merge($files, $subContainers); $id=$this->getContainerName($path); - \OC_FakeDirStream::$dirs[$id]=$files; + \OC\Files\Stream\Dir::register($id, $files); return opendir('fakedir://'.$id); } @@ -465,7 +465,7 @@ class SWIFT extends \OC\Files\Storage\Common{ case 'c': case 'c+': $tmpFile=$this->getTmpFile($path); - OC_CloseStreamWrapper::$callBacks[$tmpFile]=array($this, 'writeBack'); + \OC\Files\Stream\Close::registerCallback($tmpFile, array($this, 'writeBack')); self::$tempFiles[$tmpFile]=$path; return fopen('close://'.$tmpFile, $mode); } diff --git a/apps/files_external/lib/webdav.php b/apps/files_external/lib/webdav.php index 6d43a03058..571b4d79ae 100644 --- a/apps/files_external/lib/webdav.php +++ b/apps/files_external/lib/webdav.php @@ -104,13 +104,15 @@ class DAV extends \OC\Files\Storage\Common{ try { $response=$this->client->propfind($path, array(), 1); $id=md5('webdav'.$this->root.$path); + $content = array(); \OC_FakeDirStream::$dirs[$id]=array(); $files=array_keys($response); array_shift($files);//the first entry is the current directory foreach ($files as $file) { $file = urldecode(basename($file)); - \OC_FakeDirStream::$dirs[$id][]=$file; + $content[]=$file; } + \OC\Files\Stream\Dir::register($id, $content); return opendir('fakedir://'.$id); } catch(\Exception $e) { return false; @@ -194,7 +196,7 @@ class DAV extends \OC\Files\Storage\Common{ $ext=''; } $tmpFile = \OCP\Files::tmpFile($ext); - \OC_CloseStreamWrapper::$callBacks[$tmpFile]=array($this, 'writeBack'); + \OC\Files\Stream\Close::registerCallback($tmpFile, array($this, 'writeBack')); if($this->file_exists($path)) { $this->getFile($path, $tmpFile); } diff --git a/apps/files_sharing/lib/sharedstorage.php b/apps/files_sharing/lib/sharedstorage.php index b0be6843c5..ea28ca69b9 100644 --- a/apps/files_sharing/lib/sharedstorage.php +++ b/apps/files_sharing/lib/sharedstorage.php @@ -102,7 +102,7 @@ class Shared extends \OC\Files\Storage\Common { public function opendir($path) { if ($path == '' || $path == '/') { $files = \OCP\Share::getItemsSharedWith('file', \OC_Share_Backend_Folder::FORMAT_OPENDIR); - \OC_FakeDirStream::$dirs['shared'] = $files; + \OC\Files\Stream\Dir::register('shared', $files); return opendir('fakedir://shared'); } else if ($source = $this->getSourcePath($path)) { list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source); diff --git a/lib/archive/tar.php b/lib/archive/tar.php index 0fa633c603..117d88e5f4 100644 --- a/lib/archive/tar.php +++ b/lib/archive/tar.php @@ -308,7 +308,7 @@ class OC_Archive_TAR extends OC_Archive{ if($mode=='r' or $mode=='rb') { return fopen($tmpFile, $mode); }else{ - OC_CloseStreamWrapper::$callBacks[$tmpFile]=array($this, 'writeBack'); + \OC\Files\Stream\Close::registerCallback($tmpFile, array($this, 'writeBack')); self::$tempFiles[$tmpFile]=$path; return fopen('close://'.$tmpFile, $mode); } diff --git a/lib/archive/zip.php b/lib/archive/zip.php index 1c967baa08..8e31795ded 100644 --- a/lib/archive/zip.php +++ b/lib/archive/zip.php @@ -171,7 +171,7 @@ class OC_Archive_ZIP extends OC_Archive{ $ext=''; } $tmpFile=OCP\Files::tmpFile($ext); - OC_CloseStreamWrapper::$callBacks[$tmpFile]=array($this, 'writeBack'); + \OC\Files\Stream\Close::registerCallback($tmpFile, array($this, 'writeBack')); if($this->fileExists($path)) { $this->extractFile($path, $tmpFile); } diff --git a/lib/base.php b/lib/base.php index e3baa617c3..45e2f633cb 100644 --- a/lib/base.php +++ b/lib/base.php @@ -421,10 +421,9 @@ class OC } // register the stream wrappers - require_once 'streamwrappers.php'; - stream_wrapper_register("fakedir", "OC_FakeDirStream"); - stream_wrapper_register('static', 'OC_StaticStreamWrapper'); - stream_wrapper_register('close', 'OC_CloseStreamWrapper'); + stream_wrapper_register('fakedir', 'OC\Files\Stream\Dir'); + stream_wrapper_register('static', 'OC\Files\Stream\StaticStream'); + stream_wrapper_register('close', 'OC\Files\Stream\Close'); self::checkConfig(); self::checkInstalled(); diff --git a/lib/files/stream/close.php b/lib/files/stream/close.php new file mode 100644 index 0000000000..80de3497c3 --- /dev/null +++ b/lib/files/stream/close.php @@ -0,0 +1,100 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Files\Stream; + +/** + * stream wrapper that provides a callback on stream close + */ +class Close { + private static $callBacks = array(); + private $path = ''; + private $source; + private static $open = array(); + + public function stream_open($path, $mode, $options, &$opened_path) { + $path = substr($path, strlen('close://')); + $this->path = $path; + $this->source = fopen($path, $mode); + if (is_resource($this->source)) { + $this->meta = stream_get_meta_data($this->source); + } + self::$open[] = $path; + return is_resource($this->source); + } + + public function stream_seek($offset, $whence = SEEK_SET) { + fseek($this->source, $offset, $whence); + } + + public function stream_tell() { + return ftell($this->source); + } + + public function stream_read($count) { + return fread($this->source, $count); + } + + public function stream_write($data) { + return fwrite($this->source, $data); + } + + public function stream_set_option($option, $arg1, $arg2) { + switch ($option) { + case STREAM_OPTION_BLOCKING: + stream_set_blocking($this->source, $arg1); + break; + case STREAM_OPTION_READ_TIMEOUT: + stream_set_timeout($this->source, $arg1, $arg2); + break; + case STREAM_OPTION_WRITE_BUFFER: + stream_set_write_buffer($this->source, $arg1, $arg2); + } + } + + public function stream_stat() { + return fstat($this->source); + } + + public function stream_lock($mode) { + flock($this->source, $mode); + } + + public function stream_flush() { + return fflush($this->source); + } + + public function stream_eof() { + return feof($this->source); + } + + public function url_stat($path) { + $path = substr($path, strlen('close://')); + if (file_exists($path)) { + return stat($path); + } else { + return false; + } + } + + public function stream_close() { + fclose($this->source); + if (isset(self::$callBacks[$this->path])) { + call_user_func(self::$callBacks[$this->path], $this->path); + } + } + + public function unlink($path) { + $path = substr($path, strlen('close://')); + return unlink($path); + } + + public static function registerCallback($path, $callback) { + self::$callBacks[$path] = $callback; + } +} diff --git a/lib/files/stream/dir.php b/lib/files/stream/dir.php new file mode 100644 index 0000000000..6ca884fc99 --- /dev/null +++ b/lib/files/stream/dir.php @@ -0,0 +1,47 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Files\Stream; + +class Dir { + private static $dirs = array(); + private $name; + private $index; + + public function dir_opendir($path, $options) { + $this->name = substr($path, strlen('fakedir://')); + $this->index = 0; + if (!isset(self::$dirs[$this->name])) { + self::$dirs[$this->name] = array(); + } + return true; + } + + public function dir_readdir() { + if ($this->index >= count(self::$dirs[$this->name])) { + return false; + } + $filename = self::$dirs[$this->name][$this->index]; + $this->index++; + return $filename; + } + + public function dir_closedir() { + $this->name = ''; + return true; + } + + public function dir_rewinddir() { + $this->index = 0; + return true; + } + + public static function register($path, $content) { + self::$dirs[$path] = $content; + } +} diff --git a/lib/streamwrappers.php b/lib/files/stream/staticstream.php similarity index 57% rename from lib/streamwrappers.php rename to lib/files/stream/staticstream.php index 981c280f0d..7725a6a5a0 100644 --- a/lib/streamwrappers.php +++ b/lib/files/stream/staticstream.php @@ -1,54 +1,30 @@ + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ -class OC_FakeDirStream{ - public static $dirs=array(); - private $name; - private $index; +namespace OC\Files\Stream; - public function dir_opendir($path, $options) { - $this->name=substr($path, strlen('fakedir://')); - $this->index=0; - if(!isset(self::$dirs[$this->name])) { - self::$dirs[$this->name]=array(); - } - return true; - } - - public function dir_readdir() { - if($this->index>=count(self::$dirs[$this->name])) { - return false; - } - $filename=self::$dirs[$this->name][$this->index]; - $this->index++; - return $filename; - } - - public function dir_closedir() { - $this->name=''; - return true; - } - - public function dir_rewinddir() { - $this->index=0; - return true; - } -} - -class OC_StaticStreamWrapper { +class StaticStream { public $context; protected static $data = array(); - protected $path = ''; + protected $path = ''; protected $pointer = 0; protected $writable = false; - public function stream_close() {} + public function stream_close() { + } public function stream_eof() { return $this->pointer >= strlen(self::$data[$this->path]); } - public function stream_flush() {} + public function stream_flush() { + } public function stream_open($path, $mode, $options, &$opened_path) { switch ($mode[0]) { @@ -213,89 +189,3 @@ class OC_StaticStreamWrapper { return false; } } - -/** - * stream wrapper that provides a callback on stream close - */ -class OC_CloseStreamWrapper{ - public static $callBacks=array(); - private $path=''; - private $source; - private static $open=array(); - public function stream_open($path, $mode, $options, &$opened_path) { - $path=substr($path, strlen('close://')); - $this->path=$path; - $this->source=fopen($path, $mode); - if(is_resource($this->source)) { - $this->meta=stream_get_meta_data($this->source); - } - self::$open[]=$path; - return is_resource($this->source); - } - - public function stream_seek($offset, $whence=SEEK_SET) { - fseek($this->source, $offset, $whence); - } - - public function stream_tell() { - return ftell($this->source); - } - - public function stream_read($count) { - return fread($this->source, $count); - } - - public function stream_write($data) { - return fwrite($this->source, $data); - } - - public function stream_set_option($option, $arg1, $arg2) { - switch($option) { - case STREAM_OPTION_BLOCKING: - stream_set_blocking($this->source, $arg1); - break; - case STREAM_OPTION_READ_TIMEOUT: - stream_set_timeout($this->source, $arg1, $arg2); - break; - case STREAM_OPTION_WRITE_BUFFER: - stream_set_write_buffer($this->source, $arg1, $arg2); - } - } - - public function stream_stat() { - return fstat($this->source); - } - - public function stream_lock($mode) { - flock($this->source, $mode); - } - - public function stream_flush() { - return fflush($this->source); - } - - public function stream_eof() { - return feof($this->source); - } - - public function url_stat($path) { - $path=substr($path, strlen('close://')); - if(file_exists($path)) { - return stat($path); - }else{ - return false; - } - } - - public function stream_close() { - fclose($this->source); - if(isset(self::$callBacks[$this->path])) { - call_user_func(self::$callBacks[$this->path], $this->path); - } - } - - public function unlink($path) { - $path=substr($path, strlen('close://')); - return unlink($path); - } -} diff --git a/tests/lib/streamwrappers.php b/tests/lib/streamwrappers.php index aebbc93b90..e40f2e7612 100644 --- a/tests/lib/streamwrappers.php +++ b/tests/lib/streamwrappers.php @@ -1,41 +1,41 @@ . -* -*/ + * ownCloud + * + * @author Robin Appelman + * @copyright 2012 Robin Appelman icewind@owncloud.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see . + * + */ class Test_StreamWrappers extends PHPUnit_Framework_TestCase { public function testFakeDir() { - $items=array('foo', 'bar'); - OC_FakeDirStream::$dirs['test']=$items; - $dh=opendir('fakedir://test'); - $result=array(); - while($file=readdir($dh)) { - $result[]=$file; + $items = array('foo', 'bar'); + \OC\Files\Stream\Dir::register('test', $items); + $dh = opendir('fakedir://test'); + $result = array(); + while ($file = readdir($dh)) { + $result[] = $file; $this->assertContains($file, $items); } $this->assertEquals(count($items), count($result)); } public function testStaticStream() { - $sourceFile=OC::$SERVERROOT.'/tests/data/lorem.txt'; - $staticFile='static://test'; + $sourceFile = OC::$SERVERROOT . '/tests/data/lorem.txt'; + $staticFile = 'static://test'; $this->assertFalse(file_exists($staticFile)); file_put_contents($staticFile, file_get_contents($sourceFile)); $this->assertTrue(file_exists($staticFile)); @@ -47,27 +47,27 @@ class Test_StreamWrappers extends PHPUnit_Framework_TestCase { public function testCloseStream() { //ensure all basic stream stuff works - $sourceFile=OC::$SERVERROOT.'/tests/data/lorem.txt'; - $tmpFile=OC_Helper::TmpFile('.txt'); - $file='close://'.$tmpFile; + $sourceFile = OC::$SERVERROOT . '/tests/data/lorem.txt'; + $tmpFile = OC_Helper::TmpFile('.txt'); + $file = 'close://' . $tmpFile; $this->assertTrue(file_exists($file)); file_put_contents($file, file_get_contents($sourceFile)); $this->assertEquals(file_get_contents($sourceFile), file_get_contents($file)); unlink($file); clearstatcache(); $this->assertFalse(file_exists($file)); - + //test callback - $tmpFile=OC_Helper::TmpFile('.txt'); - $file='close://'.$tmpFile; - OC_CloseStreamWrapper::$callBacks[$tmpFile]=array('Test_StreamWrappers', 'closeCallBack'); - $fh=fopen($file, 'w'); + $tmpFile = OC_Helper::TmpFile('.txt'); + $file = 'close://' . $tmpFile; + \OC\Files\Stream\Close::registerCallback($tmpFile, array('Test_StreamWrappers', 'closeCallBack')); + $fh = fopen($file, 'w'); fwrite($fh, 'asd'); - try{ + try { fclose($fh); $this->fail('Expected exception'); - }catch(Exception $e) { - $path=$e->getMessage(); + } catch (Exception $e) { + $path = $e->getMessage(); $this->assertEquals($path, $tmpFile); } } From 232cc3211b2b312d4e32e7d0fb482a2b7affe89a Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 28 Jan 2013 15:35:30 +0100 Subject: [PATCH 238/272] add oc:// streamwrapper to provide access to ownCloud's virtual filesystem --- lib/base.php | 1 + lib/files/stream/oc.php | 129 +++++++++++++++++++++++++++++++++++ tests/lib/streamwrappers.php | 19 ++++++ 3 files changed, 149 insertions(+) create mode 100644 lib/files/stream/oc.php diff --git a/lib/base.php b/lib/base.php index 45e2f633cb..e6f00839aa 100644 --- a/lib/base.php +++ b/lib/base.php @@ -424,6 +424,7 @@ class OC stream_wrapper_register('fakedir', 'OC\Files\Stream\Dir'); stream_wrapper_register('static', 'OC\Files\Stream\StaticStream'); stream_wrapper_register('close', 'OC\Files\Stream\Close'); + stream_wrapper_register('oc', 'OC\Files\Stream\OC'); self::checkConfig(); self::checkInstalled(); diff --git a/lib/files/stream/oc.php b/lib/files/stream/oc.php new file mode 100644 index 0000000000..88e7e062df --- /dev/null +++ b/lib/files/stream/oc.php @@ -0,0 +1,129 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Files\Stream; + +/** + * a stream wrappers for ownCloud's virtual filesystem + */ +class OC { + /** + * @var \OC\Files\View + */ + static private $rootView; + + private $path; + private $dirSource; + private $fileSource; + private $meta; + + private function setup(){ + if (!self::$rootView) { + self::$rootView = new \OC\Files\View(''); + } + } + + public function stream_open($path, $mode, $options, &$opened_path) { + $this->setup(); + $path = substr($path, strlen('oc://')); + $this->path = $path; + $this->fileSource = self::$rootView->fopen($path, $mode); + if (is_resource($this->fileSource)) { + $this->meta = stream_get_meta_data($this->fileSource); + } + return is_resource($this->fileSource); + } + + public function stream_seek($offset, $whence = SEEK_SET) { + fseek($this->fileSource, $offset, $whence); + } + + public function stream_tell() { + return ftell($this->fileSource); + } + + public function stream_read($count) { + return fread($this->fileSource, $count); + } + + public function stream_write($data) { + return fwrite($this->fileSource, $data); + } + + public function stream_set_option($option, $arg1, $arg2) { + switch ($option) { + case STREAM_OPTION_BLOCKING: + stream_set_blocking($this->fileSource, $arg1); + break; + case STREAM_OPTION_READ_TIMEOUT: + stream_set_timeout($this->fileSource, $arg1, $arg2); + break; + case STREAM_OPTION_WRITE_BUFFER: + stream_set_write_buffer($this->fileSource, $arg1, $arg2); + } + } + + public function stream_stat() { + return fstat($this->fileSource); + } + + public function stream_lock($mode) { + flock($this->fileSource, $mode); + } + + public function stream_flush() { + return fflush($this->fileSource); + } + + public function stream_eof() { + return feof($this->fileSource); + } + + public function url_stat($path) { + $this->setup(); + $path = substr($path, strlen('oc://')); + if (self::$rootView->file_exists($path)) { + return self::$rootView->stat($path); + } else { + return false; + } + } + + public function stream_close() { + fclose($this->fileSource); + } + + public function unlink($path) { + $this->setup(); + $path = substr($path, strlen('oc://')); + return self::$rootView->unlink($path); + } + + public function dir_opendir($path, $options) { + $this->setup(); + $path = substr($path, strlen('oc://')); + $this->path = $path; + $this->dirSource = self::$rootView->opendir($path); + if (is_resource($this->dirSource)) { + $this->meta = stream_get_meta_data($this->dirSource); + } + return is_resource($this->dirSource); + } + + public function dir_readdir() { + return readdir($this->dirSource); + } + + public function dir_closedir() { + closedir($this->dirSource); + } + + public function dir_rewinddir() { + rewinddir($this->dirSource); + } +} diff --git a/tests/lib/streamwrappers.php b/tests/lib/streamwrappers.php index e40f2e7612..2237ee7d37 100644 --- a/tests/lib/streamwrappers.php +++ b/tests/lib/streamwrappers.php @@ -75,4 +75,23 @@ class Test_StreamWrappers extends PHPUnit_Framework_TestCase { public static function closeCallBack($path) { throw new Exception($path); } + + public function testOC() { + \OC\Files\Mount::clear(); + $storage = new \OC\Files\Storage\Temporary(array()); + $storage->file_put_contents('foo.txt', 'asd'); + new \OC\Files\Mount($storage, '/'); + + $this->assertTrue(file_exists('oc:///foo.txt')); + $this->assertEquals('asd', file_get_contents('oc:///foo.txt')); + $this->assertEquals(array('.', '..', 'foo.txt'), scandir('oc:///')); + + file_put_contents('oc:///bar.txt', 'qwerty'); + $this->assertEquals('qwerty', $storage->file_get_contents('bar.txt')); + $this->assertEquals(array('.', '..', 'bar.txt', 'foo.txt'), scandir('oc:///')); + $this->assertEquals('qwerty', file_get_contents('oc:///bar.txt')); + + unlink('oc:///foo.txt'); + $this->assertEquals(array('.', '..', 'bar.txt'), scandir('oc:///')); + } } From 7207211f3a9546f9d33cfffec0caf26ea419c50e Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 28 Jan 2013 19:58:24 +0100 Subject: [PATCH 239/272] change required ownCloud version to 4.91 --- apps/files/appinfo/info.xml | 2 +- apps/files_external/appinfo/info.xml | 2 +- apps/files_sharing/appinfo/info.xml | 2 +- apps/files_versions/appinfo/info.xml | 2 +- apps/user_ldap/appinfo/info.xml | 2 +- apps/user_webdavauth/appinfo/info.xml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/files/appinfo/info.xml b/apps/files/appinfo/info.xml index 0a1b196b06..7c82c839da 100644 --- a/apps/files/appinfo/info.xml +++ b/apps/files/appinfo/info.xml @@ -5,7 +5,7 @@ File Management AGPL Robin Appelman - 4.9 + 4.91 true diff --git a/apps/files_external/appinfo/info.xml b/apps/files_external/appinfo/info.xml index 3da1913c5f..2c04216a9f 100644 --- a/apps/files_external/appinfo/info.xml +++ b/apps/files_external/appinfo/info.xml @@ -5,7 +5,7 @@ Mount external storage sources AGPL Robin Appelman, Michael Gapczynski - 4.9 + 4.91 true diff --git a/apps/files_sharing/appinfo/info.xml b/apps/files_sharing/appinfo/info.xml index a44d0338bb..1f24a4dde8 100644 --- a/apps/files_sharing/appinfo/info.xml +++ b/apps/files_sharing/appinfo/info.xml @@ -5,7 +5,7 @@ File sharing between users AGPL Michael Gapczynski - 4.9 + 4.91 true diff --git a/apps/files_versions/appinfo/info.xml b/apps/files_versions/appinfo/info.xml index e4e5a365d5..0155f8e830 100644 --- a/apps/files_versions/appinfo/info.xml +++ b/apps/files_versions/appinfo/info.xml @@ -4,7 +4,7 @@ Versions AGPL Frank Karlitschek - 4.9 + 4.91 true Versioning of files diff --git a/apps/user_ldap/appinfo/info.xml b/apps/user_ldap/appinfo/info.xml index a760577527..53269edfb3 100644 --- a/apps/user_ldap/appinfo/info.xml +++ b/apps/user_ldap/appinfo/info.xml @@ -7,7 +7,7 @@ This app is not compatible to the WebDAV user backend. AGPL Dominik Schmidt and Arthur Schiwon - 4.9 + 4.91 true diff --git a/apps/user_webdavauth/appinfo/info.xml b/apps/user_webdavauth/appinfo/info.xml index e51f2e9ec4..f62f03577e 100755 --- a/apps/user_webdavauth/appinfo/info.xml +++ b/apps/user_webdavauth/appinfo/info.xml @@ -7,7 +7,7 @@ This app is not compatible to the LDAP user and group backend. AGPL Frank Karlitschek - 4.9 + 4.91 true From 929b8c3e6926dbe4cf2c3e0b2eb00b502d49f64a Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 28 Jan 2013 20:04:10 +0100 Subject: [PATCH 240/272] fix files_version hooks --- apps/files_versions/lib/hooks.php | 34 +++++++++++++++---------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/apps/files_versions/lib/hooks.php b/apps/files_versions/lib/hooks.php index 3a64e571d5..5cefc53289 100644 --- a/apps/files_versions/lib/hooks.php +++ b/apps/files_versions/lib/hooks.php @@ -39,15 +39,15 @@ class Hooks { * cleanup the versions directory if the actual file gets deleted */ public static function remove_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->delete( $path ); - - } + if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') { + + $versions = new Storage( new \OC_FilesystemView('') ); + + $path = $params[\OC\Files\Filesystem::signal_param_path]; + + if($path<>'') $versions->delete( $path ); + + } } /** @@ -58,15 +58,15 @@ class Hooks { * of the stored versions along the actual file */ public static function rename_hook($params) { - if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') { - - $versions = new Storage( new \OC_FilesystemView('') ); - + if(\OCP\Config::getSystemValue('files_versions', Storage::DEFAULTENABLED)=='true') { + + $versions = new Storage( new \OC_FilesystemView('') ); + $oldpath = $params['oldpath']; - $newpath = $params['newpath']; - - if($oldpath<>'' && $newpath<>'') $versions->rename( $oldpath, $newpath ); - + $newpath = $params['newpath']; + + if($oldpath<>'' && $newpath<>'') $versions->rename( $oldpath, $newpath ); + } } From 86e5a9cd7354225572507343015107edc665aa08 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 28 Jan 2013 21:09:18 +0100 Subject: [PATCH 241/272] Files: fix sharing newly uploaded files --- apps/files/ajax/upload.php | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/apps/files/ajax/upload.php b/apps/files/ajax/upload.php index befb94d084..9ecc1a6c2f 100644 --- a/apps/files/ajax/upload.php +++ b/apps/files/ajax/upload.php @@ -21,13 +21,13 @@ if (!isset($_FILES['files'])) { foreach ($_FILES['files']['error'] as $error) { if ($error != 0) { $errors = array( - UPLOAD_ERR_OK => $l->t('There is no error, the file uploaded with success'), - UPLOAD_ERR_INI_SIZE => $l->t('The uploaded file exceeds the upload_max_filesize directive in php.ini: ') + UPLOAD_ERR_OK => $l->t('There is no error, the file uploaded with success'), + UPLOAD_ERR_INI_SIZE => $l->t('The uploaded file exceeds the upload_max_filesize directive in php.ini: ') . ini_get('upload_max_filesize'), - UPLOAD_ERR_FORM_SIZE => $l->t('The uploaded file exceeds the MAX_FILE_SIZE directive that was specified' + UPLOAD_ERR_FORM_SIZE => $l->t('The uploaded file exceeds the MAX_FILE_SIZE directive that was specified' . ' in the HTML form'), - UPLOAD_ERR_PARTIAL => $l->t('The uploaded file was only partially uploaded'), - UPLOAD_ERR_NO_FILE => $l->t('No file was uploaded'), + UPLOAD_ERR_PARTIAL => $l->t('The uploaded file was only partially uploaded'), + UPLOAD_ERR_NO_FILE => $l->t('No file was uploaded'), UPLOAD_ERR_NO_TMP_DIR => $l->t('Missing a temporary folder'), UPLOAD_ERR_CANT_WRITE => $l->t('Failed to write to disk'), ); @@ -40,14 +40,17 @@ $files = $_FILES['files']; $dir = $_POST['dir']; $error = ''; +$maxUploadFilesize = OCP\Util::maxUploadFilesize($dir); +$maxHumanFilesize = OCP\Util::humanFileSize($maxUploadFilesize); + $totalSize = 0; foreach ($files['size'] as $size) { $totalSize += $size; } -if($totalSize>\OC\Files\Filesystem::free_space($dir)) { - OCP\JSON::error(array('data' => array( 'message' => $l->t( 'Not enough space available' ), - 'uploadMaxFilesize'=>$maxUploadFilesize, - 'maxHumanFilesize'=>$maxHumanFilesize))); +if ($totalSize > \OC\Files\Filesystem::free_space($dir)) { + OCP\JSON::error(array('data' => array('message' => $l->t('Not enough space available'), + 'uploadMaxFilesize' => $maxUploadFilesize, + 'maxHumanFilesize' => $maxHumanFilesize))); exit(); } @@ -58,18 +61,18 @@ if (strpos($dir, '..') === false) { $target = OCP\Files::buildNotExistingFileName(stripslashes($dir), $files['name'][$i]); // $path needs to be normalized - this failed within drag'n'drop upload to a sub-folder $target = \OC\Files\Filesystem::normalizePath($target); - if(is_uploaded_file($files['tmp_name'][$i]) and \OC\Files\Filesystem::fromTmpFile($files['tmp_name'][$i], $target)) { + if (is_uploaded_file($files['tmp_name'][$i]) and \OC\Files\Filesystem::fromTmpFile($files['tmp_name'][$i], $target)) { $meta = \OC\Files\Filesystem::getFileInfo($target); // updated max file size after upload $storageStats = \OCA\files\lib\Helper::buildFileStorageStatistics($dir); - $result[]=array( 'status' => 'success', - 'mime'=>$meta['mimetype'], - 'size'=>$meta['size'], - 'id'=>$id, - 'name'=>basename($target), - 'uploadMaxFilesize'=>$maxUploadFilesize, - 'maxHumanFilesize'=>$maxHumanFilesize + $result[] = array('status' => 'success', + 'mime' => $meta['mimetype'], + 'size' => $meta['size'], + 'id' => $meta['fileid'], + 'name' => basename($target), + 'uploadMaxFilesize' => $maxUploadFilesize, + 'maxHumanFilesize' => $maxHumanFilesize ); } } From a67f11c8afb78fc7ee011982257124f49910d269 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 28 Jan 2013 21:25:19 +0100 Subject: [PATCH 242/272] Sharing: partailly fix searchByMime --- apps/files_sharing/lib/cache.php | 55 ++++++++++++++++---------------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/apps/files_sharing/lib/cache.php b/apps/files_sharing/lib/cache.php index 0b187a3c3f..9655e44787 100644 --- a/apps/files_sharing/lib/cache.php +++ b/apps/files_sharing/lib/cache.php @@ -1,23 +1,23 @@ . -*/ + * ownCloud + * + * @author Michael Gapczynski + * @copyright 2012 Michael Gapczynski mtgap@owncloud.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see . + */ namespace OC\Files\Cache; @@ -31,18 +31,18 @@ class Shared_Cache extends Cache { private $files = array(); public function __construct($storage) { - + } /** - * @brief Get the source cache of a shared file or folder - * @param string $target Shared target file path - * @return \OC\Files\Cache\Cache - */ + * @brief Get the source cache of a shared file or folder + * @param string $target Shared target file path + * @return \OC\Files\Cache\Cache + */ private function getSourceCache($target) { $source = \OC_Share_Backend_File::getSource($target); if (isset($source['path'])) { - $source['path'] = '/'.$source['uid_owner'].'/'.$source['path']; + $source['path'] = '/' . $source['uid_owner'] . '/' . $source['path']; \OC\Files\Filesystem::initMountPoints($source['uid_owner']); list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source['path']); if ($storage) { @@ -170,10 +170,10 @@ class Shared_Cache extends Cache { if ($cache = $this->getSourceCache($source)) { $targetPath = \OC_Share_Backend_File::getSourcePath(dirname($target)); if ($targetPath) { - $targetPath .= '/'.basename($target); + $targetPath .= '/' . basename($target); $cache->move($this->files[$source], $targetPath); } - + } } @@ -222,11 +222,12 @@ class Shared_Cache extends Cache { } else { $where = '`mimepart` = ?'; } + $mimetype = $this->getMimetypeId($mimetype); $ids = $this->getAll(); $placeholders = join(',', array_fill(0, count($ids), '?')); $query = \OC_DB::prepare(' SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, `encrypted` - FROM `*PREFIX*filecache` WHERE ' . $where . ' AND `fileid` IN = ('.$placeholders.')' + FROM `*PREFIX*filecache` WHERE ' . $where . ' AND `fileid` IN (' . $placeholders . ')' ); $result = $query->execute(array_merge(array($mimetype), $ids)); return $result->fetchAll(); From ca4c006d5ab71acbd15ea011fba72eaddc34a901 Mon Sep 17 00:00:00 2001 From: Michael Gapczynski Date: Mon, 28 Jan 2013 23:51:35 -0500 Subject: [PATCH 243/272] Make zip downloads use actual folder names, fix spelling mistakes --- lib/files.php | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/files.php b/lib/files.php index 4d09946a23..e3245653f9 100644 --- a/lib/files.php +++ b/lib/files.php @@ -37,10 +37,10 @@ class OC_Files { } /** - * return the content of a file or return a zip file containning multiply files + * return the content of a file or return a zip file containing multiple files * * @param string $dir - * @param string $file ; seperated list of files to download + * @param string $file ; separated list of files to download * @param boolean $only_header ; boolean to only send header of the request */ public static function get($dir, $files, $only_header = false) { @@ -77,6 +77,7 @@ class OC_Files { } } $zip->close(); + $name = basename($dir) . '.zip'; set_time_limit($executionTime); } elseif (\OC\Files\Filesystem::is_dir($dir . '/' . $files)) { self::validateZipDownload($dir, $files); @@ -94,18 +95,20 @@ class OC_Files { $file = $dir . '/' . $files; self::zipAddDir($file, $zip); $zip->close(); + $name = $files . '.zip'; set_time_limit($executionTime); } else { $zip = false; $filename = $dir . '/' . $files; + $name = $files; } OC_Util::obEnd(); if ($zip or \OC\Files\Filesystem::isReadable($filename)) { if ( preg_match( "/MSIE/", $_SERVER["HTTP_USER_AGENT"] ) ) { - header( 'Content-Disposition: attachment; filename="' . rawurlencode( basename($filename) ) . '"' ); + header( 'Content-Disposition: attachment; filename="' . rawurlencode($name) . '"' ); } else { - header( 'Content-Disposition: attachment; filename*=UTF-8\'\'' . rawurlencode( basename($filename) ) - . '; filename="' . rawurlencode( basename($filename) ) . '"' ); + header( 'Content-Disposition: attachment; filename*=UTF-8\'\'' . rawurlencode($name) + . '; filename="' . rawurlencode($name) . '"' ); } header('Content-Transfer-Encoding: binary'); OC_Response::disableCaching(); @@ -125,7 +128,7 @@ class OC_Files { } elseif ($zip or !\OC\Files\Filesystem::file_exists($filename)) { header("HTTP/1.0 404 Not Found"); $tmpl = new OC_Template('', '404', 'guest'); - $tmpl->assign('file', $filename); + $tmpl->assign('file', $name); $tmpl->printPage(); } else { header("HTTP/1.0 403 Forbidden"); From d10e137939d8f88d3dd742ec71c653628467c51d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Tue, 29 Jan 2013 10:03:04 +0100 Subject: [PATCH 244/272] icons added; empty trash bin message added --- apps/files/js/filelist.js | 2 +- apps/files_trashbin/index.php | 1 + apps/files_trashbin/js/trash.js | 2 +- apps/files_trashbin/lib/trash.php | 2 +- apps/files_trashbin/templates/index.php | 9 ++++----- core/img/actions/undelete.png | Bin 0 -> 680 bytes 6 files changed, 8 insertions(+), 8 deletions(-) create mode 100644 core/img/actions/undelete.png diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 40d75aae93..5cb8849ffe 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -292,7 +292,7 @@ var FileList={ procesSelection(); } }); - }, + } }; $(document).ready(function(){ diff --git a/apps/files_trashbin/index.php b/apps/files_trashbin/index.php index 20f9cb8daf..2e978ae190 100644 --- a/apps/files_trashbin/index.php +++ b/apps/files_trashbin/index.php @@ -95,6 +95,7 @@ $list->assign('dirlisting', $dirlisting); $list->assign('disableDownloadActions', true); $tmpl->assign('breadcrumb', $breadcrumbNav->fetchPage(), false); $tmpl->assign('fileList', $list->fetchPage(), false); +$tmpl->assign('files', $files); $tmpl->assign('dir', OC_Filesystem::normalizePath($view->getAbsolutePath())); $tmpl->printPage(); diff --git a/apps/files_trashbin/js/trash.js b/apps/files_trashbin/js/trash.js index 92a431d495..553e4cbc6f 100644 --- a/apps/files_trashbin/js/trash.js +++ b/apps/files_trashbin/js/trash.js @@ -2,7 +2,7 @@ $(document).ready(function() { if (typeof FileActions !== 'undefined') { - FileActions.register('all', 'Undelete', OC.PERMISSION_READ, '', function(filename) { + FileActions.register('all', 'Undelete', OC.PERMISSION_READ, OC.imagePath('core', 'actions/undelete.png'), function(filename) { var tr=$('tr').filterAttr('data-file', filename); $.post(OC.filePath('files_trashbin','ajax','undelete.php'), {files:tr.attr('data-file'), dirlisting:tr.attr('data-dirlisting') }, diff --git a/apps/files_trashbin/lib/trash.php b/apps/files_trashbin/lib/trash.php index e81ba12758..fad96e5222 100644 --- a/apps/files_trashbin/lib/trash.php +++ b/apps/files_trashbin/lib/trash.php @@ -54,7 +54,7 @@ class Trashbin { self::copy_recursive($file_path, 'files_trashbin/'.$deleted.'.d'.$timestamp, $view); $query = \OC_DB::prepare("INSERT INTO *PREFIX*files_trash (id,timestamp,location,type,mime,user) VALUES (?,?,?,?,?,?)"); - $result = $query->execute(array($deleted, $timestamp, $location, $type, $mime, $user)); + $query->execute(array($deleted, $timestamp, $location, $type, $mime, $user)); if ( \OCP\App::isEnabled('files_versions') ) { if ( $view->is_dir('files_versions'.$file_path) ) { diff --git a/apps/files_trashbin/templates/index.php b/apps/files_trashbin/templates/index.php index a9cb216b1f..ebabec2d4d 100644 --- a/apps/files_trashbin/templates/index.php +++ b/apps/files_trashbin/templates/index.php @@ -5,8 +5,8 @@
- -
t('Nothing in here. Upload something!')?>
+ +
t('Nothing in here. Trash bin empty!')?>
@@ -17,9 +17,8 @@ t( 'Name' ); ?> - + Undelete" /> t('Undelete')?> diff --git a/core/img/actions/undelete.png b/core/img/actions/undelete.png new file mode 100644 index 0000000000000000000000000000000000000000..9f728252394f15555fe35e3ca049995a1c2d6117 GIT binary patch literal 680 zcmV;Z0$2TsP)Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01m_e01m_fl`9S#00007bV*G`2i*Z3 z2rD*9zB=mw000?uMObu0Z*6U5Zgc=ca%Ew3Wn>_CX>@2HM@dakSAh-}00065Nkl4dSG%CZndg-Aj!-AlP_7c^@Bq$|{f?k9u zjA}0i&6fp1e@L*l$Oa`niAk0*yht=H8FM7t+V1Tk*V?89c^-e?-#MJ$`JJzz>$=Vg zU|4RDno|kCe9h-?NzB!DT&-?4Se3GSLf%2h-^0x@D|fFPrmx4xKxho3Nk)a)Bp(-CXQ{%0fCvg~>=-GqR_BKTgM5W~as(3Ps3@CgkcS0>OJU zSi~isAV^dy63BLfgO6}p3ZqN&Q=B?}f;Z{}z6-Z_aQ!4A@Y{=~{gGvQn(p)0SkMYb z;+i^c_ zC6=6pJqKv2t>UZto}uTj$Vf6q>qY{>0F8@15K4C;mr%KU)<-Zh zfypEzi2_=_iEWEbG%wM_QY628Bs@IAOd^HZYR9$DjpJ{-@V^jQA$|hu6u!#TV1_XO O0000 Date: Tue, 29 Jan 2013 10:08:41 +0100 Subject: [PATCH 245/272] seperate css no longer needed, use files app css instead --- apps/files_trashbin/css/trash.css | 78 ------------------------------- apps/files_trashbin/index.php | 1 - 2 files changed, 79 deletions(-) delete mode 100644 apps/files_trashbin/css/trash.css diff --git a/apps/files_trashbin/css/trash.css b/apps/files_trashbin/css/trash.css deleted file mode 100644 index e0dd8c6e93..0000000000 --- a/apps/files_trashbin/css/trash.css +++ /dev/null @@ -1,78 +0,0 @@ -body { - background:#ddd; -} - -#header { - background:#1d2d44; - box-shadow:0 0 10px rgba(0,0,0,.5), inset 0 -2px 10px #222; - height:2.5em; - left:0; - line-height:2.5em; - position:fixed; - right:0; - top:0; - z-index:100; - padding:.5em; -} - -#details { - color:#fff; -} - -#header #undelete { - font-weight:700; - margin-left:2em; -} - -#header #undelete img { - padding-left:.1em; - padding-right:.3em; - vertical-align:text-bottom; -} - -#preview { - background:#eee; - border-bottom:1px solid #f8f8f8; - min-height:30em; - padding-top:2em; - text-align:center; - margin:50px auto; -} - -#noPreview { - display:none; - padding-top:5em; -} - -p.info { - color:#777; - text-align:center; - text-shadow:#fff 0 1px 0; - width:22em; - margin:2em auto; -} - -p.info a { - color:#777; - font-weight:700; -} - -#imgframe { - height:75%; - padding-bottom:2em; - width:80%; - margin:0 auto; -} - -#imgframe img { - max-height:100%; - max-width:100%; -} - -table td.filename .name { - display:block; - height:1.5em; - vertical-align:middle; - margin-left:3em; - /*font-weight:bold;*/ -} \ No newline at end of file diff --git a/apps/files_trashbin/index.php b/apps/files_trashbin/index.php index 2e978ae190..46a601cfdd 100644 --- a/apps/files_trashbin/index.php +++ b/apps/files_trashbin/index.php @@ -3,7 +3,6 @@ // Check if we are a user OCP\User::checkLoggedIn(); -OCP\Util::addStyle('files_trashbin', 'trash'); OCP\Util::addScript('files_trashbin', 'trash'); OCP\Util::addScript('files_trashbin', 'disableDefaultActions'); OCP\Util::addScript('files', 'fileactions'); From 0c8bcacdc8cbeec479c5b7f42ebb8a89ae1cd7bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Tue, 29 Jan 2013 13:36:33 +0100 Subject: [PATCH 246/272] added some additional error handling --- apps/files_trashbin/lib/trash.php | 33 ++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/apps/files_trashbin/lib/trash.php b/apps/files_trashbin/lib/trash.php index fad96e5222..68473c46b1 100644 --- a/apps/files_trashbin/lib/trash.php +++ b/apps/files_trashbin/lib/trash.php @@ -52,18 +52,27 @@ class Trashbin { } self::copy_recursive($file_path, 'files_trashbin/'.$deleted.'.d'.$timestamp, $view); - - $query = \OC_DB::prepare("INSERT INTO *PREFIX*files_trash (id,timestamp,location,type,mime,user) VALUES (?,?,?,?,?,?)"); - $query->execute(array($deleted, $timestamp, $location, $type, $mime, $user)); - - if ( \OCP\App::isEnabled('files_versions') ) { - if ( $view->is_dir('files_versions'.$file_path) ) { - $view->rename('files_versions'.$file_path, 'versions_trashbin/'. $deleted.'.d'.$timestamp); - } else if ( $versions = \OCA_Versions\Storage::getVersions($file_path) ) { - foreach ($versions as $v) { - $view->rename('files_versions'.$v['path'].'.v'.$v['version'], 'versions_trashbin/'. $deleted.'.v'.$v['version'].'.d'.$timestamp); - } + + if ( $view->file_exists('files_trashbin/'.$deleted.'.d'.$timestamp) ) { + $query = \OC_DB::prepare("INSERT INTO *PREFIX*files_trash (id,timestamp,location,type,mime,user) VALUES (?,?,?,?,?,?)"); + $result = $query->execute(array($deleted, $timestamp, $location, $type, $mime, $user)); + if ( !$result ) { // if file couldn't be added to the database than also don't store it in the trash bin. + $view->deleteAll('files_trashbin/'.$deleted.'.d'.$timestamp); + \OC_Log::write('files_trashbin', 'trash bin database couldn\'t be updated', \OC_log::ERROR); + return; } + + if ( \OCP\App::isEnabled('files_versions') ) { + if ( $view->is_dir('files_versions'.$file_path) ) { + $view->rename('files_versions'.$file_path, 'versions_trashbin/'. $deleted.'.d'.$timestamp); + } else if ( $versions = \OCA_Versions\Storage::getVersions($file_path) ) { + foreach ($versions as $v) { + $view->rename('files_versions'.$v['path'].'.v'.$v['version'], 'versions_trashbin/'. $deleted.'.v'.$v['version'].'.d'.$timestamp); + } + } + } + } else { + \OC_Log::write('files_trashbin', 'Couldn\'t move '.$file_path.' to the trash bin' , \OC_log::ERROR); } self::expire(); @@ -133,6 +142,8 @@ class Trashbin { } return true; + } else { + \OC_Log::write('files_trashbin', 'Couldn\'t restore file from trash bin, '.$filename , \OC_log::ERROR); } return false; From d6adab6734375ed2747d4a68c1d331950b0b14b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Wed, 30 Jan 2013 10:07:29 +0100 Subject: [PATCH 247/272] improved message if trash bin is empty --- apps/files_trashbin/templates/index.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/files_trashbin/templates/index.php b/apps/files_trashbin/templates/index.php index ebabec2d4d..104d93ce49 100644 --- a/apps/files_trashbin/templates/index.php +++ b/apps/files_trashbin/templates/index.php @@ -6,7 +6,7 @@
-
t('Nothing in here. Trash bin empty!')?>
+
t('Nothing in here. Your trash bin is empty!')?>
From 9639544ca0a2a149d742985bd72e9b4dc2f15347 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Wed, 30 Jan 2013 12:56:22 +0100 Subject: [PATCH 248/272] make background transparent --- core/img/actions/undelete.png | Bin 680 -> 624 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/core/img/actions/undelete.png b/core/img/actions/undelete.png index 9f728252394f15555fe35e3ca049995a1c2d6117..d712527ef617dca921bcf75b917a1f728bc2102c 100644 GIT binary patch delta 503 zcmVjtP1&(N=qgyIH-$I zORb=2uhU1ruT51DFWl*#dmi_k^F&qomon=Oo)3xbj>=#_0(psc4$7&g?G1sdihrtd zv7aJc*@qX-v+aP@#EOg?rlVeQEv%JNsfxA~@_F8b{Um2+s9#*(C@X zYNU=oqM{ppB%`F;!&D6|HLkupsDG*}BBt9Y17pD0k7Y(!{P=|L_$-Q?)z?S*c(csX zPs-yNfS^0fm31lyQ-888^p!cFgT=WPpN$hoWxt5zr9YSb9l^I|Isj-*EctAFdxgUr zkSbwYj tQmF_hz~tG|mF3Jc+XyHB?Kl2y{Q}3>Gn_rq=yd=9002ovPDHLkV1k;b>?Z&K delta 560 zcmV-00?+;M1gHg&Uh2LI_r^WCw~G1Nkl4dSG%CZndg-Aj!-AlP_7c^@Bq$|{f?k9ujA}0i&6fp1e@L*l$Oa`niAk0* zyht=H8FM7t+V1Tk*V?89c^-e?-#MJ$`JJzz>$=VgU|4RDno|kCe9h-?NzB!DTz{=@ zHdvLidqUnp$lt@wF)Mek9Hy_w$3SQdqe(`R1oT{vO!^0#-ACxXdXDw0#d3isecfE_ zf678SM}^5qS~IezeLqgeT4txl845+niYDahCIZ2GG+4wXo*+n6DiX+cf`gB6S_-2} z^HZEUeu6jZ1ilNmcyRqBBJkUbrhomBWqO+K^Ve9=3P<9aI&S!Tu^DuxhX)9&sbU8^ zJqPhBbp=MH9ib#J$=bppPb2=pC~qV0Xm@NY2-uokc$;1S{I;5P4M3FdiJ4*t@yQYH zzZk__zlBcEj%5X6vrzz6S!~4uiXlP&?K`C98k}1n(^4gtk#lpSJbX5Zo_|x=zI)TJ zZ1Tg3dN&=r> Date: Fri, 18 Jan 2013 21:49:38 +0100 Subject: [PATCH 249/272] add multselect dnd --- apps/files/css/files.css | 11 ++++ apps/files/js/files.js | 135 ++++++++++++++++++++++++++++++++------- 2 files changed, 122 insertions(+), 24 deletions(-) diff --git a/apps/files/css/files.css b/apps/files/css/files.css index e65f724f68..9ff550189d 100644 --- a/apps/files/css/files.css +++ b/apps/files/css/files.css @@ -122,3 +122,14 @@ a.action>img { max-height:16px; max-width:16px; vertical-align:text-bottom; } #scanning-message{ top:40%; left:40%; position:absolute; display:none; } div.crumb a{ padding:0.9em 0 0.7em 0; } + +table.dragshadow { + width:auto; +} +table.dragshadow td.filename { + padding-left:36px; + padding-right:16px; +} +table.dragshadow td.size { + padding-right:8px; +} \ No newline at end of file diff --git a/apps/files/js/files.js b/apps/files/js/files.js index c817d8431e..0088778457 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -813,32 +813,102 @@ function updateBreadcrumb(breadcrumbHtml) { $('p.nav').empty().html(breadcrumbHtml); } -//options for file drag/dropp +var createDragShadow = function(event){ + //select dragged file + $(event.target).parents('tr').find('td input:first').prop('checked', true); + + var selectedFiles = getSelectedFiles(); + + //also update class when we dragged more than one file + if (selectedFiles.length > 1) { + $(event.target).parents('tr').addClass('selected'); + } + + // build dragshadow + var dragshadow = $('
'); + var tbody = $(''); + dragshadow.append(tbody); + + var dir=$('#dir').val(); + + $(selectedFiles).each(function(i,elem){ + var selected= $(event.target).parents('tr').hasClass('selected'); + var newtr = $('' + +''+elem.name+''+humanFileSize(elem.size)+'' + +''); + tbody.append(newtr); + if (elem.type === 'dir') { + newtr.find('td.filename').attr('style','background-image:url('+OC.imagePath('core', 'filetypes/folder.png')+')'); + } else { + getMimeIcon(elem.mime,function(path){ + newtr.find('td.filename').attr('style','background-image:url('+path+')'); + }); + } + }); + + return dragshadow; +} + +//options for file drag/drop var dragOptions={ - distance: 20, revert: 'invalid', opacity: 0.7, helper: 'clone', + revert: 'invalid', opacity: 0.7, zIndex: 100, appendTo: 'body', cursorAt: { left: -5, top: -5 }, + helper: createDragShadow, cursor: 'move', stop: function(event, ui) { $('#fileList tr td.filename').addClass('ui-draggable'); - } -}; -var folderDropOptions={ - drop: function( event, ui ) { - var file=ui.draggable.parent().data('file'); - var target=$(this).find('.nametext').text().trim(); - var dir=$('#dir').val(); - $.ajax({ - url: OC.filePath('files', 'ajax', 'move.php'), - data: "dir="+encodeURIComponent(dir)+"&file="+encodeURIComponent(file)+'&target='+encodeURIComponent(dir)+'/'+encodeURIComponent(target), - complete: function(data){boolOperationFinished(data, function(){ - var el = $('#fileList tr').filterAttr('data-file',file).find('td.filename'); - el.draggable('destroy'); - FileList.remove(file); - });} + //reset selection + $(ui.helper.find('tr')).each(function(i,row){ + var file = $(row).data('filename'); + var selected = $(row).data('selected'); + $('#fileList tr').filterAttr('data-file',file).find('td input:first').prop('checked',selected); }); } } + +var folderDropOptions={ + drop: function( event, ui ) { + //don't allow moving a file into a selected folder + if ($(event.target).parents('tr').find('td input:first').prop('checked') === true) { + return false; + } + + var target=$(this).find('.nametext').text().trim(); + + var files = ui.helper.find('tr'); + $(files).each(function(i,row){ + var dir = $(row).data('dir'); + var file = $(row).data('filename'); + $.get(OC.filePath('files', 'ajax', 'move.php'), { dir: dir, file: file, target: dir+'/'+target }, function(result) { + if (result) { + if (result.status === 'success') { + //recalculate folder size + var oldSize = $('#fileList tr').filterAttr('data-file',target).data('size'); + var newSize = oldSize + $('#fileList tr').filterAttr('data-file',file).data('size'); + $('#fileList tr').filterAttr('data-file',target).data('size', newSize); + $('#fileList tr').filterAttr('data-file',target).find('td.filesize').text(humanFileSize(newSize)); + + FileList.remove(file); + procesSelection(); + $('#notification').hide(); + } else { + $('#notification').hide(); + $('#notification').text(result.data.message); + $('#notification').fadeIn(); + } + } else { + OC.dialogs.alert(t('Error moving file')); + } + }); + }); + //reset checkbox if we dragged a single file + if (files.length == 1) { + $(event.target).parents('tr').find('td input:first').prop('checked', $(event.target).parents('tr').hasClass('selected')); + } + }, + tolerance: 'pointer' +} + var crumbDropOptions={ drop: function( event, ui ) { - var file=ui.draggable.parent().data('file'); var target=$(this).data('dir'); var dir=$('#dir').val(); while(dir.substr(0,1)=='/'){//remove extra leading /'s @@ -851,13 +921,30 @@ var crumbDropOptions={ if(target==dir || target+'/'==dir){ return; } - $.ajax({ - url: OC.filePath('files', 'ajax', 'move.php'), - data: "dir="+encodeURIComponent(dir)+"&file="+encodeURIComponent(file)+'&target='+encodeURIComponent(target), - complete: function(data){boolOperationFinished(data, function(){ - FileList.remove(file); - });} + var files = ui.helper.find('tr'); + $(files).each(function(i,row){ + var dir = $(row).data('dir'); + var file = $(row).data('filename'); + $.get(OC.filePath('files', 'ajax', 'move.php'), { dir: dir, file: file, target: target }, function(result) { + if (result) { + if (result.status === 'success') { + FileList.remove(file); + procesSelection(); + $('#notification').hide(); + } else { + $('#notification').hide(); + $('#notification').text(result.data.message); + $('#notification').fadeIn(); + } + } else { + OC.dialogs.alert(t('Error moving file')); + } + }); }); + //reset checkbox if we dragged a single file + if (files.length == 1) { + $(event.target).parents('tr').find('td input:first').prop('checked', $(event.target).parents('tr').hasClass('selected')); + } }, tolerance: 'pointer' } From 1d0ee61f3a136372b26e90fa35f726c77aa95416 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Friedrich=20Dreyer?= Date: Fri, 18 Jan 2013 22:16:04 +0100 Subject: [PATCH 250/272] use post instead of get --- apps/files/ajax/move.php | 6 +++--- apps/files/js/files.js | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/apps/files/ajax/move.php b/apps/files/ajax/move.php index 4ebc3f42d9..74374c57b8 100644 --- a/apps/files/ajax/move.php +++ b/apps/files/ajax/move.php @@ -7,9 +7,9 @@ OCP\JSON::checkLoggedIn(); OCP\JSON::callCheck(); // Get data -$dir = stripslashes($_GET["dir"]); -$file = stripslashes($_GET["file"]); -$target = stripslashes(rawurldecode($_GET["target"])); +$dir = stripslashes($_POST["dir"]); +$file = stripslashes($_POST["file"]); +$target = stripslashes(rawurldecode($_POST["target"])); $l=OC_L10N::get('files'); diff --git a/apps/files/js/files.js b/apps/files/js/files.js index 0088778457..34772915a6 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -877,7 +877,7 @@ var folderDropOptions={ $(files).each(function(i,row){ var dir = $(row).data('dir'); var file = $(row).data('filename'); - $.get(OC.filePath('files', 'ajax', 'move.php'), { dir: dir, file: file, target: dir+'/'+target }, function(result) { + $.post(OC.filePath('files', 'ajax', 'move.php'), { dir: dir, file: file, target: dir+'/'+target }, function(result) { if (result) { if (result.status === 'success') { //recalculate folder size @@ -925,7 +925,7 @@ var crumbDropOptions={ $(files).each(function(i,row){ var dir = $(row).data('dir'); var file = $(row).data('filename'); - $.get(OC.filePath('files', 'ajax', 'move.php'), { dir: dir, file: file, target: target }, function(result) { + $.post(OC.filePath('files', 'ajax', 'move.php'), { dir: dir, file: file, target: target }, function(result) { if (result) { if (result.status === 'success') { FileList.remove(file); From 99dfaee1f616c6ffdba8007badc9b5afe427c653 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Friedrich=20Dreyer?= Date: Mon, 21 Jan 2013 14:58:55 +0100 Subject: [PATCH 251/272] reset checkbox after getting selected files instead of ofter dragging, speed up revert --- apps/files/js/files.js | 31 +++++++++++++------------------ 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/apps/files/js/files.js b/apps/files/js/files.js index 34772915a6..288a87c7e3 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -815,10 +815,19 @@ function updateBreadcrumb(breadcrumbHtml) { var createDragShadow = function(event){ //select dragged file - $(event.target).parents('tr').find('td input:first').prop('checked', true); + var isDragSelected = $(event.target).parents('tr').find('td input:first').prop('checked'); + if (!isDragSelected) { + //select dragged file + $(event.target).parents('tr').find('td input:first').prop('checked',true); + } var selectedFiles = getSelectedFiles(); + if (!isDragSelected && selectedFiles.length == 1) { + //revert the selection + $(event.target).parents('tr').find('td input:first').prop('checked',false); + } + //also update class when we dragged more than one file if (selectedFiles.length > 1) { $(event.target).parents('tr').addClass('selected'); @@ -832,8 +841,7 @@ var createDragShadow = function(event){ var dir=$('#dir').val(); $(selectedFiles).each(function(i,elem){ - var selected= $(event.target).parents('tr').hasClass('selected'); - var newtr = $('' + var newtr = $('' +''+elem.name+''+humanFileSize(elem.size)+'' +''); tbody.append(newtr); @@ -851,16 +859,11 @@ var createDragShadow = function(event){ //options for file drag/drop var dragOptions={ - revert: 'invalid', opacity: 0.7, zIndex: 100, appendTo: 'body', cursorAt: { left: -5, top: -5 }, + revert: 'invalid', revertDuration: 300, + opacity: 0.7, zIndex: 100, appendTo: 'body', cursorAt: { left: -5, top: -5 }, helper: createDragShadow, cursor: 'move', stop: function(event, ui) { $('#fileList tr td.filename').addClass('ui-draggable'); - //reset selection - $(ui.helper.find('tr')).each(function(i,row){ - var file = $(row).data('filename'); - var selected = $(row).data('selected'); - $('#fileList tr').filterAttr('data-file',file).find('td input:first').prop('checked',selected); - }); } } @@ -899,10 +902,6 @@ var folderDropOptions={ } }); }); - //reset checkbox if we dragged a single file - if (files.length == 1) { - $(event.target).parents('tr').find('td input:first').prop('checked', $(event.target).parents('tr').hasClass('selected')); - } }, tolerance: 'pointer' } @@ -941,10 +940,6 @@ var crumbDropOptions={ } }); }); - //reset checkbox if we dragged a single file - if (files.length == 1) { - $(event.target).parents('tr').find('td input:first').prop('checked', $(event.target).parents('tr').hasClass('selected')); - } }, tolerance: 'pointer' } From a6e5f4b85c0aea807f95347e68f88faf08dfbee1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Wed, 30 Jan 2013 13:01:53 +0100 Subject: [PATCH 252/272] some minor improvements --- apps/files_trashbin/ajax/undelete.php | 3 ++- apps/files_trashbin/js/trash.js | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/apps/files_trashbin/ajax/undelete.php b/apps/files_trashbin/ajax/undelete.php index 178d85ccf3..a7bb5b9de2 100644 --- a/apps/files_trashbin/ajax/undelete.php +++ b/apps/files_trashbin/ajax/undelete.php @@ -1,6 +1,6 @@ Date: Wed, 30 Jan 2013 13:04:32 +0100 Subject: [PATCH 253/272] fix OC_Log::ERROR call --- apps/files_trashbin/lib/trash.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/files_trashbin/lib/trash.php b/apps/files_trashbin/lib/trash.php index 68473c46b1..b0c2284d97 100644 --- a/apps/files_trashbin/lib/trash.php +++ b/apps/files_trashbin/lib/trash.php @@ -94,7 +94,7 @@ class Trashbin { $query = \OC_DB::prepare('SELECT location,type FROM *PREFIX*files_trash WHERE user=? AND id=? AND timestamp=?'); $result = $query->execute(array($user,$filename,$timestamp))->fetchAll(); if ( count($result) != 1 ) { - \OC_Log::write('files_trashbin', 'trash bin database inconsistent!', OC_Log::ERROR); + \OC_Log::write('files_trashbin', 'trash bin database inconsistent!', \OC_Log::ERROR); return false; } From 81a398c22c1bf43c72b91c7639671c4eb5729ab0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn=20Friedrich=20Dreyer?= Date: Wed, 30 Jan 2013 13:29:24 +0100 Subject: [PATCH 254/272] use $.trim() for IE8 --- apps/files/js/files.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/files/js/files.js b/apps/files/js/files.js index 288a87c7e3..13367b3362 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -874,7 +874,7 @@ var folderDropOptions={ return false; } - var target=$(this).find('.nametext').text().trim(); + var target=$.trim($(this).find('.nametext').text()); var files = ui.helper.find('tr'); $(files).each(function(i,row){ @@ -1045,7 +1045,7 @@ function getUniqueName(name){ num=parseInt(numMatch[numMatch.length-1])+1; base=base.split('(') base.pop(); - base=base.join('(').trim(); + base=$.trim(base.join('(')); } name=base+' ('+num+')'; if (extension) { From 0bce22966cf6a003ad8273373383300ebc94f52a Mon Sep 17 00:00:00 2001 From: irgsmirx Date: Wed, 30 Jan 2013 14:27:08 +0100 Subject: [PATCH 255/272] Update lib/base.php Correction of typo "locate" -> "locale". --- lib/base.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/base.php b/lib/base.php index f9818d3514..ebaf33814e 100644 --- a/lib/base.php +++ b/lib/base.php @@ -502,7 +502,7 @@ class OC // write error into log if locale can't be set if (OC_Util::issetlocaleworking() == false) { - OC_Log::write('core', 'setting locate to en_US.UTF-8 failed. Support is probably not installed on your system', OC_Log::ERROR); + OC_Log::write('core', 'setting locale to en_US.UTF-8 failed. Support is probably not installed on your system', OC_Log::ERROR); } if (OC_Config::getValue('installed', false)) { if (OC_Appconfig::getValue('core', 'backgroundjobs_mode', 'ajax') == 'ajax') { From 1592764a60263a8ecfe691e25f522514eae5022c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Wed, 30 Jan 2013 14:32:20 +0100 Subject: [PATCH 256/272] show a spinner instead of the delete button while moving files to the trash bin --- apps/files/css/files.css | 1 + apps/files/js/filelist.js | 18 +++++++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/apps/files/css/files.css b/apps/files/css/files.css index f9406f5865..f337645845 100644 --- a/apps/files/css/files.css +++ b/apps/files/css/files.css @@ -111,6 +111,7 @@ table td.filename form { font-size:.85em; margin-left:3em; margin-right:3em; } } #fileList .fileactions a.action img { position:relative; top:.2em; } #fileList a.action { display:inline; margin:-.5em 0; padding:1em .5em 1em .5em !important; } +#fileList img.move2trash { display:inline; margin:-.5em 0; padding:1em .5em 1em .5em !important; float:right; } a.action.delete { float:right; } a.action>img { max-height:16px; max-width:16px; vertical-align:text-bottom; } .selectedActions { display:none; float:right; } diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 5cb8849ffe..28230409d2 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -271,13 +271,20 @@ var FileList={ } }, do_delete:function(files){ + if(files.substr){ + files=[files]; + } + for (var i in files) { + var deleteAction = $('tr').filterAttr('data-file',files[i]).children("td.date").children(".action.delete"); + var oldHTML = deleteAction[0].outerHTML; + var newHTML = ''; + deleteAction[0].outerHTML = newHTML; + } // Finish any existing actions if (FileList.lastAction) { FileList.lastAction(); } - if(files.substr){ - files=[files]; - } + var fileNames = JSON.stringify(files); $.post(OC.filePath('files', 'ajax', 'delete.php'), {dir:$('#dir').val(),files:fileNames}, @@ -290,6 +297,11 @@ var FileList={ files.removeClass('selected'); }); procesSelection(); + } else { + $.each(files,function(index,file) { + var deleteAction = $('tr').filterAttr('data-file',file).children("td.date").children(".move2trash"); + deleteAction[0].outerHTML = oldHTML; + }); } }); } From c62a38fcb1b1445d107ac4096e89b134d7654920 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Wed, 30 Jan 2013 15:35:24 +0100 Subject: [PATCH 257/272] add spinner for undelete operation for visualisation --- apps/files_trashbin/js/trash.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/apps/files_trashbin/js/trash.js b/apps/files_trashbin/js/trash.js index 31d16436b2..0aa9f8dade 100644 --- a/apps/files_trashbin/js/trash.js +++ b/apps/files_trashbin/js/trash.js @@ -4,6 +4,9 @@ $(document).ready(function() { if (typeof FileActions !== 'undefined') { FileActions.register('all', 'Undelete', OC.PERMISSION_READ, OC.imagePath('core', 'actions/undelete.png'), function(filename) { var tr=$('tr').filterAttr('data-file', filename); + var spinner = ''; + var undeleteAction = $('tr').filterAttr('data-file',filename).children("td.date"); + undeleteAction[0].innerHTML = undeleteAction[0].innerHTML+spinner; $.post(OC.filePath('files_trashbin','ajax','undelete.php'), {files:tr.attr('data-file'), dirlisting:tr.attr('data-dirlisting') }, function(result){ @@ -63,8 +66,16 @@ $(document).ready(function() { }); $('.undelete').click('click',function(event) { - var fileslist=getSelectedFiles('file').join(';'); + var spinner = ''; + var files=getSelectedFiles('file'); + var fileslist=files.join(';'); var dirlisting=getSelectedFiles('dirlisting')[0]; + + for (var i in files) { + var undeleteAction = $('tr').filterAttr('data-file',files[i]).children("td.date"); + undeleteAction[0].innerHTML = undeleteAction[0].innerHTML+spinner; + } + $.post(OC.filePath('files_trashbin','ajax','undelete.php'), {files:fileslist, dirlisting:dirlisting}, function(result){ From 9ca0a93847dfbf9266f7e76e90c018d4dcff7f5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Wed, 30 Jan 2013 16:20:13 +0100 Subject: [PATCH 258/272] make spinner title translatable --- apps/files/js/filelist.js | 2 +- apps/files_trashbin/js/trash.js | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 28230409d2..d0810f7014 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -277,7 +277,7 @@ var FileList={ for (var i in files) { var deleteAction = $('tr').filterAttr('data-file',files[i]).children("td.date").children(".action.delete"); var oldHTML = deleteAction[0].outerHTML; - var newHTML = ''; + var newHTML = ''; deleteAction[0].outerHTML = newHTML; } // Finish any existing actions diff --git a/apps/files_trashbin/js/trash.js b/apps/files_trashbin/js/trash.js index 0aa9f8dade..8a407b88a0 100644 --- a/apps/files_trashbin/js/trash.js +++ b/apps/files_trashbin/js/trash.js @@ -4,7 +4,7 @@ $(document).ready(function() { if (typeof FileActions !== 'undefined') { FileActions.register('all', 'Undelete', OC.PERMISSION_READ, OC.imagePath('core', 'actions/undelete.png'), function(filename) { var tr=$('tr').filterAttr('data-file', filename); - var spinner = ''; + var spinner = ''; var undeleteAction = $('tr').filterAttr('data-file',filename).children("td.date"); undeleteAction[0].innerHTML = undeleteAction[0].innerHTML+spinner; $.post(OC.filePath('files_trashbin','ajax','undelete.php'), @@ -66,7 +66,7 @@ $(document).ready(function() { }); $('.undelete').click('click',function(event) { - var spinner = ''; + var spinner = ''; var files=getSelectedFiles('file'); var fileslist=files.join(';'); var dirlisting=getSelectedFiles('dirlisting')[0]; From 92921ceefd66f49ecf0915fe87997caefa24c6bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Schie=C3=9Fle?= Date: Wed, 30 Jan 2013 16:28:41 +0100 Subject: [PATCH 259/272] make strings translatable --- settings/templates/users.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/settings/templates/users.php b/settings/templates/users.php index f30c21efae..4d7c29678c 100644 --- a/settings/templates/users.php +++ b/settings/templates/users.php @@ -94,11 +94,11 @@ $_['subadmingroups'] = array_flip($items); change display name + alt="t("change display name")?>" title="t("change display name")?>"/> ●●●●●●● set new password + alt="t("set new password")?>" title="t("set new password")?>"/>