From c546874159d7880e3c8665eaaa7ae9a42b66adbb Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Sat, 2 Mar 2013 17:17:11 +0100 Subject: [PATCH 01/49] Convert OC_Appconfig to object interface Implemented unittest for OC\AppConfig --- lib/appconfig.php | 148 +++++++++++++++++--------------- lib/legacy/appconfig.php | 120 ++++++++++++++++++++++++++ tests/lib/appconfig.php | 181 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 382 insertions(+), 67 deletions(-) create mode 100644 lib/legacy/appconfig.php diff --git a/lib/appconfig.php b/lib/appconfig.php index e615d83817..9a21bfb0a2 100644 --- a/lib/appconfig.php +++ b/lib/appconfig.php @@ -33,11 +33,21 @@ * */ +namespace OC; + +use \OC\DB\Connection; + /** * This class provides an easy way for apps to store config values in the * database. */ -class OC_Appconfig{ +class AppConfig { + protected $conn; + + public function __construct(Connection $conn) { + $this->conn = $conn; + } + /** * @brief Get all apps using the config * @return array with app ids @@ -45,16 +55,14 @@ class OC_Appconfig{ * This function returns a list of all apps that have at least one * entry in the appconfig table. */ - public static function getApps() { - // No magic in here! - $query = OC_DB::prepare( 'SELECT DISTINCT `appid` FROM `*PREFIX*appconfig`' ); - $result = $query->execute(); + public function getApps() { + $query = 'SELECT DISTINCT `appid` FROM `*PREFIX*appconfig`'; + $result = $this->conn->executeQuery( $query ); $apps = array(); - while( $row = $result->fetchRow()) { - $apps[] = $row["appid"]; + while( $appid = $result->fetchColumn()) { + $apps[] = $appid; } - return $apps; } @@ -66,14 +74,13 @@ class OC_Appconfig{ * This function gets all keys of an app. Please note that the values are * not returned. */ - public static function getKeys( $app ) { - // No magic in here as well - $query = OC_DB::prepare( 'SELECT `configkey` FROM `*PREFIX*appconfig` WHERE `appid` = ?' ); - $result = $query->execute( array( $app )); + public function getKeys( $app ) { + $query = 'SELECT `configkey` FROM `*PREFIX*appconfig` WHERE `appid` = ?'; + $result = $this->conn->executeQuery( $query, array( $app )); $keys = array(); - while( $row = $result->fetchRow()) { - $keys[] = $row["configkey"]; + while( $key = $result->fetchColumn()) { + $keys[] = $key; } return $keys; @@ -89,15 +96,13 @@ class OC_Appconfig{ * This function gets a value from the appconfig table. If the key does * not exist the default value will be returned */ - public static function getValue( $app, $key, $default = null ) { - // At least some magic in here :-) - $query = OC_DB::prepare( 'SELECT `configvalue` FROM `*PREFIX*appconfig`' - .' WHERE `appid` = ? AND `configkey` = ?' ); - $result = $query->execute( array( $app, $key )); - $row = $result->fetchRow(); + public function getValue( $app, $key, $default = null ) { + $query = 'SELECT `configvalue` FROM `*PREFIX*appconfig`' + .' WHERE `appid` = ? AND `configkey` = ?'; + $row = $this->conn->fetchAssoc($query, array( $app, $key )); if($row) { - return $row["configvalue"]; - }else{ + return $row['configvalue']; + } else { return $default; } } @@ -108,8 +113,8 @@ class OC_Appconfig{ * @param string $key * @return bool */ - public static function hasKey($app, $key) { - $exists = self::getKeys( $app ); + public function hasKey($app, $key) { + $exists = $this->getKeys( $app ); return in_array( $key, $exists ); } @@ -118,21 +123,27 @@ class OC_Appconfig{ * @param string $app app * @param string $key key * @param string $value value - * @return bool * * Sets a value. If the key did not exist before it will be created. */ - public static function setValue( $app, $key, $value ) { - // Does the key exist? yes: update. No: insert - if(! self::hasKey($app, $key)) { - $query = OC_DB::prepare( 'INSERT INTO `*PREFIX*appconfig` ( `appid`, `configkey`, `configvalue` )' - .' VALUES( ?, ?, ? )' ); - $query->execute( array( $app, $key, $value )); - } - else{ - $query = OC_DB::prepare( 'UPDATE `*PREFIX*appconfig` SET `configvalue` = ?' - .' WHERE `appid` = ? AND `configkey` = ?' ); - $query->execute( array( $value, $app, $key )); + public function setValue( $app, $key, $value ) { + // Does the key exist? no: insert, yes: update. + if ( !$this->hasKey($app, $key)) { + $data = array( + 'appid' => $app, + 'configkey' => $key, + 'configvalue' => $value, + ); + $this->conn->insert('*PREFIX*appconfig', $data); + } else { + $data = array( + 'configvalue' => $value, + ); + $where = array( + 'appid' => $app, + 'configkey' => $key, + ); + $this->conn->update('*PREFIX*appconfig', $data, $where); } } @@ -144,12 +155,12 @@ class OC_Appconfig{ * * Deletes a key. */ - public static function deleteKey( $app, $key ) { - // Boring! - $query = OC_DB::prepare( 'DELETE FROM `*PREFIX*appconfig` WHERE `appid` = ? AND `configkey` = ?' ); - $query->execute( array( $app, $key )); - - return true; + public function deleteKey( $app, $key ) { + $where = array( + 'appid' => $app, + 'configkey' => $key, + ); + $this->conn->delete('*PREFIX*appconfig', $where); } /** @@ -159,12 +170,11 @@ class OC_Appconfig{ * * Removes all keys in appconfig belonging to the app. */ - public static function deleteApp( $app ) { - // Nothing special - $query = OC_DB::prepare( 'DELETE FROM `*PREFIX*appconfig` WHERE `appid` = ?' ); - $query->execute( array( $app )); - - return true; + public function deleteApp( $app ) { + $where = array( + 'appid' => $app, + ); + $this->conn->delete('*PREFIX*appconfig', $where); } /** @@ -173,31 +183,35 @@ class OC_Appconfig{ * @param key * @return array */ - public static function getValues($app, $key) { - if($app!==false and $key!==false) { + public function getValues($app, $key) { + if(($app!==false) == ($key!==false)) { return false; } - $fields='`configvalue`'; - $where='WHERE'; - $params=array(); + + $fields = '`configvalue`'; + $where = 'WHERE'; + $params = array(); if($app!==false) { - $fields.=', `configkey`'; - $where.=' `appid` = ?'; - $params[]=$app; - $key='configkey'; + $fields .= ', `configkey`'; + $where .= ' `appid` = ?'; + $params[] = $app; + $key = 'configkey'; }else{ - $fields.=', `appid`'; - $where.=' `configkey` = ?'; - $params[]=$key; - $key='appid'; + $fields .= ', `appid`'; + $where .= ' `configkey` = ?'; + $params[] = $key; + $key = 'appid'; } - $queryString='SELECT '.$fields.' FROM `*PREFIX*appconfig` '.$where; - $query=OC_DB::prepare($queryString); - $result=$query->execute($params); - $values=array(); - while($row=$result->fetchRow()) { - $values[$row[$key]]=$row['configvalue']; + $query = 'SELECT '.$fields.' FROM `*PREFIX*appconfig` '.$where; + $result = $this->conn->executeQuery( $query, $params ); + + $values = array(); + while( $row = $result->fetch((\PDO::FETCH_ASSOC))) { + $values[$row[$key]] = $row['configvalue']; } + return $values; } } + +require_once __DIR__.'/legacy/'.basename(__FILE__); diff --git a/lib/legacy/appconfig.php b/lib/legacy/appconfig.php new file mode 100644 index 0000000000..0ca6d4150e --- /dev/null +++ b/lib/legacy/appconfig.php @@ -0,0 +1,120 @@ +. + * + */ + +/** + * This class provides an easy way for apps to store config values in the + * database. + */ +OC_Appconfig::$object = new \OC\AppConfig(OC_DB::getConnection()); +class OC_Appconfig{ + public static $object; + /** + * @brief Get all apps using the config + * @return array with app ids + * + * This function returns a list of all apps that have at least one + * entry in the appconfig table. + */ + public static function getApps() { + return self::$object->getApps(); + } + + /** + * @brief Get the available keys for an app + * @param string $app the app we are looking for + * @return array with key names + * + * This function gets all keys of an app. Please note that the values are + * not returned. + */ + public static function getKeys( $app ) { + return self::$object->getKeys( $app ); + } + + /** + * @brief Gets the config value + * @param string $app app + * @param string $key key + * @param string $default = null, default value if the key does not exist + * @return string the value or $default + * + * This function gets a value from the appconfig table. If the key does + * not exist the default value will be returned + */ + public static function getValue( $app, $key, $default = null ) { + return self::$object->getValue($app, $key, $default); + } + + /** + * @brief check if a key is set in the appconfig + * @param string $app + * @param string $key + * @return bool + */ + public static function hasKey($app, $key) { + return self::$object->hasKey($app, $key); + } + + /** + * @brief sets a value in the appconfig + * @param string $app app + * @param string $key key + * @param string $value value + * + * Sets a value. If the key did not exist before it will be created. + */ + public static function setValue( $app, $key, $value ) { + self::$object->setValue( $app, $key, $value ); + } + + /** + * @brief Deletes a key + * @param string $app app + * @param string $key key + * + * Deletes a key. + */ + public static function deleteKey( $app, $key ) { + self::$object->deleteKey( $app, $key ); + } + + /** + * @brief Remove app from appconfig + * @param string $app app + * + * Removes all keys in appconfig belonging to the app. + */ + public static function deleteApp( $app ) { + self::$object->deleteApp( $app ); + } + + /** + * get multiply values, either the app or key can be used as wildcard by setting it to false + * @param app + * @param key + * @return array + */ + public static function getValues($app, $key) { + return self::$object->getValues($app, $key); + } +} diff --git a/tests/lib/appconfig.php b/tests/lib/appconfig.php index 4d82cd5ba7..1b7a1c668e 100644 --- a/tests/lib/appconfig.php +++ b/tests/lib/appconfig.php @@ -1,6 +1,7 @@ + * Copyright (c) 2013 Bart Visscher * This file is licensed under the Affero General Public License version 3 or * later. * See the COPYING-README file. @@ -130,3 +131,183 @@ class Test_Appconfig extends PHPUnit_Framework_TestCase { $this->assertEquals($expected, $values); } } + +class Test_AppConfig_Object extends PHPUnit_Framework_TestCase { + public function testGetApps() + { + $statementMock = $this->getMock('\Doctrine\DBAL\Statement', array(), array(), '', false); + $statementMock->expects($this->exactly(2)) + ->method('fetchColumn') + ->will($this->onConsecutiveCalls('foo', false)); + $connectionMock = $this->getMock('\OC\DB\Connection', array(), array(), '', false); + $connectionMock->expects($this->once()) + ->method('executeQuery') + ->with($this->equalTo('SELECT DISTINCT `appid` FROM `*PREFIX*appconfig`')) + ->will($this->returnValue($statementMock)); + + $appconfig = new OC\AppConfig($connectionMock); + $apps = $appconfig->getApps(); + $this->assertEquals(array('foo'), $apps); + } + + public function testGetKeys() + { + $statementMock = $this->getMock('\Doctrine\DBAL\Statement', array(), array(), '', false); + $statementMock->expects($this->exactly(2)) + ->method('fetchColumn') + ->will($this->onConsecutiveCalls('foo', false)); + $connectionMock = $this->getMock('\OC\DB\Connection', array(), array(), '', false); + $connectionMock->expects($this->once()) + ->method('executeQuery') + ->with($this->equalTo('SELECT `configkey` FROM `*PREFIX*appconfig` WHERE `appid` = ?'), + $this->equalTo(array('bar'))) + ->will($this->returnValue($statementMock)); + + $appconfig = new OC\AppConfig($connectionMock); + $keys = $appconfig->getKeys('bar'); + $this->assertEquals(array('foo'), $keys); + } + + public function testGetValue() + { + $connectionMock = $this->getMock('\OC\DB\Connection', array(), array(), '', false); + $connectionMock->expects($this->exactly(2)) + ->method('fetchAssoc') + ->with($this->equalTo('SELECT `configvalue` FROM `*PREFIX*appconfig` WHERE `appid` = ? AND `configkey` = ?'), + $this->equalTo(array('bar', 'red'))) + ->will($this->onConsecutiveCalls(array('configvalue'=>'foo'), null)); + + $appconfig = new OC\AppConfig($connectionMock); + $value = $appconfig->getValue('bar', 'red'); + $this->assertEquals('foo', $value); + $value = $appconfig->getValue('bar', 'red', 'def'); + $this->assertEquals('def', $value); + } + + public function testHasKey() + { + $statementMock = $this->getMock('\Doctrine\DBAL\Statement', array(), array(), '', false); + $statementMock->expects($this->exactly(3)) + ->method('fetchColumn') + ->will($this->onConsecutiveCalls('foo', false, false)); + $connectionMock = $this->getMock('\OC\DB\Connection', array(), array(), '', false); + $connectionMock->expects($this->exactly(2)) + ->method('executeQuery') + ->with($this->equalTo('SELECT `configkey` FROM `*PREFIX*appconfig` WHERE `appid` = ?'), + $this->equalTo(array('bar'))) + ->will($this->returnValue($statementMock)); + + $appconfig = new OC\AppConfig($connectionMock); + $this->assertTrue($appconfig->hasKey('bar', 'foo')); + $this->assertFalse($appconfig->hasKey('bar', 'foo')); + } + + public function testSetValue() + { + $statementMock = $this->getMock('\Doctrine\DBAL\Statement', array(), array(), '', false); + $statementMock->expects($this->exactly(4)) + ->method('fetchColumn') + ->will($this->onConsecutiveCalls('foo', false, 'foo', false)); + $connectionMock = $this->getMock('\OC\DB\Connection', array(), array(), '', false); + $connectionMock->expects($this->exactly(2)) + ->method('executeQuery') + ->with($this->equalTo('SELECT `configkey` FROM `*PREFIX*appconfig` WHERE `appid` = ?'), + $this->equalTo(array('bar'))) + ->will($this->returnValue($statementMock)); + $connectionMock->expects($this->once()) + ->method('insert') + ->with($this->equalTo('*PREFIX*appconfig'), + $this->equalTo( + array( + 'appid' => 'bar', + 'configkey' => 'moo', + 'configvalue' => 'v1', + ) + )); + $connectionMock->expects($this->once()) + ->method('update') + ->with($this->equalTo('*PREFIX*appconfig'), + $this->equalTo( + array( + 'configvalue' => 'v2', + )), + $this->equalTo( + array( + 'appid' => 'bar', + 'configkey' => 'foo', + ) + )); + + $appconfig = new OC\AppConfig($connectionMock); + $appconfig->setValue('bar', 'moo', 'v1'); + $appconfig->setValue('bar', 'foo', 'v2'); + } + + public function testDeleteKey() + { + $connectionMock = $this->getMock('\OC\DB\Connection', array(), array(), '', false); + $connectionMock->expects($this->once()) + ->method('delete') + ->with($this->equalTo('*PREFIX*appconfig'), + $this->equalTo( + array( + 'appid' => 'bar', + 'configkey' => 'foo', + ) + )); + + $appconfig = new OC\AppConfig($connectionMock); + $appconfig->deleteKey('bar', 'foo'); + } + + public function testDeleteApp() + { + $connectionMock = $this->getMock('\OC\DB\Connection', array(), array(), '', false); + $connectionMock->expects($this->once()) + ->method('delete') + ->with($this->equalTo('*PREFIX*appconfig'), + $this->equalTo( + array( + 'appid' => 'bar', + ) + )); + + $appconfig = new OC\AppConfig($connectionMock); + $appconfig->deleteApp('bar'); + } + + public function testGetValues() + { + $statementMock = $this->getMock('\Doctrine\DBAL\Statement', array(), array(), '', false); + $statementMock->expects($this->exactly(4)) + ->method('fetch') + ->with(\PDO::FETCH_ASSOC) + ->will($this->onConsecutiveCalls( + array('configvalue' =>'bar', 'configkey' => 'x'), + false, + array('configvalue' =>'foo', 'appid' => 'y'), + false + )); + $connectionMock = $this->getMock('\OC\DB\Connection', array(), array(), '', false); + $connectionMock->expects($this->at(0)) + ->method('executeQuery') + ->with($this->equalTo('SELECT `configvalue`, `configkey` FROM `*PREFIX*appconfig` WHERE `appid` = ?'), + $this->equalTo(array('foo'))) + ->will($this->returnValue($statementMock)); + $connectionMock->expects($this->at(1)) + ->method('executeQuery') + ->with($this->equalTo('SELECT `configvalue`, `appid` FROM `*PREFIX*appconfig` WHERE `configkey` = ?'), + $this->equalTo(array('bar'))) + ->will($this->returnValue($statementMock)); + + $appconfig = new OC\AppConfig($connectionMock); + $values = $appconfig->getValues('foo', false); + //$this->assertEquals(array('x'=> 'bar'), $values); + $values = $appconfig->getValues(false, 'bar'); + //$this->assertEquals(array('y'=> 'foo'), $values); + $values = $appconfig->getValues(false, false); + //$this->assertEquals(false, $values); + $values = $appconfig->getValues('x', 'x'); + //$this->assertEquals(false, $values); + } +} From ca88cf93ae929c8bd35e92665958c34b65701493 Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Thu, 21 Mar 2013 20:36:40 +0100 Subject: [PATCH 02/49] check for valid appinfo in installer --- lib/installer.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/installer.php b/lib/installer.php index e082c7eeee..89358e7827 100644 --- a/lib/installer.php +++ b/lib/installer.php @@ -396,6 +396,9 @@ class OC_Installer{ include OC_App::getAppPath($app)."/appinfo/install.php"; } $info=OC_App::getAppInfo($app); + if (is_null($info)) { + return; + } OC_Appconfig::setValue($app, 'installed_version', OC_App::getAppVersion($app)); //set remote/public handelers From 2f79e94a350e2c24f88fcd98cb03e9a89298d154 Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Sun, 25 Aug 2013 22:34:54 +0200 Subject: [PATCH 03/49] Style fixes --- lib/appconfig.php | 46 +++++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/lib/appconfig.php b/lib/appconfig.php index 9a21bfb0a2..7c84faf7f1 100644 --- a/lib/appconfig.php +++ b/lib/appconfig.php @@ -42,8 +42,14 @@ use \OC\DB\Connection; * database. */ class AppConfig { + /** + * @var \OC\DB\Connection $conn + */ protected $conn; + /** + * @param \OC\DB\Connection $conn + */ public function __construct(Connection $conn) { $this->conn = $conn; } @@ -57,10 +63,10 @@ class AppConfig { */ public function getApps() { $query = 'SELECT DISTINCT `appid` FROM `*PREFIX*appconfig`'; - $result = $this->conn->executeQuery( $query ); + $result = $this->conn->executeQuery($query); $apps = array(); - while( $appid = $result->fetchColumn()) { + while ($appid = $result->fetchColumn()) { $apps[] = $appid; } return $apps; @@ -74,12 +80,12 @@ class AppConfig { * This function gets all keys of an app. Please note that the values are * not returned. */ - public function getKeys( $app ) { + public function getKeys($app) { $query = 'SELECT `configkey` FROM `*PREFIX*appconfig` WHERE `appid` = ?'; - $result = $this->conn->executeQuery( $query, array( $app )); + $result = $this->conn->executeQuery($query, array($app)); $keys = array(); - while( $key = $result->fetchColumn()) { + while ($key = $result->fetchColumn()) { $keys[] = $key; } @@ -96,11 +102,11 @@ class AppConfig { * This function gets a value from the appconfig table. If the key does * not exist the default value will be returned */ - public function getValue( $app, $key, $default = null ) { + public function getValue($app, $key, $default = null) { $query = 'SELECT `configvalue` FROM `*PREFIX*appconfig`' .' WHERE `appid` = ? AND `configkey` = ?'; - $row = $this->conn->fetchAssoc($query, array( $app, $key )); - if($row) { + $row = $this->conn->fetchAssoc($query, array($app, $key)); + if ($row) { return $row['configvalue']; } else { return $default; @@ -114,8 +120,8 @@ class AppConfig { * @return bool */ public function hasKey($app, $key) { - $exists = $this->getKeys( $app ); - return in_array( $key, $exists ); + $exists = $this->getKeys($app); + return in_array($key, $exists); } /** @@ -126,9 +132,9 @@ class AppConfig { * * Sets a value. If the key did not exist before it will be created. */ - public function setValue( $app, $key, $value ) { + public function setValue($app, $key, $value) { // Does the key exist? no: insert, yes: update. - if ( !$this->hasKey($app, $key)) { + if (!$this->hasKey($app, $key)) { $data = array( 'appid' => $app, 'configkey' => $key, @@ -155,7 +161,7 @@ class AppConfig { * * Deletes a key. */ - public function deleteKey( $app, $key ) { + public function deleteKey($app, $key) { $where = array( 'appid' => $app, 'configkey' => $key, @@ -170,7 +176,7 @@ class AppConfig { * * Removes all keys in appconfig belonging to the app. */ - public function deleteApp( $app ) { + public function deleteApp($app) { $where = array( 'appid' => $app, ); @@ -184,19 +190,19 @@ class AppConfig { * @return array */ public function getValues($app, $key) { - if(($app!==false) == ($key!==false)) { + if (($app !== false) == ($key !== false)) { return false; } $fields = '`configvalue`'; $where = 'WHERE'; $params = array(); - if($app!==false) { + if ($app !== false) { $fields .= ', `configkey`'; $where .= ' `appid` = ?'; $params[] = $app; $key = 'configkey'; - }else{ + } else { $fields .= ', `appid`'; $where .= ' `configkey` = ?'; $params[] = $key; @@ -206,12 +212,10 @@ class AppConfig { $result = $this->conn->executeQuery( $query, $params ); $values = array(); - while( $row = $result->fetch((\PDO::FETCH_ASSOC))) { + while ($row = $result->fetch((\PDO::FETCH_ASSOC))) { $values[$row[$key]] = $row['configvalue']; } return $values; } -} - -require_once __DIR__.'/legacy/'.basename(__FILE__); +} \ No newline at end of file From 6619d8273aa264d39c34a13ecafe720fc2356f90 Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Sun, 25 Aug 2013 22:40:23 +0200 Subject: [PATCH 04/49] Enable appconfig asserts --- tests/lib/appconfig.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/lib/appconfig.php b/tests/lib/appconfig.php index 1b7a1c668e..1f60526356 100644 --- a/tests/lib/appconfig.php +++ b/tests/lib/appconfig.php @@ -302,12 +302,12 @@ class Test_AppConfig_Object extends PHPUnit_Framework_TestCase { $appconfig = new OC\AppConfig($connectionMock); $values = $appconfig->getValues('foo', false); - //$this->assertEquals(array('x'=> 'bar'), $values); + $this->assertEquals(array('x'=> 'bar'), $values); $values = $appconfig->getValues(false, 'bar'); - //$this->assertEquals(array('y'=> 'foo'), $values); + $this->assertEquals(array('y'=> 'foo'), $values); $values = $appconfig->getValues(false, false); - //$this->assertEquals(false, $values); + $this->assertEquals(false, $values); $values = $appconfig->getValues('x', 'x'); - //$this->assertEquals(false, $values); + $this->assertEquals(false, $values); } } From b35b22977cfc9412278ae70b49c402a95efca19e Mon Sep 17 00:00:00 2001 From: Bart Visscher Date: Wed, 2 Oct 2013 09:15:31 +0200 Subject: [PATCH 05/49] Move legacy file to correct location --- lib/{ => private}/legacy/appconfig.php | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename lib/{ => private}/legacy/appconfig.php (100%) diff --git a/lib/legacy/appconfig.php b/lib/private/legacy/appconfig.php similarity index 100% rename from lib/legacy/appconfig.php rename to lib/private/legacy/appconfig.php From 617acbd6f9e93254c31987639cc4915dceb7c4c0 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 13 Jan 2014 14:28:49 +0100 Subject: [PATCH 06/49] Add a FileInfo class which holds all info of a file and return that from getFileInfo, getDirectoryContent and search --- lib/private/files/fileinfo.php | 149 +++++++++++++++++++++++++++++++++ lib/private/files/view.php | 37 ++++---- lib/public/files/fileinfo.php | 87 +++++++++++++++++++ lib/public/util.php | 2 +- 4 files changed, 257 insertions(+), 18 deletions(-) create mode 100644 lib/private/files/fileinfo.php create mode 100644 lib/public/files/fileinfo.php diff --git a/lib/private/files/fileinfo.php b/lib/private/files/fileinfo.php new file mode 100644 index 0000000000..24d99627aa --- /dev/null +++ b/lib/private/files/fileinfo.php @@ -0,0 +1,149 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OC\Files; + +class FileInfo implements \OCP\Files\FileInfo { + /** + * @var array $data + */ + private $data; + + /** + * @var string $path + */ + private $path; + + /** + * @var \OC\Files\Storage\Storage $storage + */ + private $storage; + + /** + * @var string $internalPath + */ + private $internalPath; + + public function __construct($path, $storage, $internalPath, $data) { + $this->path = $path; + $this->storage = $storage; + $this->internalPath = $internalPath; + $this->data = $data; + } + + public function offsetSet($offset, $value) { + $this->data[$offset] = $value; + } + + public function offsetExists($offset) { + return isset($this->data[$offset]); + } + + public function offsetUnset($offset) { + unset($this->data[$offset]); + } + + public function offsetGet($offset) { + return $this->data[$offset]; + } + + public function jsonSerialize() { + return $this->data; + } + + /** + * @return string + */ + public function getPath() { + return $this->path; + } + + /** + * @return \OCP\Files\Storage + */ + public function getStorage() { + return $this->storage; + } + + /** + * @return string + */ + public function getInternalPath() { + return $this->internalPath; + } + + /** + * @return int + */ + public function getId() { + return $this->data['fileid']; + } + + /** + * @return string + */ + public function getMimetype() { + return $this->data['mimetype']; + } + + /** + * @return string + */ + public function getMimePart() { + return $this->data['mimepart']; + } + + /** + * @return string + */ + public function getName() { + return $this->data['name']; + } + + /** + * @return string + */ + public function getEtag() { + return $this->data['etag']; + } + + /** + * @return int + */ + public function getSize() { + return $this->data['size']; + } + + /** + * @return int + */ + public function getMtime() { + return $this->data['mtime']; + } + + /** + * @return bool + */ + public function isEncrypted() { + return $this->data['encrypted']; + } + + /** + * @return int + */ + public function getPermissions() { + return $this->data['permissions']; + } + + /** + * @return \OCP\Files\FileInfo::TYPE_FILE | \OCP\Files\FileInfo::TYPE_FOLDER + */ + public function getType() { + return $this->data['type']; + } +} diff --git a/lib/private/files/view.php b/lib/private/files/view.php index 8893911ed5..39c2ffed93 100644 --- a/lib/private/files/view.php +++ b/lib/private/files/view.php @@ -781,14 +781,7 @@ class View { * @param string $path * @param boolean $includeMountPoints whether to add mountpoint sizes, * defaults to true - * @return array - * - * returns an associative array with the following keys: - * - size - * - mtime - * - mimetype - * - encrypted - * - versioned + * @return \OC\Files\FileInfo | false */ public function getFileInfo($path, $includeMountPoints = true) { $data = array(); @@ -838,10 +831,13 @@ class View { $data['permissions'] = $permissions; } } + if (!$data) { + return false; + } $data = \OC_FileProxy::runPostProxies('getFileInfo', $path, $data); - return $data; + return new FileInfo($path, $storage, $internalPath, $data); } /** @@ -849,7 +845,7 @@ class View { * * @param string $directory path under datadirectory * @param string $mimetype_filter limit returned content to this mimetype or mimepart - * @return array + * @return FileInfo[] */ public function getDirectoryContent($directory, $mimetype_filter = '') { $result = array(); @@ -875,7 +871,11 @@ class View { $watcher->checkUpdate($internalPath); } - $files = $cache->getFolderContents($internalPath); //TODO: mimetype_filter + $files = array(); + $contents = $cache->getFolderContents($internalPath); //TODO: mimetype_filter + foreach ($contents as $content) { + $files[] = new FileInfo($path . '/' . $content['name'], $storage, $content['path'], $content); + } $permissions = $permissionsCache->getDirectoryPermissions($cache->getId($internalPath), $user); $ids = array(); @@ -933,7 +933,7 @@ class View { break; } } - $files[] = $rootEntry; + $files[] = new FileInfo($path . '/' . $rootEntry['name'], $subStorage, '', $rootEntry); } } } @@ -955,6 +955,7 @@ class View { $result = $files; } } + return $result; } @@ -992,7 +993,7 @@ class View { * search for files with the name matching $query * * @param string $query - * @return array + * @return FileInfo[] */ public function search($query) { return $this->searchCommon('%' . $query . '%', 'search'); @@ -1002,7 +1003,7 @@ class View { * search for files by mimetype * * @param string $mimetype - * @return array + * @return FileInfo[] */ public function searchByMime($mimetype) { return $this->searchCommon($mimetype, 'searchByMime'); @@ -1011,7 +1012,7 @@ class View { /** * @param string $query * @param string $method - * @return array + * @return FileInfo[] */ private function searchCommon($query, $method) { $files = array(); @@ -1025,8 +1026,9 @@ class View { $results = $cache->$method($query); foreach ($results as $result) { if (substr($mountPoint . $result['path'], 0, $rootLength + 1) === $this->fakeRoot . '/') { + $internalPath = $result['path']; $result['path'] = substr($mountPoint . $result['path'], $rootLength); - $files[] = $result; + $files[] = new FileInfo($mountPoint . $result['path'], $storage, $internalPath, $result); } } @@ -1040,8 +1042,9 @@ class View { $results = $cache->$method($query); if ($results) { foreach ($results as $result) { + $internalPath = $result['path']; $result['path'] = $relativeMountPoint . $result['path']; - $files[] = $result; + $files[] = new FileInfo($mountPoint . $result['path'], $storage, $internalPath, $result); } } } diff --git a/lib/public/files/fileinfo.php b/lib/public/files/fileinfo.php new file mode 100644 index 0000000000..a11378c2ee --- /dev/null +++ b/lib/public/files/fileinfo.php @@ -0,0 +1,87 @@ + Date: Mon, 13 Jan 2014 14:42:14 +0100 Subject: [PATCH 07/49] Extends phpdoc for \OCP\File\FileInfo --- lib/public/files/fileinfo.php | 41 +++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/lib/public/files/fileinfo.php b/lib/public/files/fileinfo.php index a11378c2ee..09c194fe96 100644 --- a/lib/public/files/fileinfo.php +++ b/lib/public/files/fileinfo.php @@ -22,65 +22,98 @@ interface FileInfo extends \ArrayAccess, \JsonSerializable { public function jsonSerialize(); /** + * Get the Etag of the file or folder + * * @return string */ public function getEtag(); /** + * Get the size in bytes for the file or folder + * * @return int */ public function getSize(); /** + * Get the last modified date as timestamp for the file or folder + * * @return int */ public function getMtime(); /** + * Get the name of the file or folder + * * @return string */ public function getName(); /** + * Get the path relative to the storage + * * @return string */ public function getInternalPath(); /** + * Get the absolute path + * * @return string */ public function getPath(); /** + * Get the full mimetype of the file or folder i.e. 'image/png' + * * @return string */ public function getMimetype(); /** + * Get the first part of the mimetype of the file or folder i.e. 'image' + * + * @return string + */ + public function getMimePart(); + + /** + * Get the storage the file or folder is storage on + * * @return \OCP\Files\Storage */ public function getStorage(); /** + * Get the file id of the file or folder + * * @return int */ public function getId(); /** - * @return string - */ - public function getMimePart(); - /** + * Check whether the file is encrypted + * * @return bool */ public function isEncrypted(); /** + * Get the permissions of the file or folder as bitmasked combination of the following constants + * \OCP\PERMISSION_CREATE + * \OCP\PERMISSION_READ + * \OCP\PERMISSION_UPDATE + * \OCP\PERMISSION_DELETE + * \OCP\PERMISSION_SHARE + * \OCP\PERMISSION_ALL + * * @return int */ public function getPermissions(); /** + * Check whether this is a file or a folder + * * @return \OCP\Files\FileInfo::TYPE_FILE | \OCP\Files\FileInfo::TYPE_FOLDER */ public function getType(); From 82762bb4627364241c69a8b39b1d7840a1d71614 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 13 Jan 2014 15:13:45 +0100 Subject: [PATCH 08/49] remove ArrayAccess, JsonSerializable from the public part of FileInfo --- lib/private/files/fileinfo.php | 4 ++-- lib/public/files/fileinfo.php | 20 +++++--------------- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/lib/private/files/fileinfo.php b/lib/private/files/fileinfo.php index 24d99627aa..8e9b6a8b72 100644 --- a/lib/private/files/fileinfo.php +++ b/lib/private/files/fileinfo.php @@ -1,6 +1,6 @@ + * Copyright (c) 2014 Robin Appelman * This file is licensed under the Affero General Public License version 3 or * later. * See the COPYING-README file. @@ -8,7 +8,7 @@ namespace OC\Files; -class FileInfo implements \OCP\Files\FileInfo { +class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess, \JsonSerializable { /** * @var array $data */ diff --git a/lib/public/files/fileinfo.php b/lib/public/files/fileinfo.php index 09c194fe96..cbe216023d 100644 --- a/lib/public/files/fileinfo.php +++ b/lib/public/files/fileinfo.php @@ -1,26 +1,16 @@ + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. */ namespace OCP\Files; -interface FileInfo extends \ArrayAccess, \JsonSerializable { +interface FileInfo { const TYPE_FILE = 'file'; const TYPE_FOLDER = 'folder'; - public function offsetSet($offset, $value); - - public function offsetGet($offset); - - public function offsetUnset($offset); - - public function offsetExists($offset); - - public function jsonSerialize(); - /** * Get the Etag of the file or folder * From e706cf6c06a0111f9ec5db65e46f0fcc9261b456 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 13 Jan 2014 15:57:49 +0100 Subject: [PATCH 09/49] add Support for passing a FileInfo instance to View->putFileInfo --- lib/private/files/fileinfo.php | 4 ++++ lib/private/files/view.php | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/private/files/fileinfo.php b/lib/private/files/fileinfo.php index 8e9b6a8b72..b80873d1d0 100644 --- a/lib/private/files/fileinfo.php +++ b/lib/private/files/fileinfo.php @@ -146,4 +146,8 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess, \JsonSerializable { public function getType() { return $this->data['type']; } + + public function getData(){ + return $this->data; + } } diff --git a/lib/private/files/view.php b/lib/private/files/view.php index 39c2ffed93..b587027966 100644 --- a/lib/private/files/view.php +++ b/lib/private/files/view.php @@ -963,12 +963,15 @@ class View { * change file metadata * * @param string $path - * @param array $data + * @param array | \OCP\Files\FileInfo $data * @return int * * returns the fileid of the updated file */ public function putFileInfo($path, $data) { + if ($data instanceof FileInfo) { + $data = $data->getData(); + } $path = Filesystem::normalizePath($this->fakeRoot . '/' . $path); /** * @var \OC\Files\Storage\Storage $storage From ec7c339930618726f2f87899546ab7bc80ca96d5 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 17 Jan 2014 12:56:17 +0100 Subject: [PATCH 10/49] Don't use is_array on FileInfo --- apps/files_encryption/lib/proxy.php | 4 ++-- apps/files_encryption/lib/stream.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/files_encryption/lib/proxy.php b/apps/files_encryption/lib/proxy.php index 41f352d853..70077e5e1f 100644 --- a/apps/files_encryption/lib/proxy.php +++ b/apps/files_encryption/lib/proxy.php @@ -117,7 +117,7 @@ class Proxy extends \OC_FileProxy { // update file cache for target file $tmpFileInfo = $view->getFileInfo($tmpPath); $fileInfo = $view->getFileInfo($path); - if (is_array($fileInfo) && is_array($tmpFileInfo)) { + if ($fileInfo && $tmpFileInfo) { $fileInfo['encrypted'] = true; $fileInfo['unencrypted_size'] = $tmpFileInfo['size']; $view->putFileInfo($path, $fileInfo); @@ -365,7 +365,7 @@ class Proxy extends \OC_FileProxy { } // if file is encrypted return real file size - if (is_array($fileInfo) && $fileInfo['encrypted'] === true) { + if ($fileInfo && $fileInfo['encrypted'] === true) { // try to fix unencrypted file size if it doesn't look plausible if ((int)$fileInfo['size'] > 0 && (int)$fileInfo['unencrypted_size'] === 0 ) { $fixSize = $util->getFileSize($path); diff --git a/apps/files_encryption/lib/stream.php b/apps/files_encryption/lib/stream.php index b3bf34ddb8..88eacc6f13 100644 --- a/apps/files_encryption/lib/stream.php +++ b/apps/files_encryption/lib/stream.php @@ -567,7 +567,7 @@ class Stream { // get file info $fileInfo = $this->rootView->getFileInfo($path); - if (is_array($fileInfo)) { + if ($fileInfo) { // set encryption data $fileInfo['encrypted'] = true; $fileInfo['size'] = $this->size; From 642d8e370e366bab6734a07aca78c4d0c8eca1c0 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 17 Jan 2014 13:32:38 +0100 Subject: [PATCH 11/49] Dont use php5.4 only features --- tests/lib/files/cache/cache.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/lib/files/cache/cache.php b/tests/lib/files/cache/cache.php index 7d9329328a..5ef1ed7ca3 100644 --- a/tests/lib/files/cache/cache.php +++ b/tests/lib/files/cache/cache.php @@ -169,8 +169,9 @@ class Cache extends \PHPUnit_Framework_TestCase { $this->assertEquals(916, $this->cache->calculateFolderSize($file1)); // direct cache entry retrieval returns the original values - $this->assertEquals(1025, $this->cache->get($file1)['size']); - $this->assertEquals(916, $this->cache->get($file1)['unencrypted_size']); + $cacheResult = $this->cache->get($file1); + $this->assertEquals(1025, $cacheResult['size']); + $this->assertEquals(916, $cacheResult['unencrypted_size']); $this->cache->remove($file2); $this->cache->remove($file3); From 299bb4d99db935bb529a30fe6ae0c9bc72344d69 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 17 Jan 2014 14:38:14 +0100 Subject: [PATCH 12/49] remove more is_array from encryption --- apps/files_encryption/lib/proxy.php | 4 ++-- apps/files_encryption/tests/share.php | 14 +++++++------- apps/files_encryption/tests/util.php | 8 ++++---- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/apps/files_encryption/lib/proxy.php b/apps/files_encryption/lib/proxy.php index 70077e5e1f..c1b60799e6 100644 --- a/apps/files_encryption/lib/proxy.php +++ b/apps/files_encryption/lib/proxy.php @@ -307,7 +307,7 @@ class Proxy extends \OC_FileProxy { public function postGetFileInfo($path, $data) { // if path is a folder do nothing - if (\OCP\App::isEnabled('files_encryption') && is_array($data) && array_key_exists('size', $data)) { + if (\OCP\App::isEnabled('files_encryption') && $data !== false && array_key_exists('size', $data)) { // Disable encryption proxy to prevent recursive calls $proxyStatus = \OC_FileProxy::$enabled; @@ -378,7 +378,7 @@ class Proxy extends \OC_FileProxy { $size = $fileInfo['unencrypted_size']; } else { // self healing if file was removed from file cache - if (!is_array($fileInfo)) { + if (!$fileInfo) { $fileInfo = array(); } diff --git a/apps/files_encryption/tests/share.php b/apps/files_encryption/tests/share.php index e55427620a..fba608320d 100755 --- a/apps/files_encryption/tests/share.php +++ b/apps/files_encryption/tests/share.php @@ -150,7 +150,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase { '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/' . $this->filename); // check if we have a valid file info - $this->assertTrue(is_array($fileInfo)); + $this->assertTrue($fileInfo instanceof \OC\Files\FileInfo); // check if the unencrypted file size is stored $this->assertGreaterThan(0, $fileInfo['unencrypted_size']); @@ -308,7 +308,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase { '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files' . $this->folder1); // check if we have a valid file info - $this->assertTrue(is_array($fileInfo)); + $this->assertTrue($fileInfo instanceof \OC\Files\FileInfo); // re-enable the file proxy \OC_FileProxy::$enabled = $proxyStatus; @@ -384,7 +384,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase { . $this->subfolder); // check if we have a valid file info - $this->assertTrue(is_array($fileInfoSubFolder)); + $this->assertTrue($fileInfoSubFolder instanceof \OC\Files\FileInfo); // re-enable the file proxy \OC_FileProxy::$enabled = $proxyStatus; @@ -418,7 +418,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase { . $this->subsubfolder . '/' . $this->filename); // check if we have fileInfos - $this->assertTrue(is_array($fileInfo)); + $this->assertTrue($fileInfo instanceof \OC\Files\FileInfo); // share the file with user3 \OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER4, OCP\PERMISSION_ALL); @@ -513,7 +513,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase { '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/' . $this->filename); // check if we have a valid file info - $this->assertTrue(is_array($fileInfo)); + $this->assertTrue($fileInfo instanceof \OC\Files\FileInfo); // check if the unencrypted file size is stored $this->assertGreaterThan(0, $fileInfo['unencrypted_size']); @@ -589,7 +589,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase { '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/' . $this->filename); // check if we have a valid file info - $this->assertTrue(is_array($fileInfo)); + $this->assertTrue($fileInfo instanceof \OC\Files\FileInfo); // check if the unencrypted file size is stored $this->assertGreaterThan(0, $fileInfo['unencrypted_size']); @@ -876,7 +876,7 @@ class Test_Encryption_Share extends \PHPUnit_Framework_TestCase { '/' . \Test_Encryption_Share::TEST_ENCRYPTION_SHARE_USER1 . '/files/' . $this->filename); // check if we have a valid file info - $this->assertTrue(is_array($fileInfo)); + $this->assertTrue($fileInfo instanceof \OC\Files\FileInfo); // check if the unencrypted file size is stored $this->assertGreaterThan(0, $fileInfo['unencrypted_size']); diff --git a/apps/files_encryption/tests/util.php b/apps/files_encryption/tests/util.php index b1904cbadc..c789b9e2c1 100755 --- a/apps/files_encryption/tests/util.php +++ b/apps/files_encryption/tests/util.php @@ -328,7 +328,7 @@ class Test_Encryption_Util extends \PHPUnit_Framework_TestCase { $fileInfoUnencrypted = $this->view->getFileInfo($this->userId . '/files/' . $filename); - $this->assertTrue(is_array($fileInfoUnencrypted)); + $this->assertTrue($fileInfoUnencrypted instanceof \OC\Files\FileInfo); // enable file encryption again \OC_App::enable('files_encryption'); @@ -338,7 +338,7 @@ class Test_Encryption_Util extends \PHPUnit_Framework_TestCase { $fileInfoEncrypted = $this->view->getFileInfo($this->userId . '/files/' . $filename); - $this->assertTrue(is_array($fileInfoEncrypted)); + $this->assertTrue($fileInfoEncrypted instanceof \OC\Files\FileInfo); // check if mtime and etags unchanged $this->assertEquals($fileInfoEncrypted['mtime'], $fileInfoUnencrypted['mtime']); @@ -357,14 +357,14 @@ class Test_Encryption_Util extends \PHPUnit_Framework_TestCase { $fileInfoEncrypted = $this->view->getFileInfo($this->userId . '/files/' . $filename); - $this->assertTrue(is_array($fileInfoEncrypted)); + $this->assertTrue($fileInfoEncrypted instanceof \OC\Files\FileInfo); // encrypt all unencrypted files $util->decryptAll('/' . $this->userId . '/' . 'files'); $fileInfoUnencrypted = $this->view->getFileInfo($this->userId . '/files/' . $filename); - $this->assertTrue(is_array($fileInfoUnencrypted)); + $this->assertTrue($fileInfoUnencrypted instanceof \OC\Files\FileInfo); // check if mtime and etags unchanged $this->assertEquals($fileInfoEncrypted['mtime'], $fileInfoUnencrypted['mtime']); From 94504d7265227d18c2d1abbbf76f9913c795c634 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 20 Jan 2014 14:18:10 +0100 Subject: [PATCH 13/49] undo 3rdparty change --- 3rdparty | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty b/3rdparty index 3f46672083..95ab25149c 160000 --- a/3rdparty +++ b/3rdparty @@ -1 +1 @@ -Subproject commit 3f466720839295d8607f57bf7a96b7a03d77b52b +Subproject commit 95ab25149c4903650a1113c01ccb1732fb089f14 From 3971b12768ebe4fb410724a127173c38639c0a97 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 20 Jan 2014 14:20:01 +0100 Subject: [PATCH 14/49] undo 3rdparty change --- 3rdparty | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty b/3rdparty index 95ab25149c..faeedfcc05 160000 --- a/3rdparty +++ b/3rdparty @@ -1 +1 @@ -Subproject commit 95ab25149c4903650a1113c01ccb1732fb089f14 +Subproject commit faeedfcc0573868a2f0bde81f25a67a940c100ab From fc5f20112efe03b203978c4b1045ed70c2ce5e74 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 24 Jan 2014 15:54:40 +0100 Subject: [PATCH 15/49] Add isReadable, isUpdateable, isDeletable, isShareable --- lib/private/files/fileinfo.php | 38 +++++++++++++++++++++++++++++++++- lib/public/files/fileinfo.php | 28 +++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/lib/private/files/fileinfo.php b/lib/private/files/fileinfo.php index b80873d1d0..7edea13df9 100644 --- a/lib/private/files/fileinfo.php +++ b/lib/private/files/fileinfo.php @@ -122,7 +122,7 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess, \JsonSerializable { /** * @return int */ - public function getMtime() { + public function getMTime() { return $this->data['mtime']; } @@ -150,4 +150,40 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess, \JsonSerializable { public function getData(){ return $this->data; } + + /** + * @param int $permissions + * @return bool + */ + protected function checkPermissions($permissions) { + return ($this->getPermissions() & $permissions) === $permissions; + } + + /** + * @return bool + */ + public function isReadable() { + return $this->checkPermissions(\OCP\PERMISSION_READ); + } + + /** + * @return bool + */ + public function isUpdateable() { + return $this->checkPermissions(\OCP\PERMISSION_UPDATE); + } + + /** + * @return bool + */ + public function isDeletable() { + return $this->checkPermissions(\OCP\PERMISSION_DELETE); + } + + /** + * @return bool + */ + public function isShareable() { + return $this->checkPermissions(\OCP\PERMISSION_SHARE); + } } diff --git a/lib/public/files/fileinfo.php b/lib/public/files/fileinfo.php index cbe216023d..68ce45d3fa 100644 --- a/lib/public/files/fileinfo.php +++ b/lib/public/files/fileinfo.php @@ -107,4 +107,32 @@ interface FileInfo { * @return \OCP\Files\FileInfo::TYPE_FILE | \OCP\Files\FileInfo::TYPE_FOLDER */ public function getType(); + + /** + * Check if the file or folder is readable + * + * @return bool + */ + public function isReadable(); + + /** + * Check if a file is writable + * + * @return bool + */ + public function isUpdateable(); + + /** + * Check if a file or folder can be deleted + * + * @return bool + */ + public function isDeletable(); + + /** + * Check if a file or folder can be shared + * + * @return bool + */ + public function isShareable(); } From 3c1ab66edac1ba2f1b398c859cd933c410ea3d8d Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 27 Jan 2014 15:56:57 +0100 Subject: [PATCH 16/49] Reuse the calculated free_space in buildFileStorageStatistics --- apps/files/lib/helper.php | 10 +++++----- lib/private/helper.php | 9 ++++++--- lib/public/util.php | 7 ++++--- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/apps/files/lib/helper.php b/apps/files/lib/helper.php index eaff28178e..87939d2692 100644 --- a/apps/files/lib/helper.php +++ b/apps/files/lib/helper.php @@ -5,14 +5,14 @@ namespace OCA\Files; class Helper { public static function buildFileStorageStatistics($dir) { - $l = new \OC_L10N('files'); - $maxUploadFilesize = \OCP\Util::maxUploadFilesize($dir); - $maxHumanFilesize = \OCP\Util::humanFileSize($maxUploadFilesize); - $maxHumanFilesize = $l->t('Upload') . ' max. ' . $maxHumanFilesize; - // information about storage capacities $storageInfo = \OC_Helper::getStorageInfo($dir); + $l = new \OC_L10N('files'); + $maxUploadFilesize = \OCP\Util::maxUploadFilesize($dir, $storageInfo['free']); + $maxHumanFilesize = \OCP\Util::humanFileSize($maxUploadFilesize); + $maxHumanFilesize = $l->t('Upload') . ' max. ' . $maxHumanFilesize; + return array('uploadMaxFilesize' => $maxUploadFilesize, 'maxHumanFilesize' => $maxHumanFilesize, 'usedSpacePercent' => (int)$storageInfo['relative']); diff --git a/lib/private/helper.php b/lib/private/helper.php index 1c8d01c141..12784c9a5e 100644 --- a/lib/private/helper.php +++ b/lib/private/helper.php @@ -824,13 +824,16 @@ class OC_Helper { /** * @brief calculates the maximum upload size respecting system settings, free space and user quota * - * @param $dir the current folder where the user currently operates + * @param string $dir the current folder where the user currently operates + * @param int $free the number of bytes free on the storage holding $dir, if not set this will be received from the storage directly * @return number of bytes representing */ - public static function maxUploadFilesize($dir) { + public static function maxUploadFilesize($dir, $freeSpace = null) { $upload_max_filesize = OCP\Util::computerFileSize(ini_get('upload_max_filesize')); $post_max_size = OCP\Util::computerFileSize(ini_get('post_max_size')); - $freeSpace = \OC\Files\Filesystem::free_space($dir); + if (is_null($freeSpace)) { + $freeSpace = \OC\Files\Filesystem::free_space($dir); + } if ((int)$upload_max_filesize === 0 and (int)$post_max_size === 0) { $maxUploadFilesize = \OC\Files\SPACE_UNLIMITED; } elseif ((int)$upload_max_filesize === 0 or (int)$post_max_size === 0) { diff --git a/lib/public/util.php b/lib/public/util.php index 6317f10a66..52874bcc95 100644 --- a/lib/public/util.php +++ b/lib/public/util.php @@ -456,10 +456,11 @@ class Util { /** * calculates the maximum upload size respecting system settings, free space and user quota * - * @param $dir the current folder where the user currently operates + * @param string $dir the current folder where the user currently operates + * @param int $free the number of bytes free on the storage holding $dir, if not set this will be received from the storage directly * @return number of bytes representing */ - public static function maxUploadFilesize($dir) { - return \OC_Helper::maxUploadFilesize($dir); + public static function maxUploadFilesize($dir, $free = null) { + return \OC_Helper::maxUploadFilesize($dir, $free); } } From c5742520e132dc072bd8e5f69e5568d5c16c3f1e Mon Sep 17 00:00:00 2001 From: Georg Ehrke Date: Sat, 11 Jan 2014 11:51:28 +0100 Subject: [PATCH 17/49] don't urldecode get var, php does this automatically --- apps/files_sharing/ajax/publicpreview.php | 2 +- apps/files_trashbin/ajax/preview.php | 2 +- core/ajax/preview.php | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/files_sharing/ajax/publicpreview.php b/apps/files_sharing/ajax/publicpreview.php index a52f522afa..d12d212a2e 100644 --- a/apps/files_sharing/ajax/publicpreview.php +++ b/apps/files_sharing/ajax/publicpreview.php @@ -11,7 +11,7 @@ if(!\OC_App::isEnabled('files_sharing')){ \OC_User::setIncognitoMode(true); -$file = array_key_exists('file', $_GET) ? (string) urldecode($_GET['file']) : ''; +$file = array_key_exists('file', $_GET) ? (string) $_GET['file'] : ''; $maxX = array_key_exists('x', $_GET) ? (int) $_GET['x'] : '36'; $maxY = array_key_exists('y', $_GET) ? (int) $_GET['y'] : '36'; $scalingUp = array_key_exists('scalingup', $_GET) ? (bool) $_GET['scalingup'] : true; diff --git a/apps/files_trashbin/ajax/preview.php b/apps/files_trashbin/ajax/preview.php index ce432f4d14..d003a9a079 100644 --- a/apps/files_trashbin/ajax/preview.php +++ b/apps/files_trashbin/ajax/preview.php @@ -11,7 +11,7 @@ if(!\OC_App::isEnabled('files_trashbin')){ exit; } -$file = array_key_exists('file', $_GET) ? (string) urldecode($_GET['file']) : ''; +$file = array_key_exists('file', $_GET) ? (string) $_GET['file'] : ''; $maxX = array_key_exists('x', $_GET) ? (int) $_GET['x'] : '44'; $maxY = array_key_exists('y', $_GET) ? (int) $_GET['y'] : '44'; $scalingUp = array_key_exists('scalingup', $_GET) ? (bool) $_GET['scalingup'] : true; diff --git a/core/ajax/preview.php b/core/ajax/preview.php index af0f0493f4..d85dff7632 100644 --- a/core/ajax/preview.php +++ b/core/ajax/preview.php @@ -7,7 +7,7 @@ */ \OC_Util::checkLoggedIn(); -$file = array_key_exists('file', $_GET) ? (string) urldecode($_GET['file']) : ''; +$file = array_key_exists('file', $_GET) ? (string) $_GET['file'] : ''; $maxX = array_key_exists('x', $_GET) ? (int) $_GET['x'] : '36'; $maxY = array_key_exists('y', $_GET) ? (int) $_GET['y'] : '36'; $scalingUp = array_key_exists('scalingup', $_GET) ? (bool) $_GET['scalingup'] : true; @@ -26,6 +26,7 @@ if($maxX === 0 || $maxY === 0) { exit; } + try{ $preview = new \OC\Preview(\OC_User::getUser(), 'files'); $preview->setFile($file); From f7c291e2768b83176aad6dacbf0139949c2208fd Mon Sep 17 00:00:00 2001 From: Georg Ehrke Date: Sat, 11 Jan 2014 11:53:56 +0100 Subject: [PATCH 18/49] remove empty line --- core/ajax/preview.php | 1 - 1 file changed, 1 deletion(-) diff --git a/core/ajax/preview.php b/core/ajax/preview.php index d85dff7632..a1267d6f5c 100644 --- a/core/ajax/preview.php +++ b/core/ajax/preview.php @@ -26,7 +26,6 @@ if($maxX === 0 || $maxY === 0) { exit; } - try{ $preview = new \OC\Preview(\OC_User::getUser(), 'files'); $preview->setFile($file); From d49c7ad4fb1745600f33f0acc67fa39faca3e3e4 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Thu, 30 Jan 2014 16:31:47 +0100 Subject: [PATCH 19/49] Fixed double file encoding for previews --- apps/files_sharing/templates/public.php | 2 +- apps/files_trashbin/lib/trashbin.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/files_sharing/templates/public.php b/apps/files_sharing/templates/public.php index 3ddaf4446d..3eb84ce167 100644 --- a/apps/files_sharing/templates/public.php +++ b/apps/files_sharing/templates/public.php @@ -35,7 +35,7 @@
- +
diff --git a/apps/files_trashbin/lib/trashbin.php b/apps/files_trashbin/lib/trashbin.php index 7544980e07..9b7d578c99 100644 --- a/apps/files_trashbin/lib/trashbin.php +++ b/apps/files_trashbin/lib/trashbin.php @@ -996,6 +996,6 @@ class Trashbin { } public static function preview_icon($path) { - return \OC_Helper::linkToRoute( 'core_ajax_trashbin_preview', array('x' => 36, 'y' => 36, 'file' => urlencode($path) )); + return \OC_Helper::linkToRoute( 'core_ajax_trashbin_preview', array('x' => 36, 'y' => 36, 'file' => $path )); } } From 9d9f360b7742a061586f18f7f9b2910b7ddd081d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Wed, 5 Feb 2014 12:54:31 +0100 Subject: [PATCH 20/49] Load authentication apps to get users from all backends - fixes #7019 --- apps/files/command/scan.php | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/files/command/scan.php b/apps/files/command/scan.php index 25ab70af36..f334f29a93 100644 --- a/apps/files/command/scan.php +++ b/apps/files/command/scan.php @@ -58,6 +58,7 @@ class Scan extends Command { protected function execute(InputInterface $input, OutputInterface $output) { if ($input->getOption('all')) { + \OC_App::loadApps('authentication'); $users = $this->userManager->search(''); } else { $users = $input->getArgument('user_id'); From 788c8540aa6aac50795c37b088eeaa561d44b86c Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Tue, 4 Feb 2014 19:58:49 +0100 Subject: [PATCH 21/49] Added isLocal() method to storage, used for xsendfile Added isLocal() method to Storage to find out whether the storage is local or not. This method is used for the x-sendfile logic to find out whether to add the headers. --- lib/private/files.php | 2 +- lib/private/files/storage/common.php | 9 +++++++++ lib/private/files/storage/local.php | 7 +++++++ lib/private/files/storage/wrapper/wrapper.php | 8 ++++++++ lib/public/files/storage.php | 11 +++++++++++ 5 files changed, 36 insertions(+), 1 deletion(-) diff --git a/lib/private/files.php b/lib/private/files.php index 8ce632013c..24fca4a5df 100644 --- a/lib/private/files.php +++ b/lib/private/files.php @@ -131,7 +131,7 @@ class OC_Files { } if ($xsendfile) { list($storage) = \OC\Files\Filesystem::resolvePath(\OC\Files\Filesystem::getView()->getAbsolutePath($filename)); - if ($storage instanceof \OC\Files\Storage\Local) { + if ($storage->isLocal()) { self::addSendfileHeader(\OC\Files\Filesystem::getLocalFile($filename)); } } diff --git a/lib/private/files/storage/common.php b/lib/private/files/storage/common.php index 678bf41902..55b1471593 100644 --- a/lib/private/files/storage/common.php +++ b/lib/private/files/storage/common.php @@ -370,4 +370,13 @@ abstract class Common implements \OC\Files\Storage\Storage { public function free_space($path) { return \OC\Files\SPACE_UNKNOWN; } + + /** + * {@inheritdoc} + */ + public function isLocal() { + // the common implementation returns a temporary file by + // default, which is not local + return false; + } } diff --git a/lib/private/files/storage/local.php b/lib/private/files/storage/local.php index db3c6bfca3..fa0788f237 100644 --- a/lib/private/files/storage/local.php +++ b/lib/private/files/storage/local.php @@ -298,5 +298,12 @@ if (\OC_Util::runningOnWindows()) { public function hasUpdated($path, $time) { return $this->filemtime($path) > $time; } + + /** + * {@inheritdoc} + */ + public function isLocal() { + return true; + } } } diff --git a/lib/private/files/storage/wrapper/wrapper.php b/lib/private/files/storage/wrapper/wrapper.php index f9adda8031..11ea9f71da 100644 --- a/lib/private/files/storage/wrapper/wrapper.php +++ b/lib/private/files/storage/wrapper/wrapper.php @@ -432,4 +432,12 @@ class Wrapper implements \OC\Files\Storage\Storage { public function test() { return $this->storage->test(); } + + /** + * Returns the wrapped storage's value for isLocal() + * @return bool wrapped storage's isLocal() value + */ + public function isLocal() { + return $this->storage->isLocal(); + } } diff --git a/lib/public/files/storage.php b/lib/public/files/storage.php index 194b42a648..fe30f8f50a 100644 --- a/lib/public/files/storage.php +++ b/lib/public/files/storage.php @@ -315,4 +315,15 @@ interface Storage { * @return string */ public function getETag($path); + + /** + * Returns whether the storage is local, which means that files + * are stored on the local filesystem instead of remotely. + * Calling getLocalFile() for local storages should always + * return the local files, whereas for non-local storages + * it might return a temporary file. + * + * @return bool true if the files are stored locally, false otherwise + */ + public function isLocal(); } From cd3ef0bb9d6585dcc07e7ca62285032245283106 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 7 Feb 2014 14:03:39 +0100 Subject: [PATCH 22/49] Add caching to appconfig --- lib/private/appconfig.php | 61 +++++++++++++++++++++-------- lib/private/db/statementwrapper.php | 3 ++ 2 files changed, 48 insertions(+), 16 deletions(-) diff --git a/lib/private/appconfig.php b/lib/private/appconfig.php index ff47f08d48..71aa830731 100644 --- a/lib/private/appconfig.php +++ b/lib/private/appconfig.php @@ -47,6 +47,10 @@ class AppConfig implements \OCP\IAppConfig { */ protected $conn; + private $cache = array(); + + private $appsLoaded = array(); + /** * @param \OC\DB\Connection $conn */ @@ -54,6 +58,32 @@ class AppConfig implements \OCP\IAppConfig { $this->conn = $conn; } + /** + * @param string $app + * @return string[] + */ + private function getAppCache($app) { + if (!isset($this->cache[$app])) { + $this->cache[$app] = array(); + } + return $this->cache[$app]; + } + + private function getAppValues($app) { + $appCache = $this->getAppCache($app); + if (array_search($app, $this->appsLoaded) === false) { + $query = 'SELECT `configvalue`, `configkey` FROM `*PREFIX*appconfig`' + . ' WHERE `appid` = ?'; + $result = $this->conn->executeQuery($query, array($app)); + while ($row = $result->fetch()) { + $appCache[$row['configkey']] = $row['configvalue']; + } + $this->appsLoaded[] = $app; + } + $this->cache[$app] = $appCache; + return $appCache; + } + /** * @brief Get all apps using the config * @return array with app ids @@ -81,15 +111,8 @@ class AppConfig implements \OCP\IAppConfig { * not returned. */ public function getKeys($app) { - $query = 'SELECT `configkey` FROM `*PREFIX*appconfig` WHERE `appid` = ?'; - $result = $this->conn->executeQuery($query, array($app)); - - $keys = array(); - while ($key = $result->fetchColumn()) { - $keys[] = $key; - } - - return $keys; + $values = $this->getAppValues($app); + return array_keys($values); } /** @@ -103,11 +126,9 @@ class AppConfig implements \OCP\IAppConfig { * not exist the default value will be returned */ public function getValue($app, $key, $default = null) { - $query = 'SELECT `configvalue` FROM `*PREFIX*appconfig`' - . ' WHERE `appid` = ? AND `configkey` = ?'; - $row = $this->conn->fetchAssoc($query, array($app, $key)); - if ($row) { - return $row['configvalue']; + $values = $this->getAppValues($app); + if (isset($values[$key])) { + return $values[$key]; } else { return $default; } @@ -120,8 +141,8 @@ class AppConfig implements \OCP\IAppConfig { * @return bool */ public function hasKey($app, $key) { - $exists = $this->getKeys($app); - return in_array($key, $exists); + $values = $this->getAppValues($app); + return isset($values[$key]); } /** @@ -151,6 +172,10 @@ class AppConfig implements \OCP\IAppConfig { ); $this->conn->update('*PREFIX*appconfig', $data, $where); } + if (!isset($this->cache[$app])) { + $this->cache[$app] = array(); + } + $this->cache[$app][$key] = $value; } /** @@ -167,6 +192,9 @@ class AppConfig implements \OCP\IAppConfig { 'configkey' => $key, ); $this->conn->delete('*PREFIX*appconfig', $where); + if (isset($this->cache[$app]) and isset($this->cache[$app][$key])) { + unset($this->cache[$app][$key]); + } } /** @@ -181,6 +209,7 @@ class AppConfig implements \OCP\IAppConfig { 'appid' => $app, ); $this->conn->delete('*PREFIX*appconfig', $where); + unset($this->cache[$app]); } /** diff --git a/lib/private/db/statementwrapper.php b/lib/private/db/statementwrapper.php index 5e89261d93..a71df315e2 100644 --- a/lib/private/db/statementwrapper.php +++ b/lib/private/db/statementwrapper.php @@ -31,6 +31,9 @@ class OC_DB_StatementWrapper { /** * make execute return the result instead of a bool + * + * @param array $input + * @return \OC_DB_StatementWrapper | int */ public function execute($input=array()) { if(OC_Config::getValue( "log_query", false)) { From 4cdf83e6d257c57ebad60e60dec474faa83a51d9 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 7 Feb 2014 14:03:57 +0100 Subject: [PATCH 23/49] Remove the Test_AppConfig_Object tests as they no longer make sense with caching --- tests/lib/appconfig.php | 180 ---------------------------------------- 1 file changed, 180 deletions(-) diff --git a/tests/lib/appconfig.php b/tests/lib/appconfig.php index 29b29778fd..5cbdf80502 100644 --- a/tests/lib/appconfig.php +++ b/tests/lib/appconfig.php @@ -131,183 +131,3 @@ class Test_Appconfig extends PHPUnit_Framework_TestCase { $this->assertEquals($expected, $values); } } - -class Test_AppConfig_Object extends PHPUnit_Framework_TestCase { - public function testGetApps() - { - $statementMock = $this->getMock('\Doctrine\DBAL\Statement', array(), array(), '', false); - $statementMock->expects($this->exactly(2)) - ->method('fetchColumn') - ->will($this->onConsecutiveCalls('foo', false)); - $connectionMock = $this->getMock('\OC\DB\Connection', array(), array(), '', false); - $connectionMock->expects($this->once()) - ->method('executeQuery') - ->with($this->equalTo('SELECT DISTINCT `appid` FROM `*PREFIX*appconfig`')) - ->will($this->returnValue($statementMock)); - - $appconfig = new OC\AppConfig($connectionMock); - $apps = $appconfig->getApps(); - $this->assertEquals(array('foo'), $apps); - } - - public function testGetKeys() - { - $statementMock = $this->getMock('\Doctrine\DBAL\Statement', array(), array(), '', false); - $statementMock->expects($this->exactly(2)) - ->method('fetchColumn') - ->will($this->onConsecutiveCalls('foo', false)); - $connectionMock = $this->getMock('\OC\DB\Connection', array(), array(), '', false); - $connectionMock->expects($this->once()) - ->method('executeQuery') - ->with($this->equalTo('SELECT `configkey` FROM `*PREFIX*appconfig` WHERE `appid` = ?'), - $this->equalTo(array('bar'))) - ->will($this->returnValue($statementMock)); - - $appconfig = new OC\AppConfig($connectionMock); - $keys = $appconfig->getKeys('bar'); - $this->assertEquals(array('foo'), $keys); - } - - public function testGetValue() - { - $connectionMock = $this->getMock('\OC\DB\Connection', array(), array(), '', false); - $connectionMock->expects($this->exactly(2)) - ->method('fetchAssoc') - ->with($this->equalTo('SELECT `configvalue` FROM `*PREFIX*appconfig` WHERE `appid` = ? AND `configkey` = ?'), - $this->equalTo(array('bar', 'red'))) - ->will($this->onConsecutiveCalls(array('configvalue'=>'foo'), null)); - - $appconfig = new OC\AppConfig($connectionMock); - $value = $appconfig->getValue('bar', 'red'); - $this->assertEquals('foo', $value); - $value = $appconfig->getValue('bar', 'red', 'def'); - $this->assertEquals('def', $value); - } - - public function testHasKey() - { - $statementMock = $this->getMock('\Doctrine\DBAL\Statement', array(), array(), '', false); - $statementMock->expects($this->exactly(3)) - ->method('fetchColumn') - ->will($this->onConsecutiveCalls('foo', false, false)); - $connectionMock = $this->getMock('\OC\DB\Connection', array(), array(), '', false); - $connectionMock->expects($this->exactly(2)) - ->method('executeQuery') - ->with($this->equalTo('SELECT `configkey` FROM `*PREFIX*appconfig` WHERE `appid` = ?'), - $this->equalTo(array('bar'))) - ->will($this->returnValue($statementMock)); - - $appconfig = new OC\AppConfig($connectionMock); - $this->assertTrue($appconfig->hasKey('bar', 'foo')); - $this->assertFalse($appconfig->hasKey('bar', 'foo')); - } - - public function testSetValue() - { - $statementMock = $this->getMock('\Doctrine\DBAL\Statement', array(), array(), '', false); - $statementMock->expects($this->exactly(4)) - ->method('fetchColumn') - ->will($this->onConsecutiveCalls('foo', false, 'foo', false)); - $connectionMock = $this->getMock('\OC\DB\Connection', array(), array(), '', false); - $connectionMock->expects($this->exactly(2)) - ->method('executeQuery') - ->with($this->equalTo('SELECT `configkey` FROM `*PREFIX*appconfig` WHERE `appid` = ?'), - $this->equalTo(array('bar'))) - ->will($this->returnValue($statementMock)); - $connectionMock->expects($this->once()) - ->method('insert') - ->with($this->equalTo('*PREFIX*appconfig'), - $this->equalTo( - array( - 'appid' => 'bar', - 'configkey' => 'moo', - 'configvalue' => 'v1', - ) - )); - $connectionMock->expects($this->once()) - ->method('update') - ->with($this->equalTo('*PREFIX*appconfig'), - $this->equalTo( - array( - 'configvalue' => 'v2', - )), - $this->equalTo( - array( - 'appid' => 'bar', - 'configkey' => 'foo', - ) - )); - - $appconfig = new OC\AppConfig($connectionMock); - $appconfig->setValue('bar', 'moo', 'v1'); - $appconfig->setValue('bar', 'foo', 'v2'); - } - - public function testDeleteKey() - { - $connectionMock = $this->getMock('\OC\DB\Connection', array(), array(), '', false); - $connectionMock->expects($this->once()) - ->method('delete') - ->with($this->equalTo('*PREFIX*appconfig'), - $this->equalTo( - array( - 'appid' => 'bar', - 'configkey' => 'foo', - ) - )); - - $appconfig = new OC\AppConfig($connectionMock); - $appconfig->deleteKey('bar', 'foo'); - } - - public function testDeleteApp() - { - $connectionMock = $this->getMock('\OC\DB\Connection', array(), array(), '', false); - $connectionMock->expects($this->once()) - ->method('delete') - ->with($this->equalTo('*PREFIX*appconfig'), - $this->equalTo( - array( - 'appid' => 'bar', - ) - )); - - $appconfig = new OC\AppConfig($connectionMock); - $appconfig->deleteApp('bar'); - } - - public function testGetValues() - { - $statementMock = $this->getMock('\Doctrine\DBAL\Statement', array(), array(), '', false); - $statementMock->expects($this->exactly(4)) - ->method('fetch') - ->with(\PDO::FETCH_ASSOC) - ->will($this->onConsecutiveCalls( - array('configvalue' =>'bar', 'configkey' => 'x'), - false, - array('configvalue' =>'foo', 'appid' => 'y'), - false - )); - $connectionMock = $this->getMock('\OC\DB\Connection', array(), array(), '', false); - $connectionMock->expects($this->at(0)) - ->method('executeQuery') - ->with($this->equalTo('SELECT `configvalue`, `configkey` FROM `*PREFIX*appconfig` WHERE `appid` = ?'), - $this->equalTo(array('foo'))) - ->will($this->returnValue($statementMock)); - $connectionMock->expects($this->at(1)) - ->method('executeQuery') - ->with($this->equalTo('SELECT `configvalue`, `appid` FROM `*PREFIX*appconfig` WHERE `configkey` = ?'), - $this->equalTo(array('bar'))) - ->will($this->returnValue($statementMock)); - - $appconfig = new OC\AppConfig($connectionMock); - $values = $appconfig->getValues('foo', false); - $this->assertEquals(array('x'=> 'bar'), $values); - $values = $appconfig->getValues(false, 'bar'); - $this->assertEquals(array('y'=> 'foo'), $values); - $values = $appconfig->getValues(false, false); - $this->assertEquals(false, $values); - $values = $appconfig->getValues('x', 'x'); - $this->assertEquals(false, $values); - } -} From 71e4d965a1d468ac01b404f233397def3b580ab2 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Wed, 15 Jan 2014 13:26:08 +0100 Subject: [PATCH 24/49] on filtering the share box users and groups whose name begins with the search term shall appear on top, fixes #6430 --- core/ajax/share.php | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/core/ajax/share.php b/core/ajax/share.php index 8b48effb45..784b2528f4 100644 --- a/core/ajax/share.php +++ b/core/ajax/share.php @@ -354,8 +354,32 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo break; } } + usort($shareWith, 'sortSearchFirst'); OC_JSON::success(array('data' => $shareWith)); } break; } } + + +/** + * User and Group names matching the search term at the beginning shall appear + * on top of the share dialog. + * Callback function for usort. http://php.net/usort + */ +function sortSearchFirst($a, $b) { + $enc = 'UTF-8'; + $nameA = mb_strtolower($a['label'], $enc); + $nameB = mb_strtolower($b['label'], $enc); + $term = mb_strtolower($_GET['search'], $enc); + $i = mb_strpos($nameA, $term, 0, 'UTF-8'); + $j = mb_strpos($nameB, $term, 0, 'UTF-8'); + + if($i === $j) { + return 0; + } else if ($i === 0) { + return -1; + } else { + return 1; + } +} From 1d0a2365631955944d746b4567cac85eb10a80db Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Wed, 15 Jan 2014 14:02:18 +0100 Subject: [PATCH 25/49] respect coding guidelines --- core/ajax/share.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/ajax/share.php b/core/ajax/share.php index 784b2528f4..5c2dbc6654 100644 --- a/core/ajax/share.php +++ b/core/ajax/share.php @@ -377,7 +377,7 @@ function sortSearchFirst($a, $b) { if($i === $j) { return 0; - } else if ($i === 0) { + } elseif ($i === 0) { return -1; } else { return 1; From 41e8d44cf7261e05a40842ab56d4d4c3f61cf083 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Wed, 15 Jan 2014 17:55:05 +0100 Subject: [PATCH 26/49] move sorter into a class --- core/ajax/share.php | 27 ++---------- lib/private/share/searchresultsorter.php | 53 ++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 24 deletions(-) create mode 100644 lib/private/share/searchresultsorter.php diff --git a/core/ajax/share.php b/core/ajax/share.php index 5c2dbc6654..21e320a473 100644 --- a/core/ajax/share.php +++ b/core/ajax/share.php @@ -354,32 +354,11 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo break; } } - usort($shareWith, 'sortSearchFirst'); + $sorter = new \OC\Share\SearchResultSorter($_GET['search'], + 'label'); + usort($shareWith, array($sorter, 'sort')); OC_JSON::success(array('data' => $shareWith)); } break; } } - - -/** - * User and Group names matching the search term at the beginning shall appear - * on top of the share dialog. - * Callback function for usort. http://php.net/usort - */ -function sortSearchFirst($a, $b) { - $enc = 'UTF-8'; - $nameA = mb_strtolower($a['label'], $enc); - $nameB = mb_strtolower($b['label'], $enc); - $term = mb_strtolower($_GET['search'], $enc); - $i = mb_strpos($nameA, $term, 0, 'UTF-8'); - $j = mb_strpos($nameB, $term, 0, 'UTF-8'); - - if($i === $j) { - return 0; - } elseif ($i === 0) { - return -1; - } else { - return 1; - } -} diff --git a/lib/private/share/searchresultsorter.php b/lib/private/share/searchresultsorter.php new file mode 100644 index 0000000000..27f94a694a --- /dev/null +++ b/lib/private/share/searchresultsorter.php @@ -0,0 +1,53 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + * + */ +namespace OC\Share; + +class SearchResultSorter { + private $search; + private $encoding; + private $key; + + /** + * @param $search the search term as was given by the user + * @param $key the array key containing the value that should be compared + * against + * @param $encoding optional, encoding to use, defaults to UTF-8 + */ + public function __construct($search, $key, $encoding = 'UTF-8') { + $this->encoding = $encoding; + $this->key = $key; + $this->search = mb_strtolower($search, $this->encoding); + } + + /** + * User and Group names matching the search term at the beginning shall appear + * on top of the share dialog. + * Callback function for usort. http://php.net/usort + */ + public function sort($a, $b) { + if(!isset($a[$this->key]) || !isset($b[$this->key])) { + \OCP\Util::writeLog('core', 'Sharing: cannot sort due to missing'. + 'array key', \OC_Log::ERROR); + return 0; + } + $nameA = mb_strtolower($a[$this->key], $this->encoding); + $nameB = mb_strtolower($b[$this->key], $this->encoding); + $i = mb_strpos($nameA, $this->search, 0, $this->encoding); + $j = mb_strpos($nameB, $this->search, 0, $this->encoding); + + if($i === $j) { + return 0; + } elseif ($i === 0) { + return -1; + } else { + return 1; + } + } +} + From 82716ced48c256ef5e2f8ca9b7a4e3ab20e146cd Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Wed, 15 Jan 2014 18:19:20 +0100 Subject: [PATCH 27/49] sort following entries in alphabetical order --- lib/private/share/searchresultsorter.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/private/share/searchresultsorter.php b/lib/private/share/searchresultsorter.php index 27f94a694a..f64a4766ad 100644 --- a/lib/private/share/searchresultsorter.php +++ b/lib/private/share/searchresultsorter.php @@ -27,7 +27,7 @@ class SearchResultSorter { /** * User and Group names matching the search term at the beginning shall appear - * on top of the share dialog. + * on top of the share dialog. Following entries in alphabetical order. * Callback function for usort. http://php.net/usort */ public function sort($a, $b) { @@ -41,8 +41,9 @@ class SearchResultSorter { $i = mb_strpos($nameA, $this->search, 0, $this->encoding); $j = mb_strpos($nameB, $this->search, 0, $this->encoding); - if($i === $j) { - return 0; + if($i === $j || $i > 0 && $j > 0) { + return strcmp(mb_strtolower($nameA, $this->encoding), + mb_strtolower($nameB, $this->encoding)); } elseif ($i === 0) { return -1; } else { From 9a39cd3b38837421a5ac476a78207402e4c6c91c Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Wed, 15 Jan 2014 18:26:06 +0100 Subject: [PATCH 28/49] test for share dialoge sorter --- tests/lib/share/searchresultsorter.php | 40 ++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 tests/lib/share/searchresultsorter.php diff --git a/tests/lib/share/searchresultsorter.php b/tests/lib/share/searchresultsorter.php new file mode 100644 index 0000000000..f24e40ea05 --- /dev/null +++ b/tests/lib/share/searchresultsorter.php @@ -0,0 +1,40 @@ + +* +* 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_Share_Search extends \PHPUnit_Framework_TestCase { + public function testSort() { + $search = 'lin'; + $sorter = new \OC\Share\SearchResultSorter($search, 'foobar'); + + $result = array( + array('foobar' => 'woot'), + array('foobar' => 'linux'), + array('foobar' => 'Linus'), + array('foobar' => 'Bicyclerepairwoman'), + ); + + usort($result, array($sorter, 'sort')); + $this->assertTrue($result[0]['foobar'] === 'Linus'); + $this->assertTrue($result[1]['foobar'] === 'linux'); + $this->assertTrue($result[2]['foobar'] === 'Bicyclerepairwoman'); + $this->assertTrue($result[3]['foobar'] === 'woot'); + } +} From 20bfbb0fd9316be0cbba9c2202a9b45ce7eea7f8 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Thu, 16 Jan 2014 10:30:18 +0100 Subject: [PATCH 29/49] wrong tld --- lib/private/share/searchresultsorter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/private/share/searchresultsorter.php b/lib/private/share/searchresultsorter.php index f64a4766ad..4f8799494f 100644 --- a/lib/private/share/searchresultsorter.php +++ b/lib/private/share/searchresultsorter.php @@ -1,6 +1,6 @@ + * Copyright (c) 2014 Arthur Schiwon * This file is licensed under the Affero General Public License version 3 or * later. * See the COPYING-README file. From 32afdcbefec5793fb28162c456919d2b0be5cfe9 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Thu, 16 Jan 2014 11:00:22 +0100 Subject: [PATCH 30/49] Inject logger --- lib/private/share/searchresultsorter.php | 9 ++++++--- tests/lib/share/searchresultsorter.php | 7 +++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/lib/private/share/searchresultsorter.php b/lib/private/share/searchresultsorter.php index 4f8799494f..f81a94c749 100644 --- a/lib/private/share/searchresultsorter.php +++ b/lib/private/share/searchresultsorter.php @@ -12,16 +12,19 @@ class SearchResultSorter { private $search; private $encoding; private $key; + private $log; /** * @param $search the search term as was given by the user * @param $key the array key containing the value that should be compared * against * @param $encoding optional, encoding to use, defaults to UTF-8 + * @param $log optional, an \OC\Log instance */ - public function __construct($search, $key, $encoding = 'UTF-8') { + public function __construct($search, $key, $encoding = 'UTF-8', \OC\Log $log = null) { $this->encoding = $encoding; $this->key = $key; + $this->log = is_null($log) ? new \OC\Log() : $log; $this->search = mb_strtolower($search, $this->encoding); } @@ -32,8 +35,8 @@ class SearchResultSorter { */ public function sort($a, $b) { if(!isset($a[$this->key]) || !isset($b[$this->key])) { - \OCP\Util::writeLog('core', 'Sharing: cannot sort due to missing'. - 'array key', \OC_Log::ERROR); + $this->log->error('Sharing dialogue: cannot sort due to missing array key', + array('app' => 'core')); return 0; } $nameA = mb_strtolower($a[$this->key], $this->encoding); diff --git a/tests/lib/share/searchresultsorter.php b/tests/lib/share/searchresultsorter.php index f24e40ea05..efc3e1b7aa 100644 --- a/tests/lib/share/searchresultsorter.php +++ b/tests/lib/share/searchresultsorter.php @@ -37,4 +37,11 @@ class Test_Share_Search extends \PHPUnit_Framework_TestCase { $this->assertTrue($result[2]['foobar'] === 'Bicyclerepairwoman'); $this->assertTrue($result[3]['foobar'] === 'woot'); } + + /** + * @expectedException PHPUnit_Framework_Error + */ + public function testSortWrongLog() { + $sorter = new \OC\Share\SearchResultSorter('foo', 'bar', 'UTF-8', 'foobar'); + } } From af781bdea7509c3ecd9a7b4902fb7a266dc62c80 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Wed, 5 Feb 2014 17:05:56 +0100 Subject: [PATCH 31/49] fix DI --- core/ajax/share.php | 3 ++- lib/private/share/searchresultsorter.php | 10 ++++++---- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/core/ajax/share.php b/core/ajax/share.php index 21e320a473..c251f8e7ba 100644 --- a/core/ajax/share.php +++ b/core/ajax/share.php @@ -355,7 +355,8 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo } } $sorter = new \OC\Share\SearchResultSorter($_GET['search'], - 'label'); + 'label', + new \OC\Log()); usort($shareWith, array($sorter, 'sort')); OC_JSON::success(array('data' => $shareWith)); } diff --git a/lib/private/share/searchresultsorter.php b/lib/private/share/searchresultsorter.php index f81a94c749..fbf7717909 100644 --- a/lib/private/share/searchresultsorter.php +++ b/lib/private/share/searchresultsorter.php @@ -21,10 +21,10 @@ class SearchResultSorter { * @param $encoding optional, encoding to use, defaults to UTF-8 * @param $log optional, an \OC\Log instance */ - public function __construct($search, $key, $encoding = 'UTF-8', \OC\Log $log = null) { + public function __construct($search, $key, \OC\Log $log = null, $encoding = 'UTF-8') { $this->encoding = $encoding; $this->key = $key; - $this->log = is_null($log) ? new \OC\Log() : $log; + $this->log = $log; $this->search = mb_strtolower($search, $this->encoding); } @@ -35,8 +35,10 @@ class SearchResultSorter { */ public function sort($a, $b) { if(!isset($a[$this->key]) || !isset($b[$this->key])) { - $this->log->error('Sharing dialogue: cannot sort due to missing array key', - array('app' => 'core')); + if(!is_null($this->log)) { + $this->log->error('Sharing dialogue: cannot sort due to ' . + 'missing array key', array('app' => 'core')); + } return 0; } $nameA = mb_strtolower($a[$this->key], $this->encoding); From 72f134cfce05eb089a6d8271e73d6eb95cbe94a4 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Wed, 5 Feb 2014 17:12:17 +0100 Subject: [PATCH 32/49] intendation --- tests/lib/share/searchresultsorter.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/lib/share/searchresultsorter.php b/tests/lib/share/searchresultsorter.php index efc3e1b7aa..eaf93400a7 100644 --- a/tests/lib/share/searchresultsorter.php +++ b/tests/lib/share/searchresultsorter.php @@ -25,11 +25,11 @@ class Test_Share_Search extends \PHPUnit_Framework_TestCase { $sorter = new \OC\Share\SearchResultSorter($search, 'foobar'); $result = array( - array('foobar' => 'woot'), - array('foobar' => 'linux'), - array('foobar' => 'Linus'), - array('foobar' => 'Bicyclerepairwoman'), - ); + array('foobar' => 'woot'), + array('foobar' => 'linux'), + array('foobar' => 'Linus'), + array('foobar' => 'Bicyclerepairwoman'), + ); usort($result, array($sorter, 'sort')); $this->assertTrue($result[0]['foobar'] === 'Linus'); From a6399f9ceffcf9865b8a2be155dc4f98bd2ee5dc Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 11 Feb 2014 14:00:24 +0100 Subject: [PATCH 33/49] Add the background job list to the public server container --- cron.php | 4 +- lib/private/backgroundjob/job.php | 4 +- lib/private/backgroundjob/joblist.php | 68 +++++++++++++--------- lib/private/server.php | 16 ++++++ lib/public/backgroundjob.php | 14 ++--- lib/public/backgroundjob/ijob.php | 29 ++++++++++ lib/public/backgroundjob/ijoblist.php | 73 ++++++++++++++++++++++++ lib/public/iservercontainer.php | 7 +++ tests/lib/backgroundjob/dummyjoblist.php | 2 + 9 files changed, 179 insertions(+), 38 deletions(-) create mode 100644 lib/public/backgroundjob/ijob.php create mode 100644 lib/public/backgroundjob/ijoblist.php diff --git a/cron.php b/cron.php index 0d2c07b2d9..44ca421328 100644 --- a/cron.php +++ b/cron.php @@ -97,7 +97,7 @@ try { touch(TemporaryCronClass::$lockfile); // Work - $jobList = new \OC\BackgroundJob\JobList(); + $jobList = \OC::$server->getJobList(); $jobs = $jobList->getAll(); foreach ($jobs as $job) { $job->execute($jobList, $logger); @@ -109,7 +109,7 @@ try { OC_JSON::error(array('data' => array('message' => 'Backgroundjobs are using system cron!'))); } else { // Work and success :-) - $jobList = new \OC\BackgroundJob\JobList(); + $jobList = \OC::$server->getJobList(); $job = $jobList->getNext(); $job->execute($jobList, $logger); $jobList->setLastJob($job); diff --git a/lib/private/backgroundjob/job.php b/lib/private/backgroundjob/job.php index 92bd0f8fdb..0cef401bc2 100644 --- a/lib/private/backgroundjob/job.php +++ b/lib/private/backgroundjob/job.php @@ -8,7 +8,9 @@ namespace OC\BackgroundJob; -abstract class Job { +use OCP\BackgroundJob\IJob; + +abstract class Job implements IJob { /** * @var int $id */ diff --git a/lib/private/backgroundjob/joblist.php b/lib/private/backgroundjob/joblist.php index 99743a70c7..6b0df9e0d6 100644 --- a/lib/private/backgroundjob/joblist.php +++ b/lib/private/backgroundjob/joblist.php @@ -8,14 +8,26 @@ namespace OC\BackgroundJob; -/** - * Class QueuedJob - * - * create a background job that is to be executed once - * - * @package OC\BackgroundJob - */ class JobList { + /** + * @var \OCP\IDBConnection + */ + private $conn; + + /** + * @var \OCP\IConfig $config + */ + private $config; + + /** + * @param \OCP\IDBConnection $conn + * @param \OCP\IConfig $config + */ + public function __construct($conn, $config) { + $this->conn = $conn; + $this->config = $config; + } + /** * @param Job|string $job * @param mixed $argument @@ -28,7 +40,7 @@ class JobList { $class = $job; } $argument = json_encode($argument); - $query = \OC_DB::prepare('INSERT INTO `*PREFIX*jobs`(`class`, `argument`, `last_run`) VALUES(?, ?, 0)'); + $query = $this->conn->prepare('INSERT INTO `*PREFIX*jobs`(`class`, `argument`, `last_run`) VALUES(?, ?, 0)'); $query->execute(array($class, $argument)); } } @@ -45,10 +57,10 @@ class JobList { } if (!is_null($argument)) { $argument = json_encode($argument); - $query = \OC_DB::prepare('DELETE FROM `*PREFIX*jobs` WHERE `class` = ? AND `argument` = ?'); + $query = $this->conn->prepare('DELETE FROM `*PREFIX*jobs` WHERE `class` = ? AND `argument` = ?'); $query->execute(array($class, $argument)); } else { - $query = \OC_DB::prepare('DELETE FROM `*PREFIX*jobs` WHERE `class` = ?'); + $query = $this->conn->prepare('DELETE FROM `*PREFIX*jobs` WHERE `class` = ?'); $query->execute(array($class)); } } @@ -67,9 +79,9 @@ class JobList { $class = $job; } $argument = json_encode($argument); - $query = \OC_DB::prepare('SELECT `id` FROM `*PREFIX*jobs` WHERE `class` = ? AND `argument` = ?'); - $result = $query->execute(array($class, $argument)); - return (bool)$result->fetchRow(); + $query = $this->conn->prepare('SELECT `id` FROM `*PREFIX*jobs` WHERE `class` = ? AND `argument` = ?'); + $query->execute(array($class, $argument)); + return (bool)$query->fetch(); } /** @@ -78,10 +90,10 @@ class JobList { * @return Job[] */ public function getAll() { - $query = \OC_DB::prepare('SELECT `id`, `class`, `last_run`, `argument` FROM `*PREFIX*jobs`'); - $result = $query->execute(); + $query = $this->conn->prepare('SELECT `id`, `class`, `last_run`, `argument` FROM `*PREFIX*jobs`'); + $query->execute(); $jobs = array(); - while ($row = $result->fetchRow()) { + while ($row = $query->fetch()) { $jobs[] = $this->buildJob($row); } return $jobs; @@ -94,15 +106,15 @@ class JobList { */ public function getNext() { $lastId = $this->getLastJob(); - $query = \OC_DB::prepare('SELECT `id`, `class`, `last_run`, `argument` FROM `*PREFIX*jobs` WHERE `id` > ? ORDER BY `id` ASC', 1); - $result = $query->execute(array($lastId)); - if ($row = $result->fetchRow()) { + $query = $this->conn->prepare('SELECT `id`, `class`, `last_run`, `argument` FROM `*PREFIX*jobs` WHERE `id` > ? ORDER BY `id` ASC', 1); + $query->execute(array($lastId)); + if ($row = $query->fetch()) { return $this->buildJob($row); } else { //begin at the start of the queue - $query = \OC_DB::prepare('SELECT `id`, `class`, `last_run`, `argument` FROM `*PREFIX*jobs` ORDER BY `id` ASC', 1); - $result = $query->execute(); - if ($row = $result->fetchRow()) { + $query = $this->conn->prepare('SELECT `id`, `class`, `last_run`, `argument` FROM `*PREFIX*jobs` ORDER BY `id` ASC', 1); + $query->execute(); + if ($row = $query->fetch()) { return $this->buildJob($row); } else { return null; //empty job list @@ -115,9 +127,9 @@ class JobList { * @return Job */ public function getById($id) { - $query = \OC_DB::prepare('SELECT `id`, `class`, `last_run`, `argument` FROM `*PREFIX*jobs` WHERE `id` = ?'); - $result = $query->execute(array($id)); - if ($row = $result->fetchRow()) { + $query = $this->conn->prepare('SELECT `id`, `class`, `last_run`, `argument` FROM `*PREFIX*jobs` WHERE `id` = ?'); + $query->execute(array($id)); + if ($row = $query->fetch()) { return $this->buildJob($row); } else { return null; @@ -148,7 +160,7 @@ class JobList { * @param Job $job */ public function setLastJob($job) { - \OC_Appconfig::setValue('backgroundjob', 'lastjob', $job->getId()); + $this->config->setAppValue('backgroundjob', 'lastjob', $job->getId()); } /** @@ -157,7 +169,7 @@ class JobList { * @return int */ public function getLastJob() { - return \OC_Appconfig::getValue('backgroundjob', 'lastjob', 0); + $this->config->getAppValue('backgroundjob', 'lastjob', 0); } /** @@ -166,7 +178,7 @@ class JobList { * @param Job $job */ public function setLastRun($job) { - $query = \OC_DB::prepare('UPDATE `*PREFIX*jobs` SET `last_run` = ? WHERE `id` = ?'); + $query = $this->conn->prepare('UPDATE `*PREFIX*jobs` SET `last_run` = ? WHERE `id` = ?'); $query->execute(array(time(), $job->getId())); } } diff --git a/lib/private/server.php b/lib/private/server.php index c9e593ec2e..b5ffd08ce7 100644 --- a/lib/private/server.php +++ b/lib/private/server.php @@ -148,6 +148,13 @@ class Server extends SimpleContainer implements IServerContainer { $this->registerService('AvatarManager', function($c) { return new AvatarManager(); }); + $this->registerService('JobList', function ($c) { + /** + * @var Server $c + */ + $config = $c->getConfig(); + return new \OC\BackgroundJob\JobList($c->getDatabaseConnection(), $config); + }); } /** @@ -336,4 +343,13 @@ class Server extends SimpleContainer implements IServerContainer { function getActivityManager() { return $this->query('ActivityManager'); } + + /** + * Returns an job list for controlling background jobs + * + * @return \OCP\BackgroundJob\IJobList + */ + function getJobList(){ + return $this->query('JobList'); + } } diff --git a/lib/public/backgroundjob.php b/lib/public/backgroundjob.php index a7f54491df..bcaf6e3545 100644 --- a/lib/public/backgroundjob.php +++ b/lib/public/backgroundjob.php @@ -33,7 +33,7 @@ use \OC\BackgroundJob\JobList; /** * This class provides functions to register backgroundjobs in ownCloud * - * To create a new backgroundjob create a new class that inharits from either \OC\BackgroundJob\Job, + * To create a new backgroundjob create a new class that inherits from either \OC\BackgroundJob\Job, * \OC\BackgroundJob\QueuedJob or \OC\BackgroundJob\TimedJob and register it using * \OCP\BackgroundJob->registerJob($job, $argument), $argument will be passed to the run() function * of the job when the job is executed. @@ -73,7 +73,7 @@ class BackgroundJob { * @param mixed $argument */ public static function registerJob($job, $argument = null) { - $jobList = new JobList(); + $jobList = \OC::$server->getJobList(); $jobList->add($job, $argument); } @@ -99,7 +99,7 @@ class BackgroundJob { * key is string "$klass-$method", value is array( $klass, $method ) */ static public function allRegularTasks() { - $jobList = new JobList(); + $jobList = \OC::$server->getJobList(); $allJobs = $jobList->getAll(); $regularJobs = array(); foreach ($allJobs as $job) { @@ -118,7 +118,7 @@ class BackgroundJob { * @return associative array */ public static function findQueuedTask($id) { - $jobList = new JobList(); + $jobList = \OC::$server->getJobList(); return $jobList->getById($id); } @@ -128,7 +128,7 @@ class BackgroundJob { * @return array with associative arrays */ public static function allQueuedTasks() { - $jobList = new JobList(); + $jobList = \OC::$server->getJobList(); $allJobs = $jobList->getAll(); $queuedJobs = array(); foreach ($allJobs as $job) { @@ -148,7 +148,7 @@ class BackgroundJob { * @return array with associative arrays */ public static function queuedTaskWhereAppIs($app) { - $jobList = new JobList(); + $jobList = \OC::$server->getJobList(); $allJobs = $jobList->getAll(); $queuedJobs = array(); foreach ($allJobs as $job) { @@ -186,7 +186,7 @@ class BackgroundJob { * Deletes a report */ public static function deleteQueuedTask($id) { - $jobList = new JobList(); + $jobList = \OC::$server->getJobList(); $job = $jobList->getById($id); if ($job) { $jobList->remove($job); diff --git a/lib/public/backgroundjob/ijob.php b/lib/public/backgroundjob/ijob.php new file mode 100644 index 0000000000..c427b50401 --- /dev/null +++ b/lib/public/backgroundjob/ijob.php @@ -0,0 +1,29 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace OCP\BackgroundJob; + +interface IJob { + /** + * @param \OCP\BackgroundJob\IJobList $jobList + * @param \OC\Log $logger + */ + public function execute($jobList, $logger = null); + + public function setId($id); + + public function setLastRun($lastRun); + + public function setArgument($argument); + + public function getId(); + + public function getLastRun(); + + public function getArgument(); +} diff --git a/lib/public/backgroundjob/ijoblist.php b/lib/public/backgroundjob/ijoblist.php new file mode 100644 index 0000000000..7e80d51755 --- /dev/null +++ b/lib/public/backgroundjob/ijoblist.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 OCP\BackgroundJob; + +interface IJobList { + /** + * @param \OCP\BackgroundJob\IJob |string $job + * @param mixed $argument + */ + public function add($job, $argument = null); + + /** + * @param \OCP\BackgroundJob\IJob|string $job + * @param mixed $argument + */ + public function remove($job, $argument = null); + + /** + * check if a job is in the list + * + * @param $job + * @param mixed $argument + * @return bool + */ + public function has($job, $argument); + + /** + * get all jobs in the list + * + * @return \OCP\BackgroundJob\IJob[] + */ + public function getAll(); + + /** + * get the next job in the list + * + * @return \OCP\BackgroundJob\IJob + */ + public function getNext(); + + /** + * @param int $id + * @return \OCP\BackgroundJob\IJob + */ + public function getById($id); + + /** + * set the job that was last ran + * + * @param \OCP\BackgroundJob\IJob $job + */ + public function setLastJob($job); + + /** + * get the id of the last ran job + * + * @return int + */ + public function getLastJob(); + + /** + * set the lastRun of $job to now + * + * @param \OCP\BackgroundJob\IJob $job + */ + public function setLastRun($job); +} diff --git a/lib/public/iservercontainer.php b/lib/public/iservercontainer.php index 5473f3ee33..0658bd0b02 100644 --- a/lib/public/iservercontainer.php +++ b/lib/public/iservercontainer.php @@ -176,4 +176,11 @@ interface IServerContainer { */ function getAvatarManager(); + /** + * Returns an job list for controlling background jobs + * + * @return \OCP\BackgroundJob\IJobList + */ + function getJobList(); + } diff --git a/tests/lib/backgroundjob/dummyjoblist.php b/tests/lib/backgroundjob/dummyjoblist.php index e1579c273b..7801269b27 100644 --- a/tests/lib/backgroundjob/dummyjoblist.php +++ b/tests/lib/backgroundjob/dummyjoblist.php @@ -21,6 +21,8 @@ class DummyJobList extends \OC\BackgroundJob\JobList { private $last = 0; + public function __construct(){} + /** * @param \OC\BackgroundJob\Job|string $job * @param mixed $argument From 50cc6a85e5d4c817235aee58e9d461e4034df27b Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 11 Feb 2014 14:26:40 +0100 Subject: [PATCH 34/49] Add explicit sorting --- lib/private/appconfig.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/private/appconfig.php b/lib/private/appconfig.php index 71aa830731..a47e1ad49e 100644 --- a/lib/private/appconfig.php +++ b/lib/private/appconfig.php @@ -92,7 +92,7 @@ class AppConfig implements \OCP\IAppConfig { * entry in the appconfig table. */ public function getApps() { - $query = 'SELECT DISTINCT `appid` FROM `*PREFIX*appconfig`'; + $query = 'SELECT DISTINCT `appid` FROM `*PREFIX*appconfig` ORDER BY `appid`'; $result = $this->conn->executeQuery($query); $apps = array(); @@ -112,7 +112,9 @@ class AppConfig implements \OCP\IAppConfig { */ public function getKeys($app) { $values = $this->getAppValues($app); - return array_keys($values); + $keys = array_keys($values); + sort($keys); + return $keys; } /** From 3aafa837b884e8bd701d14665d1c93e33ecc7f7e Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 12 Feb 2014 13:25:50 +0100 Subject: [PATCH 35/49] Remove internal methods from the public interface --- lib/public/backgroundjob/ijob.php | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/lib/public/backgroundjob/ijob.php b/lib/public/backgroundjob/ijob.php index c427b50401..5bf815ef8e 100644 --- a/lib/public/backgroundjob/ijob.php +++ b/lib/public/backgroundjob/ijob.php @@ -1,6 +1,6 @@ + * Copyright (c) 2014 Robin Appelman * This file is licensed under the Affero General Public License version 3 or * later. * See the COPYING-README file. @@ -15,12 +15,6 @@ interface IJob { */ public function execute($jobList, $logger = null); - public function setId($id); - - public function setLastRun($lastRun); - - public function setArgument($argument); - public function getId(); public function getLastRun(); From 62288971cacf9049ec4521d659a17857ac42d139 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 12 Feb 2014 13:32:16 +0100 Subject: [PATCH 36/49] Additional phpdoc --- lib/private/backgroundjob/joblist.php | 6 ++++-- lib/public/backgroundjob/ijob.php | 21 ++++++++++++++++++++- lib/public/backgroundjob/ijoblist.php | 10 +++++++--- 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/lib/private/backgroundjob/joblist.php b/lib/private/backgroundjob/joblist.php index 6b0df9e0d6..5ec81dc3fc 100644 --- a/lib/private/backgroundjob/joblist.php +++ b/lib/private/backgroundjob/joblist.php @@ -1,6 +1,6 @@ + * Copyright (c) 2014 Robin Appelman * This file is licensed under the Affero General Public License version 3 or * later. * See the COPYING-README file. @@ -8,7 +8,9 @@ namespace OC\BackgroundJob; -class JobList { +use OCP\BackgroundJob\IJobList; + +class JobList implements IJobList { /** * @var \OCP\IDBConnection */ diff --git a/lib/public/backgroundjob/ijob.php b/lib/public/backgroundjob/ijob.php index 5bf815ef8e..5231e9537a 100644 --- a/lib/public/backgroundjob/ijob.php +++ b/lib/public/backgroundjob/ijob.php @@ -10,14 +10,33 @@ namespace OCP\BackgroundJob; interface IJob { /** - * @param \OCP\BackgroundJob\IJobList $jobList + * Run the background job with the registered argument + * + * @param \OCP\BackgroundJob\IJobList $jobList The job list that manages the state of this job * @param \OC\Log $logger */ public function execute($jobList, $logger = null); + /** + * Get the id of the background job + * This id is determined by the job list when a job is added to the list + * + * @return int + */ public function getId(); + /** + * Get the last time this job was run as unix timestamp + * + * @return int + */ public function getLastRun(); + /** + * Get the argument associated with the background job + * This is the argument that will be passed to the background job + * + * @return mixed + */ public function getArgument(); } diff --git a/lib/public/backgroundjob/ijoblist.php b/lib/public/backgroundjob/ijoblist.php index 7e80d51755..72f3fe863d 100644 --- a/lib/public/backgroundjob/ijoblist.php +++ b/lib/public/backgroundjob/ijoblist.php @@ -1,6 +1,6 @@ + * Copyright (c) 2014 Robin Appelman * This file is licensed under the Affero General Public License version 3 or * later. * See the COPYING-README file. @@ -10,12 +10,16 @@ namespace OCP\BackgroundJob; interface IJobList { /** + * Add a job to the list + * * @param \OCP\BackgroundJob\IJob |string $job - * @param mixed $argument + * @param mixed $argument The argument to be passed to $job->run() when the job is exectured */ public function add($job, $argument = null); /** + * Remove a job from the list + * * @param \OCP\BackgroundJob\IJob|string $job * @param mixed $argument */ @@ -51,7 +55,7 @@ interface IJobList { public function getById($id); /** - * set the job that was last ran + * set the job that was last ran to the current time * * @param \OCP\BackgroundJob\IJob $job */ From d6576c640c429d24a6329573c0dfaf99d82ac867 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 12 Feb 2014 13:52:13 +0100 Subject: [PATCH 37/49] Add unit tests for JobList --- lib/private/backgroundjob/joblist.php | 2 +- tests/lib/backgroundjob/job.php | 25 ---- tests/lib/backgroundjob/joblist.php | 203 ++++++++++++++++++++++++++ tests/lib/backgroundjob/testjob.php | 34 +++++ 4 files changed, 238 insertions(+), 26 deletions(-) create mode 100644 tests/lib/backgroundjob/joblist.php create mode 100644 tests/lib/backgroundjob/testjob.php diff --git a/lib/private/backgroundjob/joblist.php b/lib/private/backgroundjob/joblist.php index 5ec81dc3fc..586e99a3cb 100644 --- a/lib/private/backgroundjob/joblist.php +++ b/lib/private/backgroundjob/joblist.php @@ -171,7 +171,7 @@ class JobList implements IJobList { * @return int */ public function getLastJob() { - $this->config->getAppValue('backgroundjob', 'lastjob', 0); + return $this->config->getAppValue('backgroundjob', 'lastjob', 0); } /** diff --git a/tests/lib/backgroundjob/job.php b/tests/lib/backgroundjob/job.php index 7d66fa772d..10a8f46462 100644 --- a/tests/lib/backgroundjob/job.php +++ b/tests/lib/backgroundjob/job.php @@ -8,31 +8,6 @@ namespace Test\BackgroundJob; - -class TestJob extends \OC\BackgroundJob\Job { - private $testCase; - - /** - * @var callable $callback - */ - private $callback; - - /** - * @param Job $testCase - * @param callable $callback - */ - public function __construct($testCase, $callback) { - $this->testCase = $testCase; - $this->callback = $callback; - } - - public function run($argument) { - $this->testCase->markRun(); - $callback = $this->callback; - $callback($argument); - } -} - class Job extends \PHPUnit_Framework_TestCase { private $run = false; diff --git a/tests/lib/backgroundjob/joblist.php b/tests/lib/backgroundjob/joblist.php new file mode 100644 index 0000000000..c3318f80cb --- /dev/null +++ b/tests/lib/backgroundjob/joblist.php @@ -0,0 +1,203 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace Test\BackgroundJob; + +class JobList extends \PHPUnit_Framework_TestCase { + /** + * @var \OC\BackgroundJob\JobList + */ + protected $instance; + + /** + * @var \OCP\IConfig | \PHPUnit_Framework_MockObject_MockObject $config + */ + protected $config; + + public function setUp() { + $conn = \OC::$server->getDatabaseConnection(); + $this->config = $this->getMock('\OCP\IConfig'); + $this->instance = new \OC\BackgroundJob\JobList($conn, $this->config); + } + + public function argumentProvider() { + return array( + array(null), + array(false), + array('foobar'), + array(12), + array(array( + 'asd' => 5, + 'foo' => 'bar' + )) + ); + } + + /** + * @dataProvider argumentProvider + * @param $argument + */ + public function testAddRemove($argument) { + $existingJobs = $this->instance->getAll(); + $job = new TestJob(); + $this->instance->add($job, $argument); + + $jobs = $this->instance->getAll(); + + $this->assertCount(count($existingJobs) + 1, $jobs); + $addedJob = $jobs[count($jobs) - 1]; + $this->assertInstanceOf('\Test\BackgroundJob\TestJob', $addedJob); + $this->assertEquals($argument, $addedJob->getArgument()); + + $this->instance->remove($job, $argument); + + $jobs = $this->instance->getAll(); + $this->assertEquals($existingJobs, $jobs); + } + + /** + * @dataProvider argumentProvider + * @param $argument + */ + public function testRemoveDifferentArgument($argument) { + $existingJobs = $this->instance->getAll(); + $job = new TestJob(); + $this->instance->add($job, $argument); + + $jobs = $this->instance->getAll(); + $this->instance->remove($job, 10); + $jobs2 = $this->instance->getAll(); + + $this->assertEquals($jobs, $jobs2); + + $this->instance->remove($job, $argument); + + $jobs = $this->instance->getAll(); + $this->assertEquals($existingJobs, $jobs); + } + + /** + * @dataProvider argumentProvider + * @param $argument + */ + public function testHas($argument) { + $job = new TestJob(); + $this->assertFalse($this->instance->has($job, $argument)); + $this->instance->add($job, $argument); + + $this->assertTrue($this->instance->has($job, $argument)); + + $this->instance->remove($job, $argument); + + $this->assertFalse($this->instance->has($job, $argument)); + } + + /** + * @dataProvider argumentProvider + * @param $argument + */ + public function testHasDifferentArgument($argument) { + $job = new TestJob(); + $this->instance->add($job, $argument); + + $this->assertFalse($this->instance->has($job, 10)); + + $this->instance->remove($job, $argument); + } + + public function testGetLastJob() { + $this->config->expects($this->once()) + ->method('getAppValue') + ->with('backgroundjob', 'lastjob', 0) + ->will($this->returnValue(15)); + + $this->assertEquals(15, $this->instance->getLastJob()); + } + + public function testGetNext() { + $job = new TestJob(); + $this->instance->add($job, 1); + $this->instance->add($job, 2); + + $jobs = $this->instance->getAll(); + + $savedJob1 = $jobs[count($jobs) - 2]; + $savedJob2 = $jobs[count($jobs) - 1]; + + $this->config->expects($this->once()) + ->method('getAppValue') + ->with('backgroundjob', 'lastjob', 0) + ->will($this->returnValue($savedJob1->getId())); + + $nextJob = $this->instance->getNext(); + + $this->assertEquals($savedJob2, $nextJob); + + $this->instance->remove($job, 1); + $this->instance->remove($job, 2); + } + + public function testGetNextWrapAround() { + $job = new TestJob(); + $this->instance->add($job, 1); + $this->instance->add($job, 2); + + $jobs = $this->instance->getAll(); + + $savedJob2 = $jobs[count($jobs) - 1]; + + $this->config->expects($this->once()) + ->method('getAppValue') + ->with('backgroundjob', 'lastjob', 0) + ->will($this->returnValue($savedJob2->getId())); + + $nextJob = $this->instance->getNext(); + + $this->assertEquals($jobs[0], $nextJob); + + $this->instance->remove($job, 1); + $this->instance->remove($job, 2); + } + + /** + * @dataProvider argumentProvider + * @param $argument + */ + public function testGetById($argument) { + $job = new TestJob(); + $this->instance->add($job, $argument); + + $jobs = $this->instance->getAll(); + + $addedJob = $jobs[count($jobs) - 1]; + + $this->assertEquals($addedJob, $this->instance->getById($addedJob->getId())); + + $this->instance->remove($job, $argument); + } + + public function testSetLastRun() { + $job = new TestJob(); + $this->instance->add($job); + + $jobs = $this->instance->getAll(); + + $addedJob = $jobs[count($jobs) - 1]; + + $timeStart = time(); + $this->instance->setLastRun($addedJob); + $timeEnd = time(); + + $addedJob = $this->instance->getById($addedJob->getId()); + + $this->assertGreaterThanOrEqual($timeStart, $addedJob->getLastRun()); + $this->assertLessThanOrEqual($timeEnd, $addedJob->getLastRun()); + + $this->instance->remove($job); + } +} diff --git a/tests/lib/backgroundjob/testjob.php b/tests/lib/backgroundjob/testjob.php new file mode 100644 index 0000000000..23fc4268d1 --- /dev/null +++ b/tests/lib/backgroundjob/testjob.php @@ -0,0 +1,34 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace Test\BackgroundJob; + + +class TestJob extends \OC\BackgroundJob\Job { + private $testCase; + + /** + * @var callable $callback + */ + private $callback; + + /** + * @param Job $testCase + * @param callable $callback + */ + public function __construct($testCase = null, $callback = null) { + $this->testCase = $testCase; + $this->callback = $callback; + } + + public function run($argument) { + $this->testCase->markRun(); + $callback = $this->callback; + $callback($argument); + } +} From 9619459e37dce3723eb912e1ea9f149dc3f5de49 Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Wed, 12 Feb 2014 11:43:34 +0100 Subject: [PATCH 38/49] Send correct path on file upload when using public app Fix issue #7152 --- apps/files/ajax/upload.php | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/apps/files/ajax/upload.php b/apps/files/ajax/upload.php index 754c34ef08..145f40c50d 100644 --- a/apps/files/ajax/upload.php +++ b/apps/files/ajax/upload.php @@ -22,6 +22,7 @@ if (empty($_POST['dirToken'])) { } else { // return only read permissions for public upload $allowedPermissions = OCP\PERMISSION_READ; + $public_directory = !empty($_POST['subdir']) ? $_POST['subdir'] : '/'; $linkItem = OCP\Share::getShareByToken($_POST['dirToken']); if ($linkItem === false) { @@ -45,7 +46,7 @@ if (empty($_POST['dirToken'])) { $dir = sprintf( "/%s/%s", $path, - isset($_POST['subdir']) ? $_POST['subdir'] : '' + $public_directory ); if (!$dir || empty($dir) || $dir === false) { @@ -112,7 +113,14 @@ if (strpos($dir, '..') === false) { } else { $target = \OC\Files\Filesystem::normalizePath(stripslashes($dir).'/'.$files['name'][$i]); } - + + $directory = \OC\Files\Filesystem::normalizePath(stripslashes($dir)); + if (isset($public_directory)) { + // If we are uploading from the public app, + // we want to send the relative path in the ajax request. + $directory = $public_directory; + } + if ( ! \OC\Files\Filesystem::file_exists($target) || (isset($_POST['resolution']) && $_POST['resolution']==='replace') ) { @@ -140,7 +148,7 @@ if (strpos($dir, '..') === false) { 'uploadMaxFilesize' => $maxUploadFileSize, 'maxHumanFilesize' => $maxHumanFileSize, 'permissions' => $meta['permissions'] & $allowedPermissions, - 'directory' => \OC\Files\Filesystem::normalizePath(stripslashes($dir)), + 'directory' => $directory, ); } @@ -168,7 +176,7 @@ if (strpos($dir, '..') === false) { 'uploadMaxFilesize' => $maxUploadFileSize, 'maxHumanFilesize' => $maxHumanFileSize, 'permissions' => $meta['permissions'] & $allowedPermissions, - 'directory' => \OC\Files\Filesystem::normalizePath(stripslashes($dir)), + 'directory' => $directory, ); } } From 658af627ce3add85282a73fa711f28745041694d Mon Sep 17 00:00:00 2001 From: Joas Schilling Date: Wed, 12 Feb 2014 18:18:09 +0100 Subject: [PATCH 39/49] External FTP Storage should request hostname instead of URL Fix issue #6277 --- apps/files_external/lib/config.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/files_external/lib/config.php b/apps/files_external/lib/config.php index 48fc4dfe46..8c85c5fbde 100755 --- a/apps/files_external/lib/config.php +++ b/apps/files_external/lib/config.php @@ -69,7 +69,7 @@ class OC_Mount_Config { if(OC_Mount_Config::checkphpftp()) $backends['\OC\Files\Storage\FTP']=array( 'backend' => 'FTP', 'configuration' => array( - 'host' => 'URL', + 'host' => 'Hostname', 'user' => 'Username', 'password' => '*Password', 'root' => '&Root', From 6e17d71a0df5d36253afa26d84e89dcf3331c6c5 Mon Sep 17 00:00:00 2001 From: Jan-Christoph Borchardt Date: Thu, 13 Feb 2014 10:39:38 +0100 Subject: [PATCH 40/49] remove forced lowercase from extension, fix #4747, also coding style fixes --- apps/files/css/files.css | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/apps/files/css/files.css b/apps/files/css/files.css index 5526abaf6e..27fa75ebc5 100644 --- a/apps/files/css/files.css +++ b/apps/files/css/files.css @@ -84,9 +84,26 @@ background-color: rgb(240,240,240); } tbody a { color:#000; } -span.extension, span.uploading, td.date { color:#999; } -span.extension { text-transform:lowercase; -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=70)"; filter:alpha(opacity=70); opacity:.7; -webkit-transition:opacity 300ms; -moz-transition:opacity 300ms; -o-transition:opacity 300ms; transition:opacity 300ms; } -tr:hover span.extension { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; filter:alpha(opacity=100); opacity:1; color:#777; } + +span.extension, span.uploading, td.date { + color: #999; +} +span.extension { + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=70)"; + filter: alpha(opacity=70); + opacity: .7; + -webkit-transition: opacity 300ms; + -moz-transition: opacity 300ms; + -o-transition: opacity 300ms; + transition: opacity 300ms; +} +tr:hover span.extension { + -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)"; + filter: alpha(opacity=100); + opacity: 1; + color: #777; +} + table tr.mouseOver td { background-color:#eee; } table th { height:24px; padding:0 8px; color:#999; } table th .name { From 3b1df2931818b90b734c8bc3a6e0dbdf1517feaf Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Thu, 13 Feb 2014 13:56:02 +0100 Subject: [PATCH 41/49] sort expected result in tests --- tests/lib/appconfig.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/lib/appconfig.php b/tests/lib/appconfig.php index 5cbdf80502..6ae790a9ed 100644 --- a/tests/lib/appconfig.php +++ b/tests/lib/appconfig.php @@ -42,6 +42,7 @@ class Test_Appconfig extends PHPUnit_Framework_TestCase { while ($row = $result->fetchRow()) { $expected[] = $row['appid']; } + sort($expected); $apps = \OC_Appconfig::getApps(); $this->assertEquals($expected, $apps); } @@ -53,6 +54,7 @@ class Test_Appconfig extends PHPUnit_Framework_TestCase { while($row = $result->fetchRow()) { $expected[] = $row["configkey"]; } + sort($expected); $keys = \OC_Appconfig::getKeys('testapp'); $this->assertEquals($expected, $keys); } From f62f1658ceba6ad02ada0823243d779a698f5f7f Mon Sep 17 00:00:00 2001 From: Georg Ehrke Date: Fri, 14 Feb 2014 11:23:39 +0100 Subject: [PATCH 42/49] suppress error msg caused by php bug --- lib/private/preview/office.php | 2 +- lib/private/preview/pdf.php | 2 +- lib/private/preview/svg.php | 2 +- lib/private/preview/unknown.php | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/private/preview/office.php b/lib/private/preview/office.php index 884b6e7dc9..02bb22e9b9 100644 --- a/lib/private/preview/office.php +++ b/lib/private/preview/office.php @@ -6,7 +6,7 @@ * See the COPYING-README file. */ //both, libreoffice backend and php fallback, need imagick -if (extension_loaded('imagick') && count(\Imagick::queryFormats("PDF")) === 1) { +if (extension_loaded('imagick') && count(@\Imagick::queryFormats("PDF")) === 1) { $isShellExecEnabled = \OC_Helper::is_function_enabled('shell_exec'); // LibreOffice preview is currently not supported on Windows diff --git a/lib/private/preview/pdf.php b/lib/private/preview/pdf.php index 572b8788ac..d390b4fc67 100644 --- a/lib/private/preview/pdf.php +++ b/lib/private/preview/pdf.php @@ -7,7 +7,7 @@ */ namespace OC\Preview; -if (extension_loaded('imagick') && count(\Imagick::queryFormats("PDF")) === 1) { +if (extension_loaded('imagick') && count(@\Imagick::queryFormats("PDF")) === 1) { class PDF extends Provider { diff --git a/lib/private/preview/svg.php b/lib/private/preview/svg.php index 07a37e8f8c..9a73fff946 100644 --- a/lib/private/preview/svg.php +++ b/lib/private/preview/svg.php @@ -7,7 +7,7 @@ */ namespace OC\Preview; -if (extension_loaded('imagick') && count(\Imagick::queryFormats("SVG")) === 1) { +if (extension_loaded('imagick') && count(@\Imagick::queryFormats("SVG")) === 1) { class SVG extends Provider { diff --git a/lib/private/preview/unknown.php b/lib/private/preview/unknown.php index 8145c82614..2d3b5c5655 100644 --- a/lib/private/preview/unknown.php +++ b/lib/private/preview/unknown.php @@ -22,7 +22,7 @@ class Unknown extends Provider { $svgPath = substr_replace($path, 'svg', -3); - if (extension_loaded('imagick') && file_exists($svgPath) && count(\Imagick::queryFormats("SVG")) === 1) { + if (extension_loaded('imagick') && file_exists($svgPath) && count(@\Imagick::queryFormats("SVG")) === 1) { // http://www.php.net/manual/de/imagick.setresolution.php#85284 $svg = new \Imagick(); From 0ba0596341779100db37e25e59ac34f5a57bd2c3 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 14 Feb 2014 14:25:45 +0100 Subject: [PATCH 43/49] remove duplicate call to groupExists --- lib/private/group/manager.php | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/private/group/manager.php b/lib/private/group/manager.php index bf469d51d1..29453c5da1 100644 --- a/lib/private/group/manager.php +++ b/lib/private/group/manager.php @@ -76,12 +76,7 @@ class Manager extends PublicEmitter { if (isset($this->cachedGroups[$gid])) { return $this->cachedGroups[$gid]; } - foreach ($this->backends as $backend) { - if ($backend->groupExists($gid)) { - return $this->getGroupObject($gid); - } - } - return null; + return $this->getGroupObject($gid); } protected function getGroupObject($gid) { @@ -91,6 +86,9 @@ class Manager extends PublicEmitter { $backends[] = $backend; } } + if (count($backends) === 0) { + return null; + } $this->cachedGroups[$gid] = new Group($gid, $backends, $this->userManager, $this); return $this->cachedGroups[$gid]; } From b35f679483f2b8f1dab56b903ca2e942ac4606ff Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 14 Feb 2014 15:07:08 +0100 Subject: [PATCH 44/49] Fix test cases for group manager --- lib/private/group/manager.php | 4 ++-- tests/lib/group/manager.php | 37 +++++++++++++++++++++++++++++++++-- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/lib/private/group/manager.php b/lib/private/group/manager.php index 29453c5da1..9b433b64fd 100644 --- a/lib/private/group/manager.php +++ b/lib/private/group/manager.php @@ -108,8 +108,8 @@ class Manager extends PublicEmitter { public function createGroup($gid) { if (!$gid) { return false; - } else if ($this->groupExists($gid)) { - return $this->get($gid); + } else if ($group = $this->get($gid)) { + return $group; } else { $this->emit('\OC\Group', 'preCreate', array($gid)); foreach ($this->backends as $backend) { diff --git a/tests/lib/group/manager.php b/tests/lib/group/manager.php index 9d3adf51a0..90f0e1b35e 100644 --- a/tests/lib/group/manager.php +++ b/tests/lib/group/manager.php @@ -116,16 +116,22 @@ class Manager extends \PHPUnit_Framework_TestCase { /** * @var \PHPUnit_Framework_MockObject_MockObject | \OC_Group_Backend $backend */ + $backendGroupCreated = false; $backend = $this->getMock('\OC_Group_Database'); $backend->expects($this->any()) ->method('groupExists') ->with('group1') - ->will($this->returnValue(false)); + ->will($this->returnCallback(function () use (&$backendGroupCreated) { + return $backendGroupCreated; + })); $backend->expects($this->once()) ->method('implementsActions') ->will($this->returnValue(true)); $backend->expects($this->once()) - ->method('createGroup'); + ->method('createGroup') + ->will($this->returnCallback(function () use (&$backendGroupCreated) { + $backendGroupCreated = true; + }));; /** * @var \OC\User\Manager $userManager @@ -170,6 +176,10 @@ class Manager extends \PHPUnit_Framework_TestCase { ->method('getGroups') ->with('1') ->will($this->returnValue(array('group1'))); + $backend->expects($this->once()) + ->method('groupExists') + ->with('group1') + ->will($this->returnValue(true)); /** * @var \OC\User\Manager $userManager @@ -193,6 +203,9 @@ class Manager extends \PHPUnit_Framework_TestCase { ->method('getGroups') ->with('1') ->will($this->returnValue(array('group1'))); + $backend1->expects($this->any()) + ->method('groupExists') + ->will($this->returnValue(true)); /** * @var \PHPUnit_Framework_MockObject_MockObject | \OC_Group_Backend $backend2 @@ -202,6 +215,9 @@ class Manager extends \PHPUnit_Framework_TestCase { ->method('getGroups') ->with('1') ->will($this->returnValue(array('group12', 'group1'))); + $backend2->expects($this->any()) + ->method('groupExists') + ->will($this->returnValue(true)); /** * @var \OC\User\Manager $userManager @@ -228,6 +244,9 @@ class Manager extends \PHPUnit_Framework_TestCase { ->method('getGroups') ->with('1', 2, 1) ->will($this->returnValue(array('group1'))); + $backend1->expects($this->any()) + ->method('groupExists') + ->will($this->returnValue(true)); /** * @var \PHPUnit_Framework_MockObject_MockObject | \OC_Group_Backend $backend2 @@ -237,6 +256,9 @@ class Manager extends \PHPUnit_Framework_TestCase { ->method('getGroups') ->with('1', 1, 0) ->will($this->returnValue(array('group12'))); + $backend2->expects($this->any()) + ->method('groupExists') + ->will($this->returnValue(true)); /** * @var \OC\User\Manager $userManager @@ -263,6 +285,10 @@ class Manager extends \PHPUnit_Framework_TestCase { ->method('getUserGroups') ->with('user1') ->will($this->returnValue(array('group1'))); + $backend->expects($this->any()) + ->method('groupExists') + ->with('group1') + ->will($this->returnValue(true)); /** * @var \OC\User\Manager $userManager @@ -286,6 +312,10 @@ class Manager extends \PHPUnit_Framework_TestCase { ->method('getUserGroups') ->with('user1') ->will($this->returnValue(array('group1'))); + $backend1->expects($this->any()) + ->method('groupExists') + ->will($this->returnValue(true)); + /** * @var \PHPUnit_Framework_MockObject_MockObject | \OC_Group_Backend $backend2 */ @@ -294,6 +324,9 @@ class Manager extends \PHPUnit_Framework_TestCase { ->method('getUserGroups') ->with('user1') ->will($this->returnValue(array('group1', 'group2'))); + $backend1->expects($this->any()) + ->method('groupExists') + ->will($this->returnValue(true)); /** * @var \OC\User\Manager $userManager From 29336683555b9ce8e4c365d6d0b02a135815b5f2 Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Fri, 14 Feb 2014 18:05:59 +0100 Subject: [PATCH 45/49] fileinfo is no longer a array --- apps/files_encryption/tests/hooks.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/files_encryption/tests/hooks.php b/apps/files_encryption/tests/hooks.php index 4452579174..7d926caea1 100644 --- a/apps/files_encryption/tests/hooks.php +++ b/apps/files_encryption/tests/hooks.php @@ -203,7 +203,7 @@ class Test_Encryption_Hooks extends \PHPUnit_Framework_TestCase { $fileInfo = $this->user1View->getFileInfo($this->filename); // check if we have a valid file info - $this->assertTrue(is_array($fileInfo)); + $this->assertTrue($fileInfo instanceof \OC\Files\FileInfo); // share the file with user2 \OCP\Share::shareItem('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_USER, self::TEST_ENCRYPTION_HOOKS_USER2, OCP\PERMISSION_ALL); From 09502fcb368c6b18a936b0b0aeb0e685829ad570 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 14 Feb 2014 19:24:12 +0100 Subject: [PATCH 46/49] remove the JsonSerializable interface from \OC\Files\FileInfo --- lib/private/files/fileinfo.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/private/files/fileinfo.php b/lib/private/files/fileinfo.php index 7edea13df9..c77571cd2a 100644 --- a/lib/private/files/fileinfo.php +++ b/lib/private/files/fileinfo.php @@ -8,7 +8,7 @@ namespace OC\Files; -class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess, \JsonSerializable { +class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess { /** * @var array $data */ @@ -52,10 +52,6 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess, \JsonSerializable { return $this->data[$offset]; } - public function jsonSerialize() { - return $this->data; - } - /** * @return string */ From 2ab062193a355e87946f310c992d5449eaf558cc Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Mon, 10 Feb 2014 16:43:04 +0100 Subject: [PATCH 47/49] catch errors during initial encryption --- apps/files_encryption/hooks/hooks.php | 22 +++++++---- apps/files_encryption/lib/util.php | 53 +++++++++++++++++---------- 2 files changed, 48 insertions(+), 27 deletions(-) diff --git a/apps/files_encryption/hooks/hooks.php b/apps/files_encryption/hooks/hooks.php index 83abf3ba9d..1eb5f4c41e 100644 --- a/apps/files_encryption/hooks/hooks.php +++ b/apps/files_encryption/hooks/hooks.php @@ -109,21 +109,27 @@ class Hooks { } - // Encrypt existing user files: - if ( - $util->encryptAll('/' . $params['uid'] . '/' . 'files', $session->getLegacyKey(), $params['password']) - ) { + // Encrypt existing user files + try { + $result = $util->encryptAll('/' . $params['uid'] . '/' . 'files', $session->getLegacyKey(), $params['password']); + } catch (\Exception $ex) { + \OCP\Util::writeLog('Encryption library', 'Initial encryption failed! Error: ' . $ex->getMessage(), \OCP\Util::FATAL); + $util->resetMigrationStatus(); + \OCP\User::logout(); + $result = false; + } + + if ($result) { \OC_Log::write( 'Encryption library', 'Encryption of existing files belonging to "' . $params['uid'] . '" completed' , \OC_Log::INFO ); + // Register successful migration in DB + $util->finishMigration(); + } - - // Register successful migration in DB - $util->finishMigration(); - } return true; diff --git a/apps/files_encryption/lib/util.php b/apps/files_encryption/lib/util.php index ae3e2a2e15..ced4b823cf 100644 --- a/apps/files_encryption/lib/util.php +++ b/apps/files_encryption/lib/util.php @@ -1185,27 +1185,49 @@ class Util { return $result; } + /** + * @brief set migration status + * @param int $status + * @return boolean + */ + private function setMigrationStatus($status) { + + $sql = 'UPDATE `*PREFIX*encryption` SET `migration_status` = ? WHERE `uid` = ?'; + $args = array($status, $this->userId); + $query = \OCP\DB::prepare($sql); + $manipulatedRows = $query->execute($args); + + if ($manipulatedRows === 1) { + $result = true; + \OCP\Util::writeLog('Encryption library', "Migration status set to " . self::MIGRATION_OPEN, \OCP\Util::INFO); + } else { + $result = false; + \OCP\Util::writeLog('Encryption library', "Could not set migration status to " . self::MIGRATION_OPEN, \OCP\Util::WARN); + } + + return $result; + } + /** * @brief start migration mode to initially encrypt users data * @return boolean */ public function beginMigration() { - $return = false; + $result = $this->setMigrationStatus(self::MIGRATION_IN_PROGRESS); - $sql = 'UPDATE `*PREFIX*encryption` SET `migration_status` = ? WHERE `uid` = ? and `migration_status` = ?'; - $args = array(self::MIGRATION_IN_PROGRESS, $this->userId, self::MIGRATION_OPEN); - $query = \OCP\DB::prepare($sql); - $manipulatedRows = $query->execute($args); - - if ($manipulatedRows === 1) { - $return = true; + if ($result) { \OCP\Util::writeLog('Encryption library', "Start migration to encryption mode for " . $this->userId, \OCP\Util::INFO); } else { \OCP\Util::writeLog('Encryption library', "Could not activate migration mode for " . $this->userId . ". Probably another process already started the initial encryption", \OCP\Util::WARN); } - return $return; + return $result; + } + + public function resetMigrationStatus() { + return $this->setMigrationStatus(self::MIGRATION_OPEN); + } /** @@ -1213,22 +1235,15 @@ class Util { * @return boolean */ public function finishMigration() { + $result = $this->setMigrationStatus(self::MIGRATION_COMPLETED); - $return = false; - - $sql = 'UPDATE `*PREFIX*encryption` SET `migration_status` = ? WHERE `uid` = ? and `migration_status` = ?'; - $args = array(self::MIGRATION_COMPLETED, $this->userId, self::MIGRATION_IN_PROGRESS); - $query = \OCP\DB::prepare($sql); - $manipulatedRows = $query->execute($args); - - if ($manipulatedRows === 1) { - $return = true; + if ($result) { \OCP\Util::writeLog('Encryption library', "Finish migration successfully for " . $this->userId, \OCP\Util::INFO); } else { \OCP\Util::writeLog('Encryption library', "Could not deactivate migration mode for " . $this->userId, \OCP\Util::WARN); } - return $return; + return $result; } /** From f2f5769df7d4c2b33a847e86a71d94d5c689decd Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Mon, 10 Feb 2014 17:23:54 +0100 Subject: [PATCH 48/49] catch errors during decryption --- apps/files_encryption/lib/util.php | 38 ++++++++----- apps/files_encryption/tests/util.php | 81 +++++++++++++++++++++++++++- settings/ajax/decryptall.php | 9 +++- 3 files changed, 112 insertions(+), 16 deletions(-) diff --git a/apps/files_encryption/lib/util.php b/apps/files_encryption/lib/util.php index ced4b823cf..f3f69997f2 100644 --- a/apps/files_encryption/lib/util.php +++ b/apps/files_encryption/lib/util.php @@ -316,7 +316,8 @@ class Util { $found = array( 'plain' => array(), 'encrypted' => array(), - 'legacy' => array() + 'legacy' => array(), + 'broken' => array(), ); } @@ -327,10 +328,7 @@ class Util { if(is_resource($handle)) { while (false !== ($file = readdir($handle))) { - if ( - $file !== "." - && $file !== ".." - ) { + if ($file !== "." && $file !== "..") { $filePath = $directory . '/' . $this->view->getRelativePath('/' . $file); $relPath = \OCA\Encryption\Helper::stripUserFilesPath($filePath); @@ -357,15 +355,23 @@ class Util { // NOTE: This is inefficient; // scanning every file like this // will eat server resources :( - if ( - Keymanager::getFileKey($this->view, $this, $relPath) - && $isEncryptedPath - ) { + if ($isEncryptedPath) { - $found['encrypted'][] = array( - 'name' => $file, - 'path' => $filePath - ); + $fileKey = Keymanager::getFileKey($this->view, $this, $relPath); + $shareKey = Keymanager::getShareKey($this->view, $this->userId, $this, $relPath); + // if file is encrypted but now file key is available, throw exception + if ($fileKey === false || $shareKey === false) { + \OCP\Util::writeLog('encryption library', 'No keys available to decrypt the file: ' . $filePath, \OCP\Util::ERROR); + $found['broken'][] = array( + 'name' => $file, + 'path' => $filePath, + ); + } else { + $found['encrypted'][] = array( + 'name' => $file, + 'path' => $filePath, + ); + } // If the file uses old // encryption system @@ -771,6 +777,12 @@ class Util { $successful = false; } + // if there are broken encrypted files than the complete decryption + // was not successful + if (!empty($found['broken'])) { + $successful = false; + } + if ($successful) { $this->view->deleteAll($this->keyfilesPath); $this->view->deleteAll($this->shareKeysPath); diff --git a/apps/files_encryption/tests/util.php b/apps/files_encryption/tests/util.php index 228f7df1b9..321bdc76f8 100755 --- a/apps/files_encryption/tests/util.php +++ b/apps/files_encryption/tests/util.php @@ -64,6 +64,8 @@ class Test_Encryption_Util extends \PHPUnit_Framework_TestCase { function setUp() { + // login user + \Test_Encryption_Util::loginHelper(\Test_Encryption_Util::TEST_ENCRYPTION_UTIL_USER1); \OC_User::setUserId(\Test_Encryption_Util::TEST_ENCRYPTION_UTIL_USER1); $this->userId = \Test_Encryption_Util::TEST_ENCRYPTION_UTIL_USER1; $this->pass = \Test_Encryption_Util::TEST_ENCRYPTION_UTIL_USER1; @@ -358,9 +360,12 @@ class Test_Encryption_Util extends \PHPUnit_Framework_TestCase { $fileInfoEncrypted = $this->view->getFileInfo($this->userId . '/files/' . $filename); $this->assertTrue($fileInfoEncrypted instanceof \OC\Files\FileInfo); + $this->assertEquals($fileInfoEncrypted['encrypted'], 1); - // encrypt all unencrypted files - $util->decryptAll('/' . $this->userId . '/' . 'files'); + // decrypt all encrypted files + $result = $util->decryptAll('/' . $this->userId . '/' . 'files'); + + $this->assertTrue($result); $fileInfoUnencrypted = $this->view->getFileInfo($this->userId . '/files/' . $filename); @@ -369,11 +374,83 @@ class Test_Encryption_Util extends \PHPUnit_Framework_TestCase { // check if mtime and etags unchanged $this->assertEquals($fileInfoEncrypted['mtime'], $fileInfoUnencrypted['mtime']); $this->assertEquals($fileInfoEncrypted['etag'], $fileInfoUnencrypted['etag']); + // file should no longer be encrypted + $this->assertEquals(0, $fileInfoUnencrypted['encrypted']); $this->view->unlink($this->userId . '/files/' . $filename); } + function testDescryptAllWithBrokenFiles() { + + $file1 = "/decryptAll1" . uniqid() . ".txt"; + $file2 = "/decryptAll2" . uniqid() . ".txt"; + + $util = new Encryption\Util($this->view, $this->userId); + + $this->view->file_put_contents($this->userId . '/files/' . $file1, $this->dataShort); + $this->view->file_put_contents($this->userId . '/files/' . $file2, $this->dataShort); + + $fileInfoEncrypted1 = $this->view->getFileInfo($this->userId . '/files/' . $file1); + $fileInfoEncrypted2 = $this->view->getFileInfo($this->userId . '/files/' . $file2); + + $this->assertTrue($fileInfoEncrypted1 instanceof \OC\Files\FileInfo); + $this->assertTrue($fileInfoEncrypted2 instanceof \OC\Files\FileInfo); + $this->assertEquals($fileInfoEncrypted1['encrypted'], 1); + $this->assertEquals($fileInfoEncrypted2['encrypted'], 1); + + // rename keyfile for file1 so that the decryption for file1 fails + // Expected behaviour: decryptAll() returns false, file2 gets decrypted anyway + $this->view->rename($this->userId . '/files_encryption/keyfiles/' . $file1 . '.key', + $this->userId . '/files_encryption/keyfiles/' . $file1 . '.key.moved'); + + // decrypt all encrypted files + $result = $util->decryptAll('/' . $this->userId . '/' . 'files'); + + $this->assertFalse($result); + + $fileInfoUnencrypted1 = $this->view->getFileInfo($this->userId . '/files/' . $file1); + $fileInfoUnencrypted2 = $this->view->getFileInfo($this->userId . '/files/' . $file2); + + $this->assertTrue($fileInfoUnencrypted1 instanceof \OC\Files\FileInfo); + $this->assertTrue($fileInfoUnencrypted2 instanceof \OC\Files\FileInfo); + + // file1 should be still encrypted; file2 should be decrypted + $this->assertEquals(1, $fileInfoUnencrypted1['encrypted']); + $this->assertEquals(0, $fileInfoUnencrypted2['encrypted']); + + // keyfiles and share keys should still exist + $this->assertTrue($this->view->is_dir($this->userId . '/files_encryption/keyfiles/')); + $this->assertTrue($this->view->is_dir($this->userId . '/files_encryption/share-keys/')); + + // rename the keyfile for file1 back + $this->view->rename($this->userId . '/files_encryption/keyfiles/' . $file1 . '.key.moved', + $this->userId . '/files_encryption/keyfiles/' . $file1 . '.key'); + + // try again to decrypt all encrypted files + $result = $util->decryptAll('/' . $this->userId . '/' . 'files'); + + $this->assertTrue($result); + + $fileInfoUnencrypted1 = $this->view->getFileInfo($this->userId . '/files/' . $file1); + $fileInfoUnencrypted2 = $this->view->getFileInfo($this->userId . '/files/' . $file2); + + $this->assertTrue($fileInfoUnencrypted1 instanceof \OC\Files\FileInfo); + $this->assertTrue($fileInfoUnencrypted2 instanceof \OC\Files\FileInfo); + + // now both files should be decrypted + $this->assertEquals(0, $fileInfoUnencrypted1['encrypted']); + $this->assertEquals(0, $fileInfoUnencrypted2['encrypted']); + + // keyfiles and share keys should be deleted + $this->assertFalse($this->view->is_dir($this->userId . '/files_encryption/keyfiles/')); + $this->assertFalse($this->view->is_dir($this->userId . '/files_encryption/share-keys/')); + + $this->view->unlink($this->userId . '/files/' . $file1); + $this->view->unlink($this->userId . '/files/' . $file2); + + } + /** * @large */ diff --git a/settings/ajax/decryptall.php b/settings/ajax/decryptall.php index ebc17aacfa..d7c104ab15 100644 --- a/settings/ajax/decryptall.php +++ b/settings/ajax/decryptall.php @@ -16,7 +16,14 @@ $util = new \OCA\Encryption\Util($view, \OCP\User::getUser()); $result = $util->initEncryption($params); if ($result !== false) { - $successful = $util->decryptAll(); + + try { + $successful = $util->decryptAll(); + } catch (\Exception $ex) { + \OCP\Util::writeLog('encryption library', "Decryption finished unexpected: " . $ex->getMessage(), \OCP\Util::ERROR); + $successful = false; + } + if ($successful === true) { \OCP\JSON::success(array('data' => array('message' => 'Files decrypted successfully'))); } else { From 6778dc5a4a92ba4e808c072a9eae48ff91b6ec74 Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Tue, 11 Feb 2014 12:44:06 +0100 Subject: [PATCH 49/49] don't block login forever if we are stuck in the middle of the initial encryption --- apps/files_encryption/ajax/getMigrationStatus.php | 8 +++----- apps/files_encryption/appinfo/app.php | 1 + apps/files_encryption/hooks/hooks.php | 7 +++---- apps/files_encryption/js/detect-migration.js | 6 +++++- apps/files_encryption/js/encryption.js | 12 ++++++++++++ 5 files changed, 24 insertions(+), 10 deletions(-) create mode 100644 apps/files_encryption/js/encryption.js diff --git a/apps/files_encryption/ajax/getMigrationStatus.php b/apps/files_encryption/ajax/getMigrationStatus.php index 17469a1af0..7c9e0dcc51 100644 --- a/apps/files_encryption/ajax/getMigrationStatus.php +++ b/apps/files_encryption/ajax/getMigrationStatus.php @@ -13,16 +13,14 @@ use OCA\Encryption\Util; $loginname = isset($_POST['user']) ? $_POST['user'] : ''; $password = isset($_POST['password']) ? $_POST['password'] : ''; -$migrationCompleted = true; +$migrationStatus = Util::MIGRATION_COMPLETED; if ($loginname !== '' && $password !== '') { $username = \OCP\User::checkPassword($loginname, $password); if ($username) { $util = new Util(new \OC_FilesystemView('/'), $username); - if ($util->getMigrationStatus() !== Util::MIGRATION_COMPLETED) { - $migrationCompleted = false; - } + $migrationStatus = $util->getMigrationStatus(); } } -\OCP\JSON::success(array('data' => array('migrationCompleted' => $migrationCompleted))); +\OCP\JSON::success(array('data' => array('migrationStatus' => $migrationStatus))); diff --git a/apps/files_encryption/appinfo/app.php b/apps/files_encryption/appinfo/app.php index fd9aa429b0..21de421c19 100644 --- a/apps/files_encryption/appinfo/app.php +++ b/apps/files_encryption/appinfo/app.php @@ -10,6 +10,7 @@ OC::$CLASSPATH['OCA\Encryption\Session'] = 'files_encryption/lib/session.php'; OC::$CLASSPATH['OCA\Encryption\Capabilities'] = 'files_encryption/lib/capabilities.php'; OC::$CLASSPATH['OCA\Encryption\Helper'] = 'files_encryption/lib/helper.php'; +\OCP\Util::addscript('files_encryption', 'encryption'); \OCP\Util::addscript('files_encryption', 'detect-migration'); if (!OC_Config::getValue('maintenance', false)) { diff --git a/apps/files_encryption/hooks/hooks.php b/apps/files_encryption/hooks/hooks.php index 1eb5f4c41e..50f322ddef 100644 --- a/apps/files_encryption/hooks/hooks.php +++ b/apps/files_encryption/hooks/hooks.php @@ -85,10 +85,9 @@ class Hooks { $ready = $util->beginMigration(); } elseif ($migrationStatus === Util::MIGRATION_IN_PROGRESS) { // refuse login as long as the initial encryption is running - while ($migrationStatus === Util::MIGRATION_IN_PROGRESS) { - sleep(60); - $migrationStatus = $util->getMigrationStatus(); - } + sleep(5); + \OCP\User::logout(); + return false; } // If migration not yet done diff --git a/apps/files_encryption/js/detect-migration.js b/apps/files_encryption/js/detect-migration.js index 301e77f24f..f5627edf4e 100644 --- a/apps/files_encryption/js/detect-migration.js +++ b/apps/files_encryption/js/detect-migration.js @@ -17,10 +17,14 @@ $(document).ready(function(){ data: {user: user, password: password}, async: false, success: function(response) { - if (response.data.migrationCompleted === false) { + if (response.data.migrationStatus === OC.Encryption.MIGRATION_OPEN) { var message = t('files_encryption', 'Initial encryption started... This can take some time. Please wait.'); $('#messageText').text(message); $('#message').removeClass('hidden').addClass('update'); + } else if (response.data.migrationStatus === OC.Encryption.MIGRATION_IN_PROGRESS) { + var message = t('files_encryption', 'Initial encryption running... Please try again later.'); + $('#messageText').text(message); + $('#message').removeClass('hidden').addClass('update'); } } }); diff --git a/apps/files_encryption/js/encryption.js b/apps/files_encryption/js/encryption.js new file mode 100644 index 0000000000..65ffabe55e --- /dev/null +++ b/apps/files_encryption/js/encryption.js @@ -0,0 +1,12 @@ +/** + * Copyright (c) 2014 + * Bjoern Schiessle + * This file is licensed under the Affero General Public License version 3 or later. + * See the COPYING-README file. + */ + +OC.Encryption={ + MIGRATION_OPEN:0, + MIGRATION_COMPLETED:1, + MIGRATION_IN_PROGRESS:-1, +};