From faf0bfb29b6c676d8b1334effba2df01f403c32c Mon Sep 17 00:00:00 2001 From: Remco Brenninkmeijer Date: Tue, 8 Jul 2014 19:52:28 +0200 Subject: Backport of sorting fix from master Changed default sorting except for names. Show sorting icons when hovering over Cleanup of unnecesary addition Fixed comments from PVince81 Corrected (Netbeans?) inserted spaces While busy cleaning, also removed extra enters Adjusted tests for new default sorting Sorting triangles pointing up for ascending, down for descending Backport + squash of 1a65d09..319caa7 from master --- apps/files/css/files.css | 14 +++++----- apps/files/js/filelist.js | 22 +++++++++++++--- apps/files/tests/js/filelistSpec.js | 51 +++++++++++++++++++++++-------------- 3 files changed, 58 insertions(+), 29 deletions(-) diff --git a/apps/files/css/files.css b/apps/files/css/files.css index 287dedc23f2..4a8bd5bb30f 100644 --- a/apps/files/css/files.css +++ b/apps/files/css/files.css @@ -152,16 +152,20 @@ table th .columntitle.name { padding-right: 80px; margin-left: 50px; } -/* hover effect on sortable column */ -table th a.columntitle:hover { - color: #000; -} + +.sort-indicator.hidden { visibility: hidden; } table th .sort-indicator { width: 10px; height: 8px; margin-left: 10px; display: inline-block; } +table th:hover .sort-indicator.hidden { + width: 10px; + height: 8px; + margin-left: 10px; + visibility: visible; +} table th, table td { border-bottom:1px solid #ddd; text-align:left; font-weight:normal; } table td { padding: 0 15px; @@ -367,7 +371,6 @@ table td.filename .uploadtext { left: 18px; } - #fileList tr td.filename { position: relative; width: 100%; @@ -432,7 +435,6 @@ a.action>img { margin-bottom: -1px; } - #fileList a.action { display: inline; padding: 18px 8px; diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 61e73b7bebc..4ff7d0c3fa0 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -18,8 +18,8 @@ this.initialize($el, options); }; FileList.prototype = { - SORT_INDICATOR_ASC_CLASS: 'icon-triangle-s', - SORT_INDICATOR_DESC_CLASS: 'icon-triangle-n', + SORT_INDICATOR_ASC_CLASS: 'icon-triangle-n', + SORT_INDICATOR_DESC_CLASS: 'icon-triangle-s', id: 'files', appName: t('files', 'Files'), @@ -371,7 +371,12 @@ this.setSort(sort, (this._sortDirection === 'desc')?'asc':'desc'); } else { - this.setSort(sort, 'asc'); + if ( sort === 'name' ) { //default sorting of name is opposite to size and mtime + this.setSort(sort, 'asc'); + } + else { + this.setSort(sort, 'desc'); + } } this.reload(); } @@ -914,16 +919,25 @@ this._sort = sort; this._sortDirection = (direction === 'desc')?'desc':'asc'; this._sortComparator = comparator; + if (direction === 'desc') { this._sortComparator = function(fileInfo1, fileInfo2) { return -comparator(fileInfo1, fileInfo2); }; } this.$el.find('thead th .sort-indicator') - .removeClass(this.SORT_INDICATOR_ASC_CLASS + ' ' + this.SORT_INDICATOR_DESC_CLASS); + .removeClass(this.SORT_INDICATOR_ASC_CLASS) + .removeClass(this.SORT_INDICATOR_DESC_CLASS) + .toggleClass('hidden', true) + .addClass(this.SORT_INDICATOR_DESC_CLASS); + this.$el.find('thead th.column-' + sort + ' .sort-indicator') + .removeClass(this.SORT_INDICATOR_ASC_CLASS) + .removeClass(this.SORT_INDICATOR_DESC_CLASS) + .toggleClass('hidden', false) .addClass(direction === 'desc' ? this.SORT_INDICATOR_DESC_CLASS : this.SORT_INDICATOR_ASC_CLASS); }, + /** * Reloads the file list using ajax call * diff --git a/apps/files/tests/js/filelistSpec.js b/apps/files/tests/js/filelistSpec.js index ae22ae0123e..0580177c5ff 100644 --- a/apps/files/tests/js/filelistSpec.js +++ b/apps/files/tests/js/filelistSpec.js @@ -1696,7 +1696,7 @@ describe('OCA.Files.FileList tests', function() { var url = fakeServer.requests[0].url; var query = OC.parseQueryString(url.substr(url.indexOf('?') + 1)); expect(query.sort).toEqual('size'); - expect(query.sortdirection).toEqual('asc'); + expect(query.sortdirection).toEqual('desc'); }); it('Toggles sort direction when clicking on already sorted column', function() { fileList.$el.find('.column-name .columntitle').click(); @@ -1710,37 +1710,51 @@ describe('OCA.Files.FileList tests', function() { var ASC_CLASS = fileList.SORT_INDICATOR_ASC_CLASS; var DESC_CLASS = fileList.SORT_INDICATOR_DESC_CLASS; fileList.$el.find('.column-size .columntitle').click(); - // moves triangle to size column + // moves triangle to size column, check indicator on name is hidden expect( - fileList.$el.find('.column-name .sort-indicator').hasClass(ASC_CLASS + ' ' + DESC_CLASS) + fileList.$el.find('.column-name .sort-indicator').hasClass('hidden') + ).toEqual(true); + // check indicator on size is visible and defaults to descending + expect( + fileList.$el.find('.column-size .sort-indicator').hasClass('hidden') ).toEqual(false); expect( - fileList.$el.find('.column-size .sort-indicator').hasClass(ASC_CLASS) + fileList.$el.find('.column-size .sort-indicator').hasClass(DESC_CLASS) ).toEqual(true); // click again on size column, reverses direction fileList.$el.find('.column-size .columntitle').click(); expect( - fileList.$el.find('.column-size .sort-indicator').hasClass(DESC_CLASS) + fileList.$el.find('.column-size .sort-indicator').hasClass('hidden') + ).toEqual(false); + expect( + fileList.$el.find('.column-size .sort-indicator').hasClass(ASC_CLASS) ).toEqual(true); // click again on size column, reverses direction fileList.$el.find('.column-size .columntitle').click(); expect( - fileList.$el.find('.column-size .sort-indicator').hasClass(ASC_CLASS) + fileList.$el.find('.column-size .sort-indicator').hasClass('hidden') + ).toEqual(false); + expect( + fileList.$el.find('.column-size .sort-indicator').hasClass(DESC_CLASS) ).toEqual(true); // click on mtime column, moves indicator there fileList.$el.find('.column-mtime .columntitle').click(); expect( - fileList.$el.find('.column-size .sort-indicator').hasClass(ASC_CLASS + ' ' + DESC_CLASS) + fileList.$el.find('.column-size .sort-indicator').hasClass('hidden') + ).toEqual(true); + expect( + fileList.$el.find('.column-mtime .sort-indicator').hasClass('hidden') ).toEqual(false); expect( - fileList.$el.find('.column-mtime .sort-indicator').hasClass(ASC_CLASS) + fileList.$el.find('.column-mtime .sort-indicator').hasClass(DESC_CLASS) ).toEqual(true); }); it('Uses correct sort comparator when inserting files', function() { testFiles.sort(OCA.Files.FileList.Comparators.size); + testFiles.reverse(); //default is descending // this will make it reload the testFiles with the correct sorting fileList.$el.find('.column-size .columntitle').click(); expect(fakeServer.requests.length).toEqual(1); @@ -1764,17 +1778,16 @@ describe('OCA.Files.FileList tests', function() { etag: '999' }; fileList.add(newFileData); + expect(fileList.findFileEl('Three.pdf').index()).toEqual(0); + expect(fileList.findFileEl('new file.txt').index()).toEqual(1); + expect(fileList.findFileEl('Two.jpg').index()).toEqual(2); + expect(fileList.findFileEl('somedir').index()).toEqual(3); + expect(fileList.findFileEl('One.txt').index()).toEqual(4); expect(fileList.files.length).toEqual(5); expect(fileList.$fileList.find('tr').length).toEqual(5); - expect(fileList.findFileEl('One.txt').index()).toEqual(0); - expect(fileList.findFileEl('somedir').index()).toEqual(1); - expect(fileList.findFileEl('Two.jpg').index()).toEqual(2); - expect(fileList.findFileEl('new file.txt').index()).toEqual(3); - expect(fileList.findFileEl('Three.pdf').index()).toEqual(4); }); it('Uses correct reversed sort comparator when inserting files', function() { testFiles.sort(OCA.Files.FileList.Comparators.size); - testFiles.reverse(); // this will make it reload the testFiles with the correct sorting fileList.$el.find('.column-size .columntitle').click(); expect(fakeServer.requests.length).toEqual(1); @@ -1811,13 +1824,13 @@ describe('OCA.Files.FileList tests', function() { etag: '999' }; fileList.add(newFileData); + expect(fileList.findFileEl('One.txt').index()).toEqual(0); + expect(fileList.findFileEl('somedir').index()).toEqual(1); + expect(fileList.findFileEl('Two.jpg').index()).toEqual(2); + expect(fileList.findFileEl('new file.txt').index()).toEqual(3); + expect(fileList.findFileEl('Three.pdf').index()).toEqual(4); expect(fileList.files.length).toEqual(5); expect(fileList.$fileList.find('tr').length).toEqual(5); - expect(fileList.findFileEl('One.txt').index()).toEqual(4); - expect(fileList.findFileEl('somedir').index()).toEqual(3); - expect(fileList.findFileEl('Two.jpg').index()).toEqual(2); - expect(fileList.findFileEl('new file.txt').index()).toEqual(1); - expect(fileList.findFileEl('Three.pdf').index()).toEqual(0); }); }); /** -- cgit v1.2.3 From 3c0f5d02bac478f028b74f4b488a3aa00b00feff Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Wed, 23 Jul 2014 13:45:56 +0200 Subject: Fix remote share when remote server is installed at the root --- apps/files_sharing/lib/external/storage.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/apps/files_sharing/lib/external/storage.php b/apps/files_sharing/lib/external/storage.php index 3a0de51192e..855be2872b5 100644 --- a/apps/files_sharing/lib/external/storage.php +++ b/apps/files_sharing/lib/external/storage.php @@ -49,7 +49,12 @@ class Storage extends DAV implements ISharedStorage { $this->remote = $options['remote']; $this->remoteUser = $options['owner']; list($protocol, $remote) = explode('://', $this->remote); - list($host, $root) = explode('/', $remote, 2); + if (strpos($remote, '/')) { + list($host, $root) = explode('/', $remote, 2); + } else { + $host = $remote; + $root = ''; + } $secure = $protocol === 'https'; $root = rtrim($root, '/') . '/public.php/webdav'; $this->mountPoint = $options['mountpoint']; @@ -148,7 +153,7 @@ class Storage extends DAV implements ISharedStorage { // ownCloud instance is gone, likely to be a temporary server configuration error throw $e; } - } catch(\Exception $shareException) { + } catch (\Exception $shareException) { // todo, maybe handle 403 better and ask the user for a new password throw $e; } -- cgit v1.2.3 From 303e504fcbd9b4b017a0774a62a0e9b65b24b6c8 Mon Sep 17 00:00:00 2001 From: Thomas Müller Date: Wed, 23 Jul 2014 20:13:19 +0200 Subject: only commit in case a transaction is active --- lib/private/db/migrator.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/private/db/migrator.php b/lib/private/db/migrator.php index 6443cf4ed48..d05f8455551 100644 --- a/lib/private/db/migrator.php +++ b/lib/private/db/migrator.php @@ -110,7 +110,9 @@ class Migrator { $this->dropTable($tmpName); } catch (DBALException $e) { // pgsql needs to commit it's failed transaction before doing anything else - $this->connection->commit(); + if ($this->connection->isTransactionActive()) { + $this->connection->commit(); + } $this->dropTable($tmpName); throw new MigrationException($table->getName(), $e->getMessage()); } -- cgit v1.2.3 From 127aa309fb1e9139b98cbdd19e641a1219936cec Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Wed, 23 Jul 2014 21:29:24 +0200 Subject: Prevent cron.php to trigger apps updating --- cron.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cron.php b/cron.php index 7f746911f42..4c86407b944 100644 --- a/cron.php +++ b/cron.php @@ -48,6 +48,11 @@ try { require_once 'lib/base.php'; + if (\OCP\Util::needUpgrade()) { + \OCP\Util::writeLog('cron', 'Update required, skipping cron', \OCP\Util::DEBUG); + exit(); + } + // load all apps to get all api routes properly setup OC_App::loadApps(); -- cgit v1.2.3 From f75f1b44123b2096b1a79be829383c61f21b4e52 Mon Sep 17 00:00:00 2001 From: Thomas Müller Date: Thu, 24 Jul 2014 12:10:05 +0200 Subject: Adding test which breaks because bit and/or enum datatypes are used --- tests/lib/db/migration.php | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 tests/lib/db/migration.php diff --git a/tests/lib/db/migration.php b/tests/lib/db/migration.php new file mode 100644 index 00000000000..820a1431f54 --- /dev/null +++ b/tests/lib/db/migration.php @@ -0,0 +1,39 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +class TestMigration extends \PHPUnit_Framework_TestCase { + + /** @var \Doctrine\DBAL\Connection */ + private $connection; + + /** @var string */ + private $tableName; + + public function setUp() { + $this->connection = \OC_DB::getConnection(); + if (!$this->connection->getDatabasePlatform() instanceof \Doctrine\DBAL\Platforms\MySqlPlatform) { + $this->markTestSkipped("Test only relevant on MySql"); + } + + $dbPrefix = \OC::$server->getConfig()->getSystemValue("dbtableprefix"); + $this->tableName = uniqid($dbPrefix . "_enum_bit_test"); + $this->connection->exec("CREATE TABLE $this->tableName(b BIT, e ENUM('1','2','3','4'))"); + } + + public function tearDown() { + $this->connection->getSchemaManager()->dropTable($this->tableName); + } + + public function testNonOCTables() { + $manager = new \OC\DB\MDB2SchemaManager($this->connection); + $manager->updateDbFromStructure(__DIR__ . '/testschema.xml'); + + $this->assertTrue(true); + } + +} -- cgit v1.2.3 From c152ab4572e5a71ecc16431daa711fb32eb47ad3 Mon Sep 17 00:00:00 2001 From: Thomas Müller Date: Thu, 24 Jul 2014 12:17:26 +0200 Subject: register type mappings for unknown/unsupported mysql types --- lib/private/db/mysqlmigrator.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/private/db/mysqlmigrator.php b/lib/private/db/mysqlmigrator.php index 97495f52032..c0adcdf5df3 100644 --- a/lib/private/db/mysqlmigrator.php +++ b/lib/private/db/mysqlmigrator.php @@ -17,6 +17,10 @@ class MySQLMigrator extends Migrator { * @return \Doctrine\DBAL\Schema\SchemaDiff */ protected function getDiff(Schema $targetSchema, \Doctrine\DBAL\Connection $connection) { + $platform = $connection->getDatabasePlatform(); + $platform->registerDoctrineTypeMapping('enum', 'string'); + $platform->registerDoctrineTypeMapping('bit', 'string'); + $schemaDiff = parent::getDiff($targetSchema, $connection); // identifiers need to be quoted for mysql -- cgit v1.2.3 From f33c49e2be7be32af7090da77101b35743e87ee0 Mon Sep 17 00:00:00 2001 From: Thomas Müller Date: Thu, 24 Jul 2014 12:50:39 +0200 Subject: - adding default value for $recoveryPassword - set password correctly in lost password --- core/lostpassword/controller/lostcontroller.php | 25 ++++++++++++++++++++++++- lib/private/user/user.php | 2 +- lib/public/iuser.php | 6 +++--- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/core/lostpassword/controller/lostcontroller.php b/core/lostpassword/controller/lostcontroller.php index b1be65b4f01..e4d51fde077 100644 --- a/core/lostpassword/controller/lostcontroller.php +++ b/core/lostpassword/controller/lostcontroller.php @@ -20,13 +20,36 @@ use \OC\Core\LostPassword\EncryptedDataException; class LostController extends Controller { + /** + * @var \OCP\IURLGenerator + */ protected $urlGenerator; + + /** + * @var \OCP\IUserManager + */ protected $userManager; + + /** + * @var \OC_Defaults + */ protected $defaults; + + /** + * @var IL10N + */ protected $l10n; protected $from; protected $isDataEncrypted; + + /** + * @var IConfig + */ protected $config; + + /** + * @var IUserSession + */ protected $userSession; public function __construct($appName, @@ -110,7 +133,7 @@ class LostController extends Controller { throw new \Exception($this->l10n->t('Couldn\'t reset password because the token is invalid')); } - if (!$user->setPassword($userId, $password)) { + if (!$user->setPassword($password)) { throw new \Exception(); } diff --git a/lib/private/user/user.php b/lib/private/user/user.php index f9c2cb4d130..993fb4c0c64 100644 --- a/lib/private/user/user.php +++ b/lib/private/user/user.php @@ -156,7 +156,7 @@ class User implements IUser { * @param string $recoveryPassword for the encryption app to reset encryption keys * @return bool */ - public function setPassword($password, $recoveryPassword) { + public function setPassword($password, $recoveryPassword = null) { if ($this->emitter) { $this->emitter->emit('\OC\User', 'preSetPassword', array($this, $password, $recoveryPassword)); } diff --git a/lib/public/iuser.php b/lib/public/iuser.php index dc4acc7658f..c15edcd14dd 100644 --- a/lib/public/iuser.php +++ b/lib/public/iuser.php @@ -18,14 +18,14 @@ interface IUser { public function getUID(); /** - * get the displayname for the user, if no specific displayname is set it will fallback to the user id + * get the display name for the user, if no specific display name is set it will fallback to the user id * * @return string */ public function getDisplayName(); /** - * set the displayname for the user + * set the display name for the user * * @param string $displayName * @return bool @@ -59,7 +59,7 @@ interface IUser { * @param string $recoveryPassword for the encryption app to reset encryption keys * @return bool */ - public function setPassword($password, $recoveryPassword); + public function setPassword($password, $recoveryPassword = null); /** * get the users home folder to mount -- cgit v1.2.3 From d6e61745c88c63590fa849ce77bc42ab66c71814 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Thu, 17 Jul 2014 12:42:09 +0200 Subject: Fix preview animation on uploading When adding/uploading files, the preview is now animated. When loading a list of files directly the preview is displayed directly. --- apps/files/js/filelist.js | 6 ++++-- apps/files_sharing/js/sharedfilelist.js | 1 - 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 4ff7d0c3fa0..4fa8ca65e39 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -712,6 +712,7 @@ * @param options map of attributes: * - "updateSummary": true to update the summary after adding (default), false otherwise * - "silent": true to prevent firing events like "fileActionsReady" + * - "animate": true to animate preview loading (defaults to true here) * @return new tr element (not appended to the table) */ add: function(fileData, options) { @@ -719,7 +720,7 @@ var $tr; var $rows; var $insertionPoint; - options = options || {}; + options = _.extend({animate: true}, options || {}); // there are three situations to cover: // 1) insertion point is visible on the current page @@ -777,6 +778,7 @@ * @param options map of attributes: * - "index" optional index at which to insert the element * - "updateSummary" true to update the summary after adding (default), false otherwise + * - "animate" true to animate the preview rendering * @return new tr element (not appended to the table) */ _renderRow: function(fileData, options) { @@ -818,7 +820,7 @@ if (fileData.isPreviewAvailable) { // lazy load / newly inserted td ? - if (!fileData.icon) { + if (options.animate) { this.lazyLoadPreview({ path: path + '/' + fileData.name, mime: mime, diff --git a/apps/files_sharing/js/sharedfilelist.js b/apps/files_sharing/js/sharedfilelist.js index d5c65a6c681..c060691b8b5 100644 --- a/apps/files_sharing/js/sharedfilelist.js +++ b/apps/files_sharing/js/sharedfilelist.js @@ -162,7 +162,6 @@ else { file.type = 'file'; if (share.isPreviewAvailable) { - file.icon = true; file.isPreviewAvailable = true; } } -- cgit v1.2.3 From 06bcf3db8d5d16ff70835fef7139b9446270cb24 Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Thu, 24 Jul 2014 18:12:08 +0200 Subject: fix sharing update, add proper escaping --- apps/files_sharing/appinfo/update.php | 6 ++++-- apps/files_sharing/tests/update.php | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/files_sharing/appinfo/update.php b/apps/files_sharing/appinfo/update.php index fc547ba349d..72acdbac736 100644 --- a/apps/files_sharing/appinfo/update.php +++ b/apps/files_sharing/appinfo/update.php @@ -32,6 +32,7 @@ function updateFilePermissions($chunkSize = 99) { } } + $connection = \OC_DB::getConnection(); $chunkedPermissionList = array_chunk($updatedRows, $chunkSize, true); foreach ($chunkedPermissionList as $subList) { @@ -39,7 +40,7 @@ function updateFilePermissions($chunkSize = 99) { //update share table $ids = implode(',', array_keys($subList)); foreach ($subList as $id => $permission) { - $statement .= "WHEN " . $id . " THEN " . $permission . " "; + $statement .= "WHEN " . $connection->quote($id, \PDO::PARAM_INT) . " THEN " . $permission . " "; } $statement .= ' END WHERE `id` IN (' . $ids . ')'; @@ -95,6 +96,7 @@ function removeSharedFolder($mkdirs = true, $chunkSize = 99) { } $chunkedShareList = array_chunk($shares, $chunkSize, true); + $connection = \OC_DB::getConnection(); foreach ($chunkedShareList as $subList) { @@ -102,7 +104,7 @@ function removeSharedFolder($mkdirs = true, $chunkSize = 99) { //update share table $ids = implode(',', array_keys($subList)); foreach ($subList as $id => $target) { - $statement .= "WHEN " . $id . " THEN '/Shared" . $target . "' "; + $statement .= "WHEN " . $connection->quote($id, \PDO::PARAM_INT) . " THEN " . $connection->quote('/Shared' . $target, \PDO::PARAM_STR); } $statement .= ' END WHERE `id` IN (' . $ids . ')'; diff --git a/apps/files_sharing/tests/update.php b/apps/files_sharing/tests/update.php index 86b92b69616..d3555cc2ee3 100644 --- a/apps/files_sharing/tests/update.php +++ b/apps/files_sharing/tests/update.php @@ -176,6 +176,7 @@ class Test_Files_Sharing_Update_Routine extends Test_Files_Sharing_Base { array(\OCP\Share::SHARE_TYPE_USER, 'folder', 'user2', 'admin', '/foo2'), array(\OCP\Share::SHARE_TYPE_USER, 'file', 'user3', 'admin', '/foo3'), array(\OCP\Share::SHARE_TYPE_USER, 'folder', 'user4', 'admin', '/foo4'), + array(\OCP\Share::SHARE_TYPE_USER, 'folder', 'user4', 'admin', "/foo'4"), array(\OCP\Share::SHARE_TYPE_LINK, 'file', 'user1', 'admin', '/ShouldNotChange'), array(\OCP\Share::SHARE_TYPE_CONTACT, 'contact', 'admin', 'user1', '/ShouldNotChange'), -- cgit v1.2.3 From c72f0e692be2e1726595f3a9bde7b99aba32fece Mon Sep 17 00:00:00 2001 From: Der-Jan Date: Fri, 25 Jul 2014 16:19:35 +0200 Subject: Fixed wrong brackets in apps settings --- settings/js/apps.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings/js/apps.js b/settings/js/apps.js index 3f9a9eab17a..9061b43c7be 100644 --- a/settings/js/apps.js +++ b/settings/js/apps.js @@ -398,7 +398,7 @@ $(document).ready(function(){ if(item) { item.trigger('click'); item.addClass('active'); - $('#app-navigation').animate({scrollTop: $(item).offset().top-70}, 'slow','swing'); + $('#app-navigation').animate({scrollTop: item.offset().top-70}, 'slow','swing'); } } -- cgit v1.2.3 From 87ec3fbf1d2c3efc7427c0e178ffb09ae2aacaac Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 25 Jul 2014 17:52:50 +0200 Subject: Dont throw an error when calling $server->getUserFolder when logged out Conflicts: lib/private/server.php --- lib/private/server.php | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/private/server.php b/lib/private/server.php index da705863078..3299792e20d 100644 --- a/lib/private/server.php +++ b/lib/private/server.php @@ -255,7 +255,11 @@ class Server extends SimpleContainer implements IServerContainer { * @return \OCP\Files\Folder */ function getUserFolder() { - $dir = '/' . \OCP\User::getUser(); + $user = $this->getUserSession()->getUser(); + if (!$user) { + return null; + } + $dir = '/' . $user->getUID(); $root = $this->getRootFolder(); $folder = null; -- cgit v1.2.3 From 05301825e22f16c7553a5cd490452e35d38e6ac5 Mon Sep 17 00:00:00 2001 From: Lukas Reschke Date: Sun, 27 Jul 2014 16:46:32 +0200 Subject: Verify whether the URL is valid Required for https://github.com/owncloud/mail/pull/100#issuecomment-50266017 @karlitschek Backport for stable6 and stable7 requested. --- lib/private/util.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/private/util.php b/lib/private/util.php index eea194288f9..67da7a2f63f 100755 --- a/lib/private/util.php +++ b/lib/private/util.php @@ -1217,11 +1217,16 @@ class OC_Util { /** * @Brief Get file content via curl. * @param string $url Url to get content + * @throws Exception If the URL does not start with http:// or https:// * @return string of the response or false on error * This function get the content of a page via curl, if curl is enabled. * If not, file_get_contents is used. */ public static function getUrlContent($url) { + if (strpos($url, 'http://') !== 0 && strpos($url, 'https://') !== 0) { + throw new Exception('$url must start with https:// or http://', 1); + } + if (function_exists('curl_init')) { $curl = curl_init(); $max_redirects = 10; -- cgit v1.2.3 From 9cd741417ace87a4be7b37f57bdd5c122588505e Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Wed, 23 Jul 2014 23:38:17 +0200 Subject: Set version AFTER a successful update If an app upgrade failed, the core version will not be increased either in the database. This will re-display the update page and make it possible to redo the apps upgrade. Note that any core repair routine must take into account that an update might need to be redone again even though the core's DB state is already the one of the new version. --- lib/private/updater.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/private/updater.php b/lib/private/updater.php index d50c2554c75..7acd6446ec4 100644 --- a/lib/private/updater.php +++ b/lib/private/updater.php @@ -212,8 +212,6 @@ class Updater extends BasicEmitter { \OC_DB::updateDbFromStructure(\OC::$SERVERROOT . '/db_structure.xml'); $this->emit('\OC\Updater', 'dbUpgrade'); - // TODO: why not do this at the end ? - \OC_Config::setValue('version', implode('.', \OC_Util::getVersion())); $disabledApps = \OC_App::checkAppsRequirements(); if (!empty($disabledApps)) { $this->emit('\OC\Updater', 'disabledApps', array($disabledApps)); @@ -227,6 +225,9 @@ class Updater extends BasicEmitter { //Invalidate update feed \OC_Appconfig::setValue('core', 'lastupdatedat', 0); + + // only set the final version if everything went well + \OC_Config::setValue('version', implode('.', \OC_Util::getVersion())); } } } -- cgit v1.2.3 From 2d7379da2c172fac069fdb2828e69bfd8a4b9be7 Mon Sep 17 00:00:00 2001 From: Jan-Christoph Borchardt Date: Thu, 24 Jul 2014 14:38:19 +0200 Subject: improve look of search on mobile, save space in top bar --- core/css/icons.css | 3 +++ core/css/mobile.css | 16 +++++++++++----- core/img/actions/search-white.png | Bin 0 -> 309 bytes core/img/actions/search-white.svg | 5 +++++ 4 files changed, 19 insertions(+), 5 deletions(-) create mode 100644 core/img/actions/search-white.png create mode 100644 core/img/actions/search-white.svg diff --git a/core/css/icons.css b/core/css/icons.css index 60f0b1b8c60..534c1c66514 100644 --- a/core/css/icons.css +++ b/core/css/icons.css @@ -136,6 +136,9 @@ .icon-search { background-image: url('../img/actions/search.svg'); } +.icon-search-white { + background-image: url('../img/actions/search-white.svg'); +} .icon-settings { background-image: url('../img/actions/settings.svg'); diff --git a/core/css/mobile.css b/core/css/mobile.css index 6e2172ddbb7..21090d08cb7 100644 --- a/core/css/mobile.css +++ b/core/css/mobile.css @@ -31,16 +31,22 @@ /* compress search box on mobile, expand when focused */ .searchbox input[type="search"] { - width: 15%; - -webkit-transition: width 100ms; - -moz-transition: width 100ms; - -o-transition: width 100ms; - transition: width 100ms; + width: 0; + cursor: pointer; + background-color: transparent; + background-image: url('../img/actions/search-white.svg'); + -webkit-transition: all 100ms; + -moz-transition: all 100ms; + -o-transition: all 100ms; + transition: all 100ms; } .searchbox input[type="search"]:focus, .searchbox input[type="search"]:active { width: 155px; max-width: 50%; + cursor: text; + background-color: #fff; + background-image: url('../img/actions/search.svg'); } /* do not show display name on mobile when profile picture is present */ diff --git a/core/img/actions/search-white.png b/core/img/actions/search-white.png new file mode 100644 index 00000000000..9812c44a3d6 Binary files /dev/null and b/core/img/actions/search-white.png differ diff --git a/core/img/actions/search-white.svg b/core/img/actions/search-white.svg new file mode 100644 index 00000000000..9b312d0460d --- /dev/null +++ b/core/img/actions/search-white.svg @@ -0,0 +1,5 @@ + + + + + -- cgit v1.2.3 From df0d00c8c6a8553e245182f03999e50836d688cc Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 25 Jul 2014 18:02:02 +0200 Subject: Dont try to execute jobs that no longer exist --- lib/private/backgroundjob/joblist.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/private/backgroundjob/joblist.php b/lib/private/backgroundjob/joblist.php index 211d7e9abfc..9d15cd1663a 100644 --- a/lib/private/backgroundjob/joblist.php +++ b/lib/private/backgroundjob/joblist.php @@ -96,7 +96,10 @@ class JobList implements IJobList { $query->execute(); $jobs = array(); while ($row = $query->fetch()) { - $jobs[] = $this->buildJob($row); + $job = $this->buildJob($row); + if ($job) { + $jobs[] = $job; + } } return $jobs; } -- cgit v1.2.3 From 0cabafb513fda8bc127032a4a97100ef26001933 Mon Sep 17 00:00:00 2001 From: Morris Jobke Date: Mon, 28 Jul 2014 15:05:55 +0200 Subject: update getID3 library & add autoload --- 3rdparty | 2 +- lib/private/preview/mp3.php | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/3rdparty b/3rdparty index 6ece897f443..53284a9f7f3 160000 --- a/3rdparty +++ b/3rdparty @@ -1 +1 @@ -Subproject commit 6ece897f4435c246730db947576ad6f8a94dbdb7 +Subproject commit 53284a9f7f35f6cc7fef6c41c1d7a8cc916f28e4 diff --git a/lib/private/preview/mp3.php b/lib/private/preview/mp3.php index 21f160fd50f..bb4d3dfce86 100644 --- a/lib/private/preview/mp3.php +++ b/lib/private/preview/mp3.php @@ -14,8 +14,6 @@ class MP3 extends Provider { } public function getThumbnail($path, $maxX, $maxY, $scalingup, $fileview) { - require_once('getid3/getid3.php'); - $getID3 = new \getID3(); $tmpPath = $fileview->toTmpFile($path); -- cgit v1.2.3 From 7cd1a48222b6e9c3b42df563b24e79f5a26a683f Mon Sep 17 00:00:00 2001 From: Jan-Christoph Borchardt Date: Thu, 24 Jul 2014 14:03:40 +0200 Subject: enable input grouping also outside of log in screen --- core/css/styles.css | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/core/css/styles.css b/core/css/styles.css index 40c1622ca26..17ed3fbc74c 100644 --- a/core/css/styles.css +++ b/core/css/styles.css @@ -444,13 +444,15 @@ input[name='password-clone'] { .groupbottom { position: relative; } -#body-login .grouptop input { +#body-login .grouptop input, +.grouptop input { margin-bottom: 0; border-bottom: 0; border-bottom-left-radius: 0; border-bottom-right-radius: 0; } -#body-login .groupmiddle input { +#body-login .groupmiddle input, +.groupmiddle input { margin-top: 0; margin-bottom: 0; border-top: 0; @@ -458,7 +460,8 @@ input[name='password-clone'] { border-radius: 0; box-shadow: 0 1px 0 rgba(0,0,0,.1) inset !important; } -#body-login .groupbottom input { +#body-login .groupbottom input, +.groupbottom input { margin-top: 0; border-top: 0; border-top-right-radius: 0; -- cgit v1.2.3 From 02a61c0b6a16e956a0a8fdf5889162e7c857c169 Mon Sep 17 00:00:00 2001 From: Thomas Müller Date: Tue, 15 Jul 2014 17:13:34 +0200 Subject: ownCloud users are exported as address book --- lib/base.php | 9 +++ lib/private/contacts/localaddressbook.php | 104 ++++++++++++++++++++++++++++++ tests/lib/contacts/localadressbook.php | 95 +++++++++++++++++++++++++++ 3 files changed, 208 insertions(+) create mode 100644 lib/private/contacts/localaddressbook.php create mode 100644 tests/lib/contacts/localadressbook.php diff --git a/lib/base.php b/lib/base.php index 730cee5231d..341859dc5f3 100644 --- a/lib/base.php +++ b/lib/base.php @@ -564,6 +564,7 @@ class OC { self::registerPreviewHooks(); self::registerShareHooks(); self::registerLogRotate(); + self::registerLocalAddressBook(); //make sure temporary files are cleaned up register_shutdown_function(array('OC_Helper', 'cleanTmp')); @@ -575,6 +576,14 @@ class OC { } } + private static function registerLocalAddressBook() { + self::$server->getContactsManager()->register(function() { + $userManager = \OC::$server->getUserManager(); + \OC::$server->getContactsManager()->registerAddressBook( + new \OC\Contacts\LocalAddressBook($userManager)); + }); + } + /** * register hooks for the cache */ diff --git a/lib/private/contacts/localaddressbook.php b/lib/private/contacts/localaddressbook.php new file mode 100644 index 00000000000..483bbee83f8 --- /dev/null +++ b/lib/private/contacts/localaddressbook.php @@ -0,0 +1,104 @@ +deepdiver@owncloud.com> + * + */ + +namespace OC\Contacts; + +class LocalAddressBook implements \OCP\IAddressBook { + + /** + * @var \OCP\IUserManager + */ + private $userManager; + + /** + * @param $userManager + */ + public function __construct($userManager) { + $this->userManager = $userManager; + } + + /** + * @return string defining the technical unique key + */ + public function getKey() { + return 'local'; + } + + /** + * In comparison to getKey() this function returns a human readable (maybe translated) name + * + * @return mixed + */ + public function getDisplayName() { + return "Local users"; + } + + /** + * @param string $pattern which should match within the $searchProperties + * @param array $searchProperties defines the properties within the query pattern should match + * @param array $options - for future use. One should always have options! + * @return array an array of contacts which are arrays of key-value-pairs + */ + public function search($pattern, $searchProperties, $options) { + $users = array(); + if($pattern == '') { + // Fetch all contacts + $users = $this->userManager->search(''); + } else { + foreach($searchProperties as $property) { + $result = array(); + if($property === 'FN') { + $result = $this->userManager->searchDisplayName($pattern); + } else if ($property === 'id') { + $result = $this->userManager->search($pattern); + } + if (is_array($result)) { + $users = array_merge($users, $result); + } + } + } + + $contacts = array(); + foreach($users as $user){ + $contact = array( + "id" => $user->getUID(), + "FN" => $user->getDisplayname(), + "EMAIL" => array(), + "IMPP" => array( + "x-owncloud-handle:" . $user->getUID() + ) + ); + $contacts[] = $contact; + } + return $contacts; + } + + /** + * @param array $properties this array if key-value-pairs defines a contact + * @return array an array representing the contact just created or updated + */ + public function createOrUpdate($properties) { + return array(); + } + + /** + * @return int + */ + public function getPermissions() { + return \OCP\PERMISSION_READ; + } + + /** + * @param object $id the unique identifier to a contact + * @return bool successful or not + */ + public function delete($id) { + return false; + } +} diff --git a/tests/lib/contacts/localadressbook.php b/tests/lib/contacts/localadressbook.php new file mode 100644 index 00000000000..276863aeb6c --- /dev/null +++ b/tests/lib/contacts/localadressbook.php @@ -0,0 +1,95 @@ +. + */ + +class Test_LocalAddressBook extends PHPUnit_Framework_TestCase +{ + + public function testSearchFN() { + $stub = $this->getMockForAbstractClass('\OCP\IUserManager', array('searchDisplayName')); + + $stub->expects($this->any())->method('searchDisplayName')->will($this->return(array( + new SimpleUserForTesting('tom', 'Thomas'), + new SimpleUserForTesting('tomtom', 'Thomas T.'), + ))); + + $localAddressBook = new LocalAddressBook($stub); + + $result = $localAddressBook->search('tom', array('FN'), array()); + $this->assertEqual(2, count($result)); + } + + public function testSearchId() { + $stub = $this->getMockForAbstractClass('\OCP\IUserManager', array('searchDisplayName')); + + $stub->expects($this->any())->method('search')->will($this->return(array( + new SimpleUserForTesting('tom', 'Thomas'), + new SimpleUserForTesting('tomtom', 'Thomas T.'), + ))); + + $localAddressBook = new LocalAddressBook($stub); + + $result = $localAddressBook->search('tom', array('id'), array()); + $this->assertEqual(2, count($result)); + } +} + + +class SimpleUserForTesting implements \OCP\IUser { + + public function __construct($uid, $displayName) { + + $this->uid = $uid; + $this->displayName = $displayName; + } + + public function getUID() { + return $this->uid; + } + + public function getDisplayName() { + return $this->displayName; + } + + public function setDisplayName($displayName) { + } + + public function getLastLogin() { + } + + public function updateLastLoginTimestamp() { + } + + public function delete() { + } + + public function setPassword($password, $recoveryPassword) { + } + + public function getHome() { + } + + public function canChangeAvatar() { + } + + public function canChangePassword() { + } + + public function canChangeDisplayName() { + } + + public function isEnabled() { + } + + public function setEnabled($enabled) { + } +} -- cgit v1.2.3 From 51cbe2a0ee77529799817d5843a02bb5169751e6 Mon Sep 17 00:00:00 2001 From: Thomas Müller Date: Mon, 28 Jul 2014 10:29:39 +0200 Subject: generate copy of sqlite database file in data directory --- lib/private/db/mdb2schemamanager.php | 3 ++- lib/private/db/sqlitemigrator.php | 26 +++++++++++++++++++++++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/lib/private/db/mdb2schemamanager.php b/lib/private/db/mdb2schemamanager.php index d3e379c9417..91e590a901a 100644 --- a/lib/private/db/mdb2schemamanager.php +++ b/lib/private/db/mdb2schemamanager.php @@ -59,7 +59,8 @@ class MDB2SchemaManager { public function getMigrator() { $platform = $this->conn->getDatabasePlatform(); if ($platform instanceof SqlitePlatform) { - return new SQLiteMigrator($this->conn); + $config = \OC::$server->getConfig(); + return new SQLiteMigrator($this->conn, $config); } else if ($platform instanceof OraclePlatform) { return new OracleMigrator($this->conn); } else if ($platform instanceof MySqlPlatform) { diff --git a/lib/private/db/sqlitemigrator.php b/lib/private/db/sqlitemigrator.php index f5f78986771..81bbcb567ea 100644 --- a/lib/private/db/sqlitemigrator.php +++ b/lib/private/db/sqlitemigrator.php @@ -11,6 +11,21 @@ namespace OC\DB; use Doctrine\DBAL\DBALException; class SQLiteMigrator extends Migrator { + + /** + * @var \OCP\IConfig + */ + private $config; + + /** + * @param \Doctrine\DBAL\Connection $connection + * @param \OCP\IConfig $config + */ + public function __construct(\Doctrine\DBAL\Connection $connection, \OCP\IConfig $config) { + parent::__construct($connection); + $this->config = $config; + } + /** * @param \Doctrine\DBAL\Schema\Schema $targetSchema * @throws \OC\DB\MigrationException @@ -19,7 +34,7 @@ class SQLiteMigrator extends Migrator { */ public function checkMigrate(\Doctrine\DBAL\Schema\Schema $targetSchema) { $dbFile = $this->connection->getDatabase(); - $tmpFile = \OC_Helper::tmpFile('.db'); + $tmpFile = $this->buildTempDatabase(); copy($dbFile, $tmpFile); $connectionParams = array( @@ -37,4 +52,13 @@ class SQLiteMigrator extends Migrator { throw new MigrationException('', $e->getMessage()); } } + + /** + * @return string + */ + private function buildTempDatabase() { + $dataDir = $this->config->getSystemValue("datadirectory", \OC::$SERVERROOT . '/data'); + $tmpFile = uniqid("oc_"); + return "$dataDir/$tmpFile.db"; + } } -- cgit v1.2.3 From 2e28b7fff982aff352f82bafa1e2cf4ec23956ff Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Wed, 23 Jul 2014 16:42:33 +0200 Subject: add OCS api call to set expire date for link shares --- apps/files_sharing/lib/api.php | 27 ++++++++++++++- apps/files_sharing/tests/api.php | 74 +++++++++++++++++++++++++++++++++++++++- core/ajax/share.php | 16 +++------ lib/private/share/share.php | 59 +++++++++++++++++++++++++++++--- lib/private/util.php | 19 +++++++++-- lib/public/share.php | 5 +-- lib/public/util.php | 9 +++++ tests/lib/share/share.php | 48 ++++++++++++++++++++------ 8 files changed, 224 insertions(+), 33 deletions(-) diff --git a/apps/files_sharing/lib/api.php b/apps/files_sharing/lib/api.php index 50ba74f5beb..368e3172c6b 100644 --- a/apps/files_sharing/lib/api.php +++ b/apps/files_sharing/lib/api.php @@ -339,6 +339,8 @@ class Api { return self::updatePassword($share, $params); } elseif (isset($params['_put']['publicUpload'])) { return self::updatePublicUpload($share, $params); + } elseif (isset($params['_put']['expireDate'])) { + return self::updateExpireDate($share, $params); } } catch (\Exception $e) { @@ -420,6 +422,29 @@ class Api { } + /** + * set expire date for public link share + * @param array $share information about the share + * @param array $params contains 'expireDate' which needs to be a well formated date string, e.g DD-MM-YYYY + * @return \OC_OCS_Result + */ + private static function updateExpireDate($share, $params) { + // only public links can have a expire date + if ((int)$share['share_type'] !== \OCP\Share::SHARE_TYPE_LINK ) { + return new \OC_OCS_Result(null, 404, "expire date only exists for public link shares"); + } + + try { + $expireDateSet = \OCP\Share::setExpirationDate($share['item_type'], $share['item_source'], $params['_put']['expireDate'], (int)$share['stime']); + $result = ($expireDateSet) ? new \OC_OCS_Result() : new \OC_OCS_Result(null, 404, "couldn't set expire date"); + } catch (\Exception $e) { + $result = new \OC_OCS_Result(null, 404, $e->getMessage()); + } + + return $result; + + } + /** * update password for public link share * @param array $share information about the share @@ -555,7 +580,7 @@ class Api { * @return array with: item_source, share_type, share_with, item_type, permissions */ private static function getShareFromId($shareID) { - $sql = 'SELECT `file_source`, `item_source`, `share_type`, `share_with`, `item_type`, `permissions` FROM `*PREFIX*share` WHERE `id` = ?'; + $sql = 'SELECT `file_source`, `item_source`, `share_type`, `share_with`, `item_type`, `permissions`, `stime` FROM `*PREFIX*share` WHERE `id` = ?'; $args = array($shareID); $query = \OCP\DB::prepare($sql); $result = $query->execute($args); diff --git a/apps/files_sharing/tests/api.php b/apps/files_sharing/tests/api.php index 72dd5816ea0..49571d4c3c2 100644 --- a/apps/files_sharing/tests/api.php +++ b/apps/files_sharing/tests/api.php @@ -938,6 +938,78 @@ class Test_Files_Sharing_Api extends Test_Files_Sharing_Base { } + /** + * @medium + */ + function testUpdateShareExpireDate() { + + $fileInfo = $this->view->getFileInfo($this->folder); + + // enforce expire date, by default 7 days after the file was shared + \OCP\Config::setAppValue('core', 'shareapi_default_expire_date', 'yes'); + \OCP\Config::setAppValue('core', 'shareapi_enforce_expire_date', 'yes'); + + $dateWithinRange = new \DateTime(); + $dateWithinRange->add(new \DateInterval('P5D')); + $dateOutOfRange = new \DateTime(); + $dateOutOfRange->add(new \DateInterval('P8D')); + + $result = \OCP\Share::shareItem('folder', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_LINK, + null, 1); + + // share was successful? + $this->assertTrue(is_string($result)); + + $items = \OCP\Share::getItemShared('file', null); + + // make sure that we found a link share + $this->assertEquals(1, count($items)); + + $linkShare = reset($items); + + // update expire date to a valid value + $params = array(); + $params['id'] = $linkShare['id']; + $params['_put'] = array(); + $params['_put']['expireDate'] = $dateWithinRange->format('Y-m-d'); + + $result = Share\Api::updateShare($params); + + $this->assertTrue($result->succeeded()); + + $items = \OCP\Share::getItemShared('file', $linkShare['file_source']); + + $updatedLinkShare = reset($items); + + // date should be changed + $this->assertTrue(is_array($updatedLinkShare)); + $this->assertEquals($dateWithinRange->format('Y-m-d') . ' 00:00:00', $updatedLinkShare['expiration']); + + // update expire date to a value out of range + $params = array(); + $params['id'] = $linkShare['id']; + $params['_put'] = array(); + $params['_put']['expireDate'] = $dateOutOfRange->format('Y-m-d'); + + $result = Share\Api::updateShare($params); + + $this->assertFalse($result->succeeded()); + + $items = \OCP\Share::getItemShared('file', $linkShare['file_source']); + + $updatedLinkShare = reset($items); + + // date shouldn't be changed + $this->assertTrue(is_array($updatedLinkShare)); + $this->assertEquals($dateWithinRange->format('Y-m-d') . ' 00:00:00', $updatedLinkShare['expiration']); + + // cleanup + \OCP\Config::setAppValue('core', 'shareapi_default_expire_date', 'no'); + \OCP\Config::setAppValue('core', 'shareapi_enforce_expire_date', 'no'); + \OCP\Share::unshare('file', $fileInfo['fileid'], \OCP\Share::SHARE_TYPE_LINK, null); + + } + /** * @medium * @depends testCreateShare @@ -1158,7 +1230,7 @@ class Test_Files_Sharing_Api extends Test_Files_Sharing_Base { $result = \OCP\Share::shareItem('file', $info->getId(), \OCP\Share::SHARE_TYPE_USER, \Test_Files_Sharing_Api::TEST_FILES_SHARING_API_USER2, 31); $this->assertTrue($result); - $result = \OCP\Share::setExpirationDate('file', $info->getId() , $expireDate); + $result = \OCP\Share::setExpirationDate('file', $info->getId() , $expireDate, $now); $this->assertTrue($result); //manipulate stime so that both shares are older then the default expire date diff --git a/core/ajax/share.php b/core/ajax/share.php index be72e36541a..4b5a6cd664f 100644 --- a/core/ajax/share.php +++ b/core/ajax/share.php @@ -80,18 +80,12 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo break; case 'setExpirationDate': if (isset($_POST['date'])) { - $l = OC_L10N::get('core'); - $date = new \DateTime($_POST['date']); - $today = new \DateTime('now'); - - - - if ($date < $today) { - OC_JSON::error(array('data' => array('message' => $l->t('Expiration date is in the past.')))); - return; + try { + $return = OCP\Share::setExpirationDate($_POST['itemType'], $_POST['itemSource'], $_POST['date']); + ($return) ? OC_JSON::success() : OC_JSON::error(); + } catch (\Exception $e) { + OC_JSON::error(array('data' => array('message' => $e->getMessage()))); } - $return = OCP\Share::setExpirationDate($_POST['itemType'], $_POST['itemSource'], $_POST['date']); - ($return) ? OC_JSON::success() : OC_JSON::error(); } break; case 'informRecipients': diff --git a/lib/private/share/share.php b/lib/private/share/share.php index 673c0dc383a..af286e7154e 100644 --- a/lib/private/share/share.php +++ b/lib/private/share/share.php @@ -924,20 +924,70 @@ class Share extends \OC\Share\Constants { throw new \Exception($message_t); } + /** + * validate expire date if it meets all constraints + * + * @param string $expireDate well formate date string, e.g. "DD-MM-YYYY" + * @param string $shareTime timestamp when the file was shared + * @param string $itemType + * @param string $itemSource + * @return DateTime validated date + * @throws \Exception + */ + private static function validateExpireDate($expireDate, $shareTime, $itemType, $itemSource) { + $l = \OC_L10N::get('lib'); + $date = new \DateTime($expireDate); + $today = new \DateTime('now'); + + // if the user doesn't provide a share time we need to get it from the database + // fall-back mode to keep API stable, because the $shareTime parameter was added later + $defaultExpireDateEnforced = \OCP\Util::isDefaultExpireDateEnforced(); + if ($defaultExpireDateEnforced && $shareTime === null) { + $items = self::getItemShared($itemType, $itemSource); + $firstItem = reset($items); + $shareTime = (int)$firstItem['stime']; + } + + if ($defaultExpireDateEnforced) { + // initialize max date with share time + $maxDate = new \DateTime(); + $maxDate->setTimestamp($shareTime); + $maxDays = \OCP\Config::getAppValue('core', 'shareapi_expire_after_n_days', '7'); + $maxDate->add(new \DateInterval('P' . $maxDays . 'D')); + if ($date > $maxDate) { + $warning = 'Can not set expire date. Shares can not expire later then ' . $maxDays . ' after they where shared'; + $warning_t = $l->t('Can not set expire date. Shares can not expire later then %s after they where shared', array($maxDays)); + \OCP\Util::writeLog('OCP\Share', $warning, \OCP\Util::WARN); + throw new \Exception($warning_t); + } + } + + if ($date < $today) { + $message = 'Can not set expire date. Expire date is in the past'; + $message_t = $l->t('Can not set expire date. Expire date is in the past'); + \OCP\Util::writeLog('OCP\Share', $message, \OCP\Util::WARN); + throw new \Exception($message_t); + } + + return $date; + } + /** * Set expiration date for a share * @param string $itemType * @param string $itemSource * @param string $date expiration date + * @param int $shareTime timestamp from when the file was shared + * @throws \Exception * @return boolean */ - public static function setExpirationDate($itemType, $itemSource, $date) { + public static function setExpirationDate($itemType, $itemSource, $date, $shareTime = null) { $user = \OC_User::getUser(); if ($date == '') { $date = null; } else { - $date = new \DateTime($date); + $date = self::validateExpireDate($date, $shareTime, $itemType, $itemSource); } $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `expiration` = ? WHERE `item_type` = ? AND `item_source` = ? AND `uid_owner` = ? AND `share_type` = ?'); $query->bindValue(1, $date, 'datetime'); @@ -954,11 +1004,10 @@ class Share extends \OC\Share\Constants { 'date' => $date, 'uidOwner' => $user )); - + return true; + } - } - /** * Checks whether a share has expired, calls unshareItem() if yes. * @param array $item Share data (usually database row) diff --git a/lib/private/util.php b/lib/private/util.php index 67da7a2f63f..896b076afa6 100755 --- a/lib/private/util.php +++ b/lib/private/util.php @@ -22,7 +22,7 @@ class OC_Util { self::$rootMounted = true; } } - + /** * mounting an object storage as the root fs will in essence remove the * necessity of a data folder being present. @@ -50,7 +50,7 @@ class OC_Util { self::$rootMounted = true; } } - + /** * Can be set up * @param string $user @@ -170,6 +170,21 @@ class OC_Util { return false; } + /** + * check if share API enforces a default expire date + * @return boolean + */ + public static function isDefaultExpireDateEnforced() { + $isDefaultExpireDateEnabled = \OCP\Config::getAppValue('core', 'shareapi_default_expire_date', 'no'); + $enforceDefaultExpireDate = false; + if ($isDefaultExpireDateEnabled === 'yes') { + $value = \OCP\Config::getAppValue('core', 'shareapi_enforce_expire_date', 'no'); + $enforceDefaultExpireDate = ($value === 'yes') ? true : false; + } + + return $enforceDefaultExpireDate; + } + /** * Get the quota of a user * @param string $user diff --git a/lib/public/share.php b/lib/public/share.php index 8566a38c61e..c0939dce53f 100644 --- a/lib/public/share.php +++ b/lib/public/share.php @@ -298,10 +298,11 @@ class Share extends \OC\Share\Constants { * @param string $itemType * @param string $itemSource * @param string $date expiration date + * @param int $shareTime timestamp from when the file was shared * @return boolean */ - public static function setExpirationDate($itemType, $itemSource, $date) { - return \OC\Share\Share::setExpirationDate($itemType, $itemSource, $date); + public static function setExpirationDate($itemType, $itemSource, $date, $shareTime = null) { + return \OC\Share\Share::setExpirationDate($itemType, $itemSource, $date, $shareTime); } /** diff --git a/lib/public/util.php b/lib/public/util.php index 8f4691eeade..2a6e977f085 100644 --- a/lib/public/util.php +++ b/lib/public/util.php @@ -517,6 +517,15 @@ class Util { return \OC_Util::isPublicLinkPasswordRequired(); } + /** + * check if share API enforces a default expire date + * @return boolean + */ + public static function isDefaultExpireDateEnforced() { + return \OC_Util::isDefaultExpireDateEnforced(); + } + + /** * Checks whether the current version needs upgrade. * diff --git a/tests/lib/share/share.php b/tests/lib/share/share.php index 5920b44a8e0..0e3dfe8291d 100644 --- a/tests/lib/share/share.php +++ b/tests/lib/share/share.php @@ -326,10 +326,14 @@ class Test_Share extends PHPUnit_Framework_TestCase { $this->shareUserOneTestFileWithUserTwo(); $this->shareUserTestFileAsLink(); - $this->assertTrue( - OCP\Share::setExpirationDate('test', 'test.txt', $this->dateInPast), - 'Failed asserting that user 1 successfully set an expiration date for the test.txt share.' - ); + // manipulate share table and set expire date to the past + $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `expiration` = ? WHERE `item_type` = ? AND `item_source` = ? AND `uid_owner` = ? AND `share_type` = ?'); + $query->bindValue(1, new \DateTime($this->dateInPast), 'datetime'); + $query->bindValue(2, 'test'); + $query->bindValue(3, 'test.txt'); + $query->bindValue(4, $this->user1); + $query->bindValue(5, \OCP\Share::SHARE_TYPE_LINK); + $query->execute(); $shares = OCP\Share::getItemsShared('test'); $this->assertSame(1, count($shares)); @@ -337,6 +341,24 @@ class Test_Share extends PHPUnit_Framework_TestCase { $this->assertSame(\OCP\Share::SHARE_TYPE_USER, $share['share_type']); } + public function testSetExpireDateInPast() { + OC_User::setUserId($this->user1); + $this->shareUserOneTestFileWithUserTwo(); + $this->shareUserTestFileAsLink(); + + $setExpireDateFailed = false; + try { + $this->assertTrue( + OCP\Share::setExpirationDate('test', 'test.txt', $this->dateInPast, ''), + 'Failed asserting that user 1 successfully set an expiration date for the test.txt share.' + ); + } catch (\Exception $e) { + $setExpireDateFailed = true; + } + + $this->assertTrue($setExpireDateFailed); + } + public function testShareWithUserExpirationValid() { OC_User::setUserId($this->user1); $this->shareUserOneTestFileWithUserTwo(); @@ -344,7 +366,7 @@ class Test_Share extends PHPUnit_Framework_TestCase { $this->assertTrue( - OCP\Share::setExpirationDate('test', 'test.txt', $this->dateInFuture), + OCP\Share::setExpirationDate('test', 'test.txt', $this->dateInFuture, ''), 'Failed asserting that user 1 successfully set an expiration date for the test.txt share.' ); @@ -552,7 +574,7 @@ class Test_Share extends PHPUnit_Framework_TestCase { // testGetShareByTokenExpirationValid $this->assertTrue( - OCP\Share::setExpirationDate('test', 'test.txt', $this->dateInFuture), + OCP\Share::setExpirationDate('test', 'test.txt', $this->dateInFuture, ''), 'Failed asserting that user 1 successfully set a future expiration date for the test.txt share.' ); $row = $this->getShareByValidToken($token); @@ -561,11 +583,15 @@ class Test_Share extends PHPUnit_Framework_TestCase { 'Failed asserting that the returned row has an expiration date.' ); - // testGetShareByTokenExpirationExpired - $this->assertTrue( - OCP\Share::setExpirationDate('test', 'test.txt', $this->dateInPast), - 'Failed asserting that user 1 successfully set a past expiration date for the test.txt share.' - ); + // manipulate share table and set expire date to the past + $query = \OC_DB::prepare('UPDATE `*PREFIX*share` SET `expiration` = ? WHERE `item_type` = ? AND `item_source` = ? AND `uid_owner` = ? AND `share_type` = ?'); + $query->bindValue(1, new \DateTime($this->dateInPast), 'datetime'); + $query->bindValue(2, 'test'); + $query->bindValue(3, 'test.txt'); + $query->bindValue(4, $this->user1); + $query->bindValue(5, \OCP\Share::SHARE_TYPE_LINK); + $query->execute(); + $this->assertFalse( OCP\Share::getShareByToken($token), 'Failed asserting that an expired share could not be found.' -- cgit v1.2.3 From 663e8bc5e2f1d7e788fc308e042e3575493c5fc8 Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Mon, 28 Jul 2014 12:39:22 +0200 Subject: adjust error code --- apps/files_sharing/lib/api.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/files_sharing/lib/api.php b/apps/files_sharing/lib/api.php index 368e3172c6b..faf141db25f 100644 --- a/apps/files_sharing/lib/api.php +++ b/apps/files_sharing/lib/api.php @@ -411,7 +411,7 @@ class Api { if ($share['item_type'] !== 'folder' || (int)$share['share_type'] !== \OCP\Share::SHARE_TYPE_LINK ) { - return new \OC_OCS_Result(null, 404, "public upload is only possible for public shared folders"); + return new \OC_OCS_Result(null, 400, "public upload is only possible for public shared folders"); } // read, create, update (7) if public upload is enabled or @@ -431,7 +431,7 @@ class Api { private static function updateExpireDate($share, $params) { // only public links can have a expire date if ((int)$share['share_type'] !== \OCP\Share::SHARE_TYPE_LINK ) { - return new \OC_OCS_Result(null, 404, "expire date only exists for public link shares"); + return new \OC_OCS_Result(null, 400, "expire date only exists for public link shares"); } try { -- cgit v1.2.3 From 8757a99f789614a9d05ec98a0e347f2f056b0710 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Mon, 14 Jul 2014 16:05:46 +0200 Subject: Fix enforced share expiration date to be based on share time --- core/js/js.js | 12 ++++ core/js/share.js | 28 ++++++-- core/js/tests/specs/coreSpec.js | 6 ++ core/js/tests/specs/shareSpec.js | 139 ++++++++++++++++++++++++++++++++++++--- 4 files changed, 170 insertions(+), 15 deletions(-) diff --git a/core/js/js.js b/core/js/js.js index 4a9a5ce82ff..5c9d7bf0141 100644 --- a/core/js/js.js +++ b/core/js/js.js @@ -1353,6 +1353,18 @@ OC.Util = { } }); }); + }, + + /** + * Remove the time component from a given date + * + * @param {Date} date date + * @return {Date} date with stripped time + */ + stripTime: function(date) { + // FIXME: likely to break when crossing DST + // would be better to use a library like momentJS + return new Date(date.getFullYear(), date.getMonth(), date.getDate()); } }; diff --git a/core/js/share.js b/core/js/share.js index e8d486055b0..14abdf18ade 100644 --- a/core/js/share.js +++ b/core/js/share.js @@ -436,7 +436,7 @@ OC.Share={ } } if (share.expiration != null) { - OC.Share.showExpirationDate(share.expiration); + OC.Share.showExpirationDate(share.expiration, share.stime); } }); } @@ -716,7 +716,24 @@ OC.Share={ dirname:function(path) { return path.replace(/\\/g,'/').replace(/\/[^\/]*$/, ''); }, - showExpirationDate:function(date) { + /** + * Displays the expiration date field + * + * @param {Date} date current expiration date + * @param {int} [shareTime] share timestamp in seconds, defaults to now + */ + showExpirationDate:function(date, shareTime) { + var now = new Date(); + var datePickerOptions = { + minDate: now, + maxDate: null + }; + if (_.isNumber(shareTime)) { + shareTime = new Date(shareTime * 1000); + } + if (!shareTime) { + shareTime = now; + } $('#expirationCheckbox').attr('checked', true); $('#expirationDate').val(date); $('#expirationDate').show('blind'); @@ -726,13 +743,14 @@ OC.Share={ }); if (oc_appconfig.core.defaultExpireDateEnforced) { $('#expirationCheckbox').attr('disabled', true); - $.datepicker.setDefaults({ - maxDate : new Date(date.replace(' 00:00:00', '')) - }); + shareTime = OC.Util.stripTime(shareTime).getTime(); + // max date is share date + X days + datePickerOptions.maxDate = new Date(shareTime + oc_appconfig.core.defaultExpireDate * 24 * 3600 * 1000); } if(oc_appconfig.core.defaultExpireDateEnabled) { $('#defaultExpireMessage').show('blind'); } + $.datepicker.setDefaults(datePickerOptions); } }; diff --git a/core/js/tests/specs/coreSpec.js b/core/js/tests/specs/coreSpec.js index dd9d4a79277..166210d0312 100644 --- a/core/js/tests/specs/coreSpec.js +++ b/core/js/tests/specs/coreSpec.js @@ -466,6 +466,12 @@ describe('Core base tests', function() { } }); }); + describe('stripTime', function() { + it('strips time from dates', function() { + expect(OC.Util.stripTime(new Date(2014, 2, 24, 15, 4, 45, 24))) + .toEqual(new Date(2014, 2, 24, 0, 0, 0, 0)); + }); + }); }); }); diff --git a/core/js/tests/specs/shareSpec.js b/core/js/tests/specs/shareSpec.js index 00a88ba36ef..32fecf82b65 100644 --- a/core/js/tests/specs/shareSpec.js +++ b/core/js/tests/specs/shareSpec.js @@ -83,16 +83,6 @@ describe('OC.Share tests', function() { expect($el.attr('data-item-source')).toEqual('123'); // TODO: expect that other parts are rendered correctly }); - it('shows default expiration date when set', function() { - oc_appconfig.core.defaultExpireDateEnabled = "yes"; - oc_appconfig.core.defaultExpireDate = ''; - // TODO: expect that default date was set - }); - it('shows default expiration date is set but disabled', function() { - oc_appconfig.core.defaultExpireDateEnabled = "no"; - oc_appconfig.core.defaultExpireDate = ''; - // TODO: expect that default date was NOT set - }); describe('Share with link', function() { // TODO: test ajax calls // TODO: test password field visibility (whenever enforced or not) @@ -265,6 +255,135 @@ describe('OC.Share tests', function() { OC.linkTo('', 'public.php')+'?service=files&t=anothertoken'; expect($('#dropdown #linkText').val()).toEqual(link); }); + describe('expiration date', function() { + var shareData; + var shareItem; + var clock; + + function showDropDown() { + OC.Share.showDropDown( + 'file', + 123, + $container, + 'http://localhost/dummylink', + 31, + 'folder' + ); + } + + beforeEach(function() { + // pick a fake date + clock = sinon.useFakeTimers(new Date(2014, 0, 20, 14, 0, 0).getTime()); + shareItem = { + displayname_owner: 'root', + expiration: null, + file_source: 123, + file_target: '/folder', + id: 20, + item_source: '123', + item_type: 'folder', + mail_send: '0', + parent: null, + path: '/folder', + permissions: OC.PERMISSION_READ, + share_type: OC.Share.SHARE_TYPE_LINK, + share_with: null, + stime: 1403884258, + storage: 1, + token: 'tehtoken', + uid_owner: 'root' + }; + shareData = { + reshare: [], + shares: [] + }; + loadItemStub.returns(shareData); + oc_appconfig.core.defaultExpireDate = 7; + oc_appconfig.core.defaultExpireDateEnabled = false; + oc_appconfig.core.defaultExpireDateEnforced = false; + }); + afterEach(function() { + clock.restore(); + }); + + it('does not check expiration date checkbox when no date was set', function() { + shareItem.expiration = null; + shareData.shares.push(shareItem); + showDropDown(); + expect($('#dropdown [name=expirationCheckbox]').prop('checked')).toEqual(false); + expect($('#dropdown #expirationDate').val()).toEqual(''); + }); + it('does not check expiration date checkbox for new share', function() { + showDropDown(); + expect($('#dropdown [name=expirationCheckbox]').prop('checked')).toEqual(false); + expect($('#dropdown #expirationDate').val()).toEqual(''); + }); + it('checks expiration date checkbox and populates field when expiration date was set', function() { + shareItem.expiration = 1234; + shareData.shares.push(shareItem); + showDropDown(); + expect($('#dropdown [name=expirationCheckbox]').prop('checked')).toEqual(true); + expect($('#dropdown #expirationDate').val()).toEqual('1234'); + }); + it('sets default date when default date setting is enabled', function() { + /* jshint camelcase:false */ + oc_appconfig.core.defaultExpireDateEnabled = true; + showDropDown(); + $('#dropdown [name=linkCheckbox]').click(); + // enabled by default + expect($('#dropdown [name=expirationCheckbox]').prop('checked')).toEqual(true); + // TODO: those zeros must go... + expect($('#dropdown #expirationDate').val()).toEqual('2014-1-27 00:00:00'); + + // disabling is allowed + $('#dropdown [name=expirationCheckbox]').click(); + expect($('#dropdown [name=expirationCheckbox]').prop('checked')).toEqual(false); + }); + it('enforces default date when enforced date setting is enabled', function() { + /* jshint camelcase:false */ + oc_appconfig.core.defaultExpireDateEnabled = true; + oc_appconfig.core.defaultExpireDateEnforced = true; + showDropDown(); + $('#dropdown [name=linkCheckbox]').click(); + expect($('#dropdown [name=expirationCheckbox]').prop('checked')).toEqual(true); + // TODO: those zeros must go... + expect($('#dropdown #expirationDate').val()).toEqual('2014-1-27 00:00:00'); + + // disabling is not allowed + expect($('#dropdown [name=expirationCheckbox]').prop('disabled')).toEqual(true); + $('#dropdown [name=expirationCheckbox]').click(); + expect($('#dropdown [name=expirationCheckbox]').prop('checked')).toEqual(true); + }); + it('sets picker minDate to today and no maxDate by default', function() { + showDropDown(); + $('#dropdown [name=linkCheckbox]').click(); + $('#dropdown [name=expirationCheckbox]').click(); + expect($.datepicker._defaults.minDate).toEqual(new Date()); + expect($.datepicker._defaults.maxDate).toEqual(null); + }); + it('limits the date range to X days after share time when enforced', function() { + /* jshint camelcase:false */ + oc_appconfig.core.defaultExpireDateEnabled = true; + oc_appconfig.core.defaultExpireDateEnforced = true; + showDropDown(); + $('#dropdown [name=linkCheckbox]').click(); + expect($.datepicker._defaults.minDate).toEqual(new Date()); + expect($.datepicker._defaults.maxDate).toEqual(new Date(2014, 0, 27, 0, 0, 0, 0)); + }); + it('limits the date range to X days after share time when enforced, even when redisplayed the next days', function() { + // item exists, was created two days ago + shareItem.expiration = '2014-1-27'; + // share time has time component but must be stripped later + shareItem.stime = new Date(2014, 0, 20, 11, 0, 25).getTime() / 1000; + shareData.shares.push(shareItem); + /* jshint camelcase:false */ + oc_appconfig.core.defaultExpireDateEnabled = true; + oc_appconfig.core.defaultExpireDateEnforced = true; + showDropDown(); + expect($.datepicker._defaults.minDate).toEqual(new Date()); + expect($.datepicker._defaults.maxDate).toEqual(new Date(2014, 0, 27, 0, 0, 0, 0)); + }); + }); }); describe('"sharesChanged" event', function() { var autocompleteOptions; -- cgit v1.2.3 From 3b760141d686e94e9347798692607fc617f8b372 Mon Sep 17 00:00:00 2001 From: Thomas Müller Date: Mon, 28 Jul 2014 09:52:29 +0200 Subject: remove MssqlBundle --- 3rdparty | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/3rdparty b/3rdparty index 53284a9f7f3..b958a1e609b 160000 --- a/3rdparty +++ b/3rdparty @@ -1 +1 @@ -Subproject commit 53284a9f7f35f6cc7fef6c41c1d7a8cc916f28e4 +Subproject commit b958a1e609b6bd09dd0fa29943b48eb024a31b75 -- cgit v1.2.3 From 53c1e3d41b80add15f9f6d5035b7315c9983ae78 Mon Sep 17 00:00:00 2001 From: Thomas Müller Date: Mon, 28 Jul 2014 22:38:56 +0200 Subject: function declaration did not match --- tests/lib/contacts/localadressbook.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/lib/contacts/localadressbook.php b/tests/lib/contacts/localadressbook.php index 276863aeb6c..3d08fc9711f 100644 --- a/tests/lib/contacts/localadressbook.php +++ b/tests/lib/contacts/localadressbook.php @@ -72,7 +72,7 @@ class SimpleUserForTesting implements \OCP\IUser { public function delete() { } - public function setPassword($password, $recoveryPassword) { + public function setPassword($password, $recoveryPassword = null) { } public function getHome() { -- cgit v1.2.3 From bfb6b7ba9083c40efdcf0bb2a659d4b2467adaa3 Mon Sep 17 00:00:00 2001 From: Thomas Müller Date: Mon, 28 Jul 2014 22:38:56 +0200 Subject: function declaration did not match --- tests/lib/contacts/localadressbook.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/lib/contacts/localadressbook.php b/tests/lib/contacts/localadressbook.php index 3d08fc9711f..bb69910820f 100644 --- a/tests/lib/contacts/localadressbook.php +++ b/tests/lib/contacts/localadressbook.php @@ -17,7 +17,7 @@ class Test_LocalAddressBook extends PHPUnit_Framework_TestCase public function testSearchFN() { $stub = $this->getMockForAbstractClass('\OCP\IUserManager', array('searchDisplayName')); - $stub->expects($this->any())->method('searchDisplayName')->will($this->return(array( + $stub->expects($this->any())->method('searchDisplayName')->will($this->returnValue(array( new SimpleUserForTesting('tom', 'Thomas'), new SimpleUserForTesting('tomtom', 'Thomas T.'), ))); @@ -25,13 +25,13 @@ class Test_LocalAddressBook extends PHPUnit_Framework_TestCase $localAddressBook = new LocalAddressBook($stub); $result = $localAddressBook->search('tom', array('FN'), array()); - $this->assertEqual(2, count($result)); + $this->assertEquals(2, count($result)); } public function testSearchId() { $stub = $this->getMockForAbstractClass('\OCP\IUserManager', array('searchDisplayName')); - $stub->expects($this->any())->method('search')->will($this->return(array( + $stub->expects($this->any())->method('search')->will($this->returnValue(array( new SimpleUserForTesting('tom', 'Thomas'), new SimpleUserForTesting('tomtom', 'Thomas T.'), ))); @@ -39,7 +39,7 @@ class Test_LocalAddressBook extends PHPUnit_Framework_TestCase $localAddressBook = new LocalAddressBook($stub); $result = $localAddressBook->search('tom', array('id'), array()); - $this->assertEqual(2, count($result)); + $this->assertEquals(2, count($result)); } } -- cgit v1.2.3 From 59aa54ddb8776ee4adc05948ca19fe6730a05e5c Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Mon, 28 Jul 2014 22:04:13 +0200 Subject: update attributes for share with user list, file should always have delete permissions, this means unshare in this context, and the overview page is always a root view --- apps/files_sharing/js/sharedfilelist.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/files_sharing/js/sharedfilelist.js b/apps/files_sharing/js/sharedfilelist.js index c060691b8b5..7a43185a2d7 100644 --- a/apps/files_sharing/js/sharedfilelist.js +++ b/apps/files_sharing/js/sharedfilelist.js @@ -59,6 +59,9 @@ $tr.attr('data-share-id', _.pluck(fileData.shares, 'id').join(',')); if (this._sharedWithUser) { $tr.attr('data-share-owner', fileData.shareOwner); + $tr.attr('data-mounttype', 'shared-root'); + var permission = parseInt($tr.attr('data-permissions')) | OC.PERMISSION_DELETE; + $tr.attr('data-permissions', permission); } return $tr; }, -- cgit v1.2.3 From b4a379b7e39a989902de204349f187f614f87ced Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Mon, 28 Jul 2014 22:35:11 +0200 Subject: remove share permission if user is excluded from sharing --- lib/private/share/share.php | 2 +- tests/lib/share/share.php | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/lib/private/share/share.php b/lib/private/share/share.php index af286e7154e..0c42853302d 100644 --- a/lib/private/share/share.php +++ b/lib/private/share/share.php @@ -1397,7 +1397,7 @@ class Share extends \OC\Share\Constants { } } // Check if resharing is allowed, if not remove share permission - if (isset($row['permissions']) && !self::isResharingAllowed()) { + if (isset($row['permissions']) && (!self::isResharingAllowed() | \OC_Util::isSharingDisabledForUser())) { $row['permissions'] &= ~\OCP\PERMISSION_SHARE; } // Add display names to result diff --git a/tests/lib/share/share.php b/tests/lib/share/share.php index 0e3dfe8291d..24dedb619f8 100644 --- a/tests/lib/share/share.php +++ b/tests/lib/share/share.php @@ -375,6 +375,39 @@ class Test_Share extends PHPUnit_Framework_TestCase { } + /* + * if user is in a group excluded from resharing, then the share permission should + * be removed + */ + public function testShareWithUserAndUserIsExcludedFromResharing() { + + OC_User::setUserId($this->user1); + $this->assertTrue( + OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_USER, $this->user4, OCP\PERMISSION_ALL), + 'Failed asserting that user 1 successfully shared text.txt with user 4.' + ); + $this->assertContains( + 'test.txt', + OCP\Share::getItemShared('test', 'test.txt', Test_Share_Backend::FORMAT_SOURCE), + 'Failed asserting that test.txt is a shared file of user 1.' + ); + + // exclude group2 from sharing + \OC_Appconfig::setValue('core', 'shareapi_exclude_groups_list', $this->group2); + \OC_Appconfig::setValue('core', 'shareapi_exclude_groups', "yes"); + + OC_User::setUserId($this->user4); + + $share = OCP\Share::getItemSharedWith('test', 'test.txt'); + + $this->assertSame(\OCP\PERMISSION_ALL & ~OCP\PERMISSION_SHARE, $share['permissions'], + 'Failed asserting that user 4 is excluded from re-sharing'); + + \OC_Appconfig::deleteKey('core', 'shareapi_exclude_groups_list'); + \OC_Appconfig::deleteKey('core', 'shareapi_exclude_groups'); + + } + protected function shareUserOneTestFileWithGroupOne() { OC_User::setUserId($this->user1); $this->assertTrue( -- cgit v1.2.3 From 72459ca50a6441ac4ec1dd5b9d403724bfbac3b7 Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Mon, 28 Jul 2014 11:31:06 +0200 Subject: show a warning in the personal settings and admin settins if the encyption keys are not initialized --- apps/files_encryption/settings-admin.php | 3 +++ apps/files_encryption/templates/settings-admin.php | 4 ++++ apps/files_encryption/templates/settings-personal.php | 9 ++++++--- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/apps/files_encryption/settings-admin.php b/apps/files_encryption/settings-admin.php index 88e06613997..496a7cffb50 100644 --- a/apps/files_encryption/settings-admin.php +++ b/apps/files_encryption/settings-admin.php @@ -12,8 +12,11 @@ $tmpl = new OCP\Template('files_encryption', 'settings-admin'); // Check if an adminRecovery account is enabled for recovering files after lost pwd $recoveryAdminEnabled = \OC::$server->getAppConfig()->getValue('files_encryption', 'recoveryAdminEnabled', '0'); +$session = new \OCA\Encryption\Session(new \OC\Files\View('/')); +$initStatus = $session->getInitialized(); $tmpl->assign('recoveryEnabled', $recoveryAdminEnabled); +$tmpl->assign('initStatus', $initStatus); \OCP\Util::addscript('files_encryption', 'settings-admin'); \OCP\Util::addscript('core', 'multiselect'); diff --git a/apps/files_encryption/templates/settings-admin.php b/apps/files_encryption/templates/settings-admin.php index d4e6abf004a..a97261dc1c9 100644 --- a/apps/files_encryption/templates/settings-admin.php +++ b/apps/files_encryption/templates/settings-admin.php @@ -1,6 +1,9 @@

t('Encryption')); ?>

+ + t("Encryption App is enabled but your keys are not initialized, please log-out and log-in again")); ?> +

t("Enable recovery key (allow to recover users files in case of password loss):")); ?>
@@ -57,4 +60,5 @@

+
diff --git a/apps/files_encryption/templates/settings-personal.php b/apps/files_encryption/templates/settings-personal.php index e9988df3275..3d44b9fa9a5 100644 --- a/apps/files_encryption/templates/settings-personal.php +++ b/apps/files_encryption/templates/settings-personal.php @@ -1,7 +1,11 @@

t( 'Encryption' ) ); ?>

- + + + t("Encryption App is enabled but your keys are not initialized, please log-out and log-in again")); ?> + +

- - +

-- cgit v1.2.3 From d677040bdc8b77f0bf881bb2e053694691aca192 Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Fri, 18 Jul 2014 14:34:04 +0200 Subject: define type in pre hook --- apps/files_encryption/hooks/hooks.php | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/apps/files_encryption/hooks/hooks.php b/apps/files_encryption/hooks/hooks.php index c40fc43f7d5..61ba0c52879 100644 --- a/apps/files_encryption/hooks/hooks.php +++ b/apps/files_encryption/hooks/hooks.php @@ -419,10 +419,14 @@ class Hooks { $mp1 = $view->getMountPoint('/' . $user . '/files/' . $params['oldpath']); $mp2 = $view->getMountPoint('/' . $user . '/files/' . $params['newpath']); + $type = $view->is_dir('/' . $user . '/files/' . $params['oldpath']) ? 'folder' : 'file'; + if ($mp1 === $mp2) { self::$renamedFiles[$params['oldpath']] = array( 'uid' => $ownerOld, - 'path' => $pathOld); + 'path' => $pathOld, + 'type' => $type, + ); } } @@ -451,6 +455,7 @@ class Hooks { isset(self::$renamedFiles[$params['oldpath']]['path'])) { $ownerOld = self::$renamedFiles[$params['oldpath']]['uid']; $pathOld = self::$renamedFiles[$params['oldpath']]['path']; + $type = self::$renamedFiles[$params['oldpath']]['type']; unset(self::$renamedFiles[$params['oldpath']]); } else { \OCP\Util::writeLog('Encryption library', "can't get path and owner from the file before it was renamed", \OCP\Util::DEBUG); @@ -485,8 +490,7 @@ class Hooks { } // handle share keys - if (!$view->is_dir($oldKeyfilePath)) { - $type = 'file'; + if ($type === 'file') { $oldKeyfilePath .= '.key'; $newKeyfilePath .= '.key'; @@ -498,7 +502,6 @@ class Hooks { } } else { - $type = "folder"; // handle share-keys folders $view->rename($oldShareKeyPath, $newShareKeyPath); } -- cgit v1.2.3 From 1cd1214b9389a3fad3f13852a4436646a85df77b Mon Sep 17 00:00:00 2001 From: Michael Göhler Date: Sat, 26 Jul 2014 13:06:51 +0200 Subject: max icon size for app menu --- core/css/header.css | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/css/header.css b/core/css/header.css index 86db48a3f08..2df8cdd3aa3 100644 --- a/core/css/header.css +++ b/core/css/header.css @@ -190,6 +190,8 @@ #navigation .app-icon { margin: 0 auto; padding: 0; + max-height: 32px; + max-width: 32px; } /* Apps management */ -- cgit v1.2.3 From 64094c5384d1731eaf819937ed412b9d7511a130 Mon Sep 17 00:00:00 2001 From: Jan-Christoph Borchardt Date: Tue, 29 Jul 2014 14:53:43 +0200 Subject: use more understandable 'link' icon for public links (instead of hard to recognize globe), fix #9707 --- core/img/actions/public.png | Bin 338 -> 307 bytes core/img/actions/public.svg | 7 +++++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/core/img/actions/public.png b/core/img/actions/public.png index 077bb7504de..772838ad205 100644 Binary files a/core/img/actions/public.png and b/core/img/actions/public.png differ diff --git a/core/img/actions/public.svg b/core/img/actions/public.svg index c70a7627788..99a71c6cb5b 100644 --- a/core/img/actions/public.svg +++ b/core/img/actions/public.svg @@ -1,4 +1,7 @@ - - + + + + + -- cgit v1.2.3 From eb665bf3c8cad7672ec5608d1e86f86a633df595 Mon Sep 17 00:00:00 2001 From: Thomas Müller Date: Tue, 29 Jul 2014 16:59:40 +0200 Subject: only call exec() if allowed to --- settings/admin.php | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/settings/admin.php b/settings/admin.php index 704f4519ff6..dd5f969fa1a 100755 --- a/settings/admin.php +++ b/settings/admin.php @@ -101,9 +101,11 @@ $tmpl->printPage(); * @return null|string */ function findBinaryPath($program) { - exec('command -v ' . escapeshellarg($program) . ' 2> /dev/null', $output, $returnCode); - if ($returnCode === 0 && count($output) > 0) { - return escapeshellcmd($output[0]); + if (OC_Helper::is_function_enabled('exec')) { + exec('command -v ' . escapeshellarg($program) . ' 2> /dev/null', $output, $returnCode); + if ($returnCode === 0 && count($output) > 0) { + return escapeshellcmd($output[0]); + } } return null; } -- cgit v1.2.3 From 185e41afddb6daa51b2a94123b90f84d715bd413 Mon Sep 17 00:00:00 2001 From: Morris Jobke Date: Sat, 26 Jul 2014 00:04:49 +0200 Subject: Fix template rendering for 'blank' templates --- lib/public/appframework/http/templateresponse.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/public/appframework/http/templateresponse.php b/lib/public/appframework/http/templateresponse.php index 02589f4e2a4..c74d3b60254 100644 --- a/lib/public/appframework/http/templateresponse.php +++ b/lib/public/appframework/http/templateresponse.php @@ -134,8 +134,10 @@ class TemplateResponse extends Response { * @return string the rendered html */ public function render(){ + // \OCP\Template needs an empty string instead of 'blank' for an unwrapped response + $renderAs = $this->renderAs === 'blank' ? '' : $this->renderAs; - $template = new \OCP\Template($this->appName, $this->templateName, $this->renderAs); + $template = new \OCP\Template($this->appName, $this->templateName, $renderAs); foreach($this->params as $key => $value){ $template->assign($key, $value); -- cgit v1.2.3 From dc41f63930648531c17d39ad38ab191b0c9da66b Mon Sep 17 00:00:00 2001 From: Thomas Müller Date: Fri, 25 Jul 2014 08:46:43 +0200 Subject: migration test for sqlite - adding type mapping for 'tinyint unsigned' --- lib/private/db/sqlitemigrator.php | 13 +++++++++++++ tests/lib/db/migration.php | 39 --------------------------------------- tests/lib/db/mysqlmigration.php | 39 +++++++++++++++++++++++++++++++++++++++ tests/lib/db/sqlitemigration.php | 39 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 91 insertions(+), 39 deletions(-) delete mode 100644 tests/lib/db/migration.php create mode 100644 tests/lib/db/mysqlmigration.php create mode 100644 tests/lib/db/sqlitemigration.php diff --git a/lib/private/db/sqlitemigrator.php b/lib/private/db/sqlitemigrator.php index 81bbcb567ea..94b421c5562 100644 --- a/lib/private/db/sqlitemigrator.php +++ b/lib/private/db/sqlitemigrator.php @@ -9,6 +9,7 @@ namespace OC\DB; use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Schema\Schema; class SQLiteMigrator extends Migrator { @@ -61,4 +62,16 @@ class SQLiteMigrator extends Migrator { $tmpFile = uniqid("oc_"); return "$dataDir/$tmpFile.db"; } + + /** + * @param Schema $targetSchema + * @param \Doctrine\DBAL\Connection $connection + * @return \Doctrine\DBAL\Schema\SchemaDiff + */ + protected function getDiff(Schema $targetSchema, \Doctrine\DBAL\Connection $connection) { + $platform = $connection->getDatabasePlatform(); + $platform->registerDoctrineTypeMapping('tinyint unsigned', 'integer'); + + return parent::getDiff($targetSchema, $connection); + } } diff --git a/tests/lib/db/migration.php b/tests/lib/db/migration.php deleted file mode 100644 index 820a1431f54..00000000000 --- a/tests/lib/db/migration.php +++ /dev/null @@ -1,39 +0,0 @@ - - * This file is licensed under the Affero General Public License version 3 or - * later. - * See the COPYING-README file. - */ - -class TestMigration extends \PHPUnit_Framework_TestCase { - - /** @var \Doctrine\DBAL\Connection */ - private $connection; - - /** @var string */ - private $tableName; - - public function setUp() { - $this->connection = \OC_DB::getConnection(); - if (!$this->connection->getDatabasePlatform() instanceof \Doctrine\DBAL\Platforms\MySqlPlatform) { - $this->markTestSkipped("Test only relevant on MySql"); - } - - $dbPrefix = \OC::$server->getConfig()->getSystemValue("dbtableprefix"); - $this->tableName = uniqid($dbPrefix . "_enum_bit_test"); - $this->connection->exec("CREATE TABLE $this->tableName(b BIT, e ENUM('1','2','3','4'))"); - } - - public function tearDown() { - $this->connection->getSchemaManager()->dropTable($this->tableName); - } - - public function testNonOCTables() { - $manager = new \OC\DB\MDB2SchemaManager($this->connection); - $manager->updateDbFromStructure(__DIR__ . '/testschema.xml'); - - $this->assertTrue(true); - } - -} diff --git a/tests/lib/db/mysqlmigration.php b/tests/lib/db/mysqlmigration.php new file mode 100644 index 00000000000..584df1d4465 --- /dev/null +++ b/tests/lib/db/mysqlmigration.php @@ -0,0 +1,39 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +class TestMySqlMigration extends \PHPUnit_Framework_TestCase { + + /** @var \Doctrine\DBAL\Connection */ + private $connection; + + /** @var string */ + private $tableName; + + public function setUp() { + $this->connection = \OC_DB::getConnection(); + if (!$this->connection->getDatabasePlatform() instanceof \Doctrine\DBAL\Platforms\MySqlPlatform) { + $this->markTestSkipped("Test only relevant on MySql"); + } + + $dbPrefix = \OC::$server->getConfig()->getSystemValue("dbtableprefix"); + $this->tableName = uniqid($dbPrefix . "_enum_bit_test"); + $this->connection->exec("CREATE TABLE $this->tableName(b BIT, e ENUM('1','2','3','4'))"); + } + + public function tearDown() { + $this->connection->getSchemaManager()->dropTable($this->tableName); + } + + public function testNonOCTables() { + $manager = new \OC\DB\MDB2SchemaManager($this->connection); + $manager->updateDbFromStructure(__DIR__ . '/testschema.xml'); + + $this->assertTrue(true); + } + +} diff --git a/tests/lib/db/sqlitemigration.php b/tests/lib/db/sqlitemigration.php new file mode 100644 index 00000000000..adfc03a2ca7 --- /dev/null +++ b/tests/lib/db/sqlitemigration.php @@ -0,0 +1,39 @@ + + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +class TestSqliteMigration extends \PHPUnit_Framework_TestCase { + + /** @var \Doctrine\DBAL\Connection */ + private $connection; + + /** @var string */ + private $tableName; + + public function setUp() { + $this->connection = \OC_DB::getConnection(); + if (!$this->connection->getDatabasePlatform() instanceof \Doctrine\DBAL\Platforms\SqlitePlatform) { + $this->markTestSkipped("Test only relevant on Sqlite"); + } + + $dbPrefix = \OC::$server->getConfig()->getSystemValue("dbtableprefix"); + $this->tableName = uniqid($dbPrefix . "_enum_bit_test"); + $this->connection->exec("CREATE TABLE $this->tableName(t0 tinyint unsigned, t1 tinyint)"); + } + + public function tearDown() { + $this->connection->getSchemaManager()->dropTable($this->tableName); + } + + public function testNonOCTables() { + $manager = new \OC\DB\MDB2SchemaManager($this->connection); + $manager->updateDbFromStructure(__DIR__ . '/testschema.xml'); + + $this->assertTrue(true); + } + +} -- cgit v1.2.3 From 8f9deb118aabcd9dc94bb99cbeec56199a7cdd29 Mon Sep 17 00:00:00 2001 From: Jan-Christoph Borchardt Date: Wed, 30 Jul 2014 11:07:16 +0200 Subject: also use link icon for the folders .. --- core/img/filetypes/folder-public.png | Bin 1308 -> 1251 bytes core/img/filetypes/folder-public.svg | 41 +++++++++++++++++++---------------- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/core/img/filetypes/folder-public.png b/core/img/filetypes/folder-public.png index 40d8cd067bb..7f8f8e3411c 100644 Binary files a/core/img/filetypes/folder-public.png and b/core/img/filetypes/folder-public.png differ diff --git a/core/img/filetypes/folder-public.svg b/core/img/filetypes/folder-public.svg index f1f9321fd35..9516a7de59d 100644 --- a/core/img/filetypes/folder-public.svg +++ b/core/img/filetypes/folder-public.svg @@ -1,59 +1,62 @@ - + - + - + - + - + - + - + - + - + - - - + + + - - - + + + - - - + + + - + + + + -- cgit v1.2.3 From 0b8135dcc468bc35fee96595c1fadb0636c6db3e Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Mon, 28 Jul 2014 17:13:17 +0200 Subject: make sure that we set the expire date if a date is adefault date is set --- lib/private/share/share.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/private/share/share.php b/lib/private/share/share.php index 0c42853302d..7fd5cd70e1d 100644 --- a/lib/private/share/share.php +++ b/lib/private/share/share.php @@ -595,6 +595,7 @@ class Share extends \OC\Share\Constants { $shareWith['group'] = $group; $shareWith['users'] = array_diff(\OC_Group::usersInGroup($group), array($uidOwner)); } else if ($shareType === self::SHARE_TYPE_LINK) { + $updateExistingShare = false; if (\OC_Appconfig::getValue('core', 'shareapi_allow_links', 'yes') == 'yes') { // when updating a link share @@ -629,7 +630,7 @@ class Share extends \OC\Share\Constants { throw new \Exception($message_t); } - if (!empty($updateExistingShare) && + if ($updateExistingShare === false && self::isDefaultExpireDateEnabled() && empty($expirationDate)) { $expirationDate = Helper::calcExpireDate(); -- cgit v1.2.3 From 60b1a6e75f0a3bc0a8b912061f5b0462a20e6d52 Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Mon, 28 Jul 2014 17:19:19 +0200 Subject: add unit test --- tests/lib/share/share.php | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/lib/share/share.php b/tests/lib/share/share.php index 24dedb619f8..bb827eece73 100644 --- a/tests/lib/share/share.php +++ b/tests/lib/share/share.php @@ -631,6 +631,32 @@ class Test_Share extends PHPUnit_Framework_TestCase { ); } + public function testShareItemWithLinkAndDefaultExpireDate() { + OC_User::setUserId($this->user1); + + \OC_Appconfig::setValue('core', 'shareapi_default_expire_date', 'yes'); + \OC_Appconfig::setValue('core', 'shareapi_expire_after_n_days', '2'); + + $token = OCP\Share::shareItem('test', 'test.txt', OCP\Share::SHARE_TYPE_LINK, null, OCP\PERMISSION_READ); + $this->assertInternalType( + 'string', + $token, + 'Failed asserting that user 1 successfully shared text.txt as link with token.' + ); + + // share should have default expire date + + $row = $this->getShareByValidToken($token); + $this->assertNotEmpty( + $row['expiration'], + 'Failed asserting that the returned row has an default expiration date.' + ); + + \OC_Appconfig::deleteKey('core', 'shareapi_default_expire_date'); + \OC_Appconfig::deleteKey('core', 'shareapi_expire_after_n_days'); + + } + public function testUnshareAll() { $this->shareUserTestFileWithUser($this->user1, $this->user2); $this->shareUserTestFileWithUser($this->user2, $this->user3); -- cgit v1.2.3 From 5eca22d229a20722ff209bd4e5fca8f73063a5ec Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Thu, 24 Jul 2014 15:30:00 +0200 Subject: make the versions and encryption app aware of the copy operation --- apps/files_encryption/hooks/hooks.php | 43 ++++++++++++++++++++++++++++------- apps/files_encryption/lib/helper.php | 4 +++- apps/files_versions/appinfo/app.php | 1 + apps/files_versions/lib/hooks.php | 20 +++++++++++++++- apps/files_versions/lib/versions.php | 11 +++++---- 5 files changed, 65 insertions(+), 14 deletions(-) diff --git a/apps/files_encryption/hooks/hooks.php b/apps/files_encryption/hooks/hooks.php index 61ba0c52879..4a257f2ad33 100644 --- a/apps/files_encryption/hooks/hooks.php +++ b/apps/files_encryption/hooks/hooks.php @@ -426,18 +426,44 @@ class Hooks { 'uid' => $ownerOld, 'path' => $pathOld, 'type' => $type, + 'operation' => 'rename', ); + } } /** - * after a file is renamed, rename its keyfile and share-keys also fix the file size and fix also the sharing - * @param array $params array with oldpath and newpath + * mark file as renamed so that we know the original source after the file was renamed + * @param array $params with the old path and the new path + */ + public static function preCopy($params) { + $user = \OCP\User::getUser(); + $view = new \OC\Files\View('/'); + $util = new Util($view, $user); + list($ownerOld, $pathOld) = $util->getUidAndFilename($params['oldpath']); + + // we only need to rename the keys if the rename happens on the same mountpoint + // otherwise we perform a stream copy, so we get a new set of keys + $mp1 = $view->getMountPoint('/' . $user . '/files/' . $params['oldpath']); + $mp2 = $view->getMountPoint('/' . $user . '/files/' . $params['newpath']); + + $type = $view->is_dir('/' . $user . '/files/' . $params['oldpath']) ? 'folder' : 'file'; + + if ($mp1 === $mp2) { + self::$renamedFiles[$params['oldpath']] = array( + 'uid' => $ownerOld, + 'path' => $pathOld, + 'type' => $type, + 'operation' => 'copy'); + } + } + + /** + * after a file is renamed/copied, rename/copy its keyfile and share-keys also fix the file size and fix also the sharing * - * This function is connected to the rename signal of OC_Filesystem and adjust the name and location - * of the stored versions along the actual file + * @param array $params array with oldpath and newpath */ - public static function postRename($params) { + public static function postRenameOrCopy($params) { if (\OCP\App::isEnabled('files_encryption') === false) { return true; @@ -456,6 +482,7 @@ class Hooks { $ownerOld = self::$renamedFiles[$params['oldpath']]['uid']; $pathOld = self::$renamedFiles[$params['oldpath']]['path']; $type = self::$renamedFiles[$params['oldpath']]['type']; + $operation = self::$renamedFiles[$params['oldpath']]['operation']; unset(self::$renamedFiles[$params['oldpath']]); } else { \OCP\Util::writeLog('Encryption library', "can't get path and owner from the file before it was renamed", \OCP\Util::DEBUG); @@ -498,17 +525,17 @@ class Hooks { $matches = Helper::findShareKeys($oldShareKeyPath, $view); foreach ($matches as $src) { $dst = \OC\Files\Filesystem::normalizePath(str_replace($pathOld, $pathNew, $src)); - $view->rename($src, $dst); + $view->$operation($src, $dst); } } else { // handle share-keys folders - $view->rename($oldShareKeyPath, $newShareKeyPath); + $view->$operation($oldShareKeyPath, $newShareKeyPath); } // Rename keyfile so it isn't orphaned if ($view->file_exists($oldKeyfilePath)) { - $view->rename($oldKeyfilePath, $newKeyfilePath); + $view->$operation($oldKeyfilePath, $newKeyfilePath); } diff --git a/apps/files_encryption/lib/helper.php b/apps/files_encryption/lib/helper.php index fed0788028f..ed42cec326a 100755 --- a/apps/files_encryption/lib/helper.php +++ b/apps/files_encryption/lib/helper.php @@ -62,7 +62,9 @@ class Helper { public static function registerFilesystemHooks() { \OCP\Util::connectHook('OC_Filesystem', 'rename', 'OCA\Encryption\Hooks', 'preRename'); - \OCP\Util::connectHook('OC_Filesystem', 'post_rename', 'OCA\Encryption\Hooks', 'postRename'); + \OCP\Util::connectHook('OC_Filesystem', 'post_rename', 'OCA\Encryption\Hooks', 'postRenameOrCopy'); + \OCP\Util::connectHook('OC_Filesystem', 'copy', 'OCA\Encryption\Hooks', 'preCopy'); + \OCP\Util::connectHook('OC_Filesystem', 'post_copy', 'OCA\Encryption\Hooks', 'postRenameOrCopy'); \OCP\Util::connectHook('OC_Filesystem', 'post_delete', 'OCA\Encryption\Hooks', 'postDelete'); \OCP\Util::connectHook('OC_Filesystem', 'delete', 'OCA\Encryption\Hooks', 'preDelete'); \OCP\Util::connectHook('OC_Filesystem', 'post_umount', 'OCA\Encryption\Hooks', 'postUmount'); diff --git a/apps/files_versions/appinfo/app.php b/apps/files_versions/appinfo/app.php index 371162cd16f..6020d401ef6 100644 --- a/apps/files_versions/appinfo/app.php +++ b/apps/files_versions/appinfo/app.php @@ -14,3 +14,4 @@ OCP\Util::connectHook('OC_Filesystem', 'write', "OCA\Files_Versions\Hooks", "wri OCP\Util::connectHook('OC_Filesystem', 'post_delete', "OCA\Files_Versions\Hooks", "remove_hook"); OCP\Util::connectHook('OC_Filesystem', 'delete', "OCA\Files_Versions\Hooks", "pre_remove_hook"); OCP\Util::connectHook('OC_Filesystem', 'rename', "OCA\Files_Versions\Hooks", "rename_hook"); +OCP\Util::connectHook('OC_Filesystem', 'copy', "OCA\Files_Versions\Hooks", "copy_hook"); diff --git a/apps/files_versions/lib/hooks.php b/apps/files_versions/lib/hooks.php index 990f1403e8d..17eacc6a6ed 100644 --- a/apps/files_versions/lib/hooks.php +++ b/apps/files_versions/lib/hooks.php @@ -69,7 +69,25 @@ class Hooks { $oldpath = $params['oldpath']; $newpath = $params['newpath']; if($oldpath<>'' && $newpath<>'') { - Storage::rename( $oldpath, $newpath ); + Storage::renameOrCopy($oldpath, $newpath, 'rename'); + } + } + } + + /** + * copy versions of copied files + * @param array $params array with oldpath and newpath + * + * This function is connected to the copy signal of OC_Filesystem and copies the + * the stored versions to the new location + */ + public static function copy_hook($params) { + + if (\OCP\App::isEnabled('files_versions')) { + $oldpath = $params['oldpath']; + $newpath = $params['newpath']; + if($oldpath<>'' && $newpath<>'') { + Storage::renameOrCopy($oldpath, $newpath, 'copy'); } } } diff --git a/apps/files_versions/lib/versions.php b/apps/files_versions/lib/versions.php index 2e048416c11..97926f5bbcb 100644 --- a/apps/files_versions/lib/versions.php +++ b/apps/files_versions/lib/versions.php @@ -174,9 +174,12 @@ class Storage { } /** - * rename versions of a file + * rename or copy versions of a file + * @param string $old_path + * @param string $new_path + * @param string $operation can be 'copy' or 'rename' */ - public static function rename($old_path, $new_path) { + public static function renameOrCopy($old_path, $new_path, $operation) { list($uid, $oldpath) = self::getUidAndFilename($old_path); list($uidn, $newpath) = self::getUidAndFilename($new_path); $versions_view = new \OC\Files\View('/'.$uid .'/files_versions'); @@ -191,13 +194,13 @@ class Storage { self::expire($newpath); if ( $files_view->is_dir($oldpath) && $versions_view->is_dir($oldpath) ) { - $versions_view->rename($oldpath, $newpath); + $versions_view->$operation($oldpath, $newpath); } else if ( ($versions = Storage::getVersions($uid, $oldpath)) ) { // create missing dirs if necessary self::createMissingDirectories($newpath, new \OC\Files\View('/'. $uidn)); foreach ($versions as $v) { - $versions_view->rename($oldpath.'.v'.$v['version'], $newpath.'.v'.$v['version']); + $versions_view->$operation($oldpath.'.v'.$v['version'], $newpath.'.v'.$v['version']); } } } -- cgit v1.2.3 From 2e8f9932997b9c2641557e316193a7c4e791691a Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Thu, 24 Jul 2014 16:49:38 +0200 Subject: add unit test for rename and copy operation --- apps/files_encryption/tests/hooks.php | 52 ++++++++++++++++ apps/files_versions/appinfo/app.php | 8 +-- apps/files_versions/lib/hooks.php | 10 +++ apps/files_versions/tests/versions.php | 108 +++++++++++++++++++++++++++++++++ 4 files changed, 171 insertions(+), 7 deletions(-) diff --git a/apps/files_encryption/tests/hooks.php b/apps/files_encryption/tests/hooks.php index 5eda8df01b9..cc5b6d5b6f6 100644 --- a/apps/files_encryption/tests/hooks.php +++ b/apps/files_encryption/tests/hooks.php @@ -335,6 +335,58 @@ class Test_Encryption_Hooks extends \PHPUnit_Framework_TestCase { $this->rootView->unlink('/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $this->folder); } + /** + * test rename operation + */ + function testCopyHook() { + + // save file with content + $cryptedFile = file_put_contents('crypt:///' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $this->filename, $this->data); + + // test that data was successfully written + $this->assertTrue(is_int($cryptedFile)); + + // check if keys exists + $this->assertTrue($this->rootView->file_exists( + '/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/share-keys/' + . $this->filename . '.' . self::TEST_ENCRYPTION_HOOKS_USER1 . '.shareKey')); + + $this->assertTrue($this->rootView->file_exists( + '/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/keyfiles/' + . $this->filename . '.key')); + + // make subfolder and sub-subfolder + $this->rootView->mkdir('/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $this->folder); + $this->rootView->mkdir('/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $this->folder . '/' . $this->folder); + + $this->assertTrue($this->rootView->is_dir('/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $this->folder . '/' . $this->folder)); + + // copy the file to the sub-subfolder + \OC\Files\Filesystem::copy($this->filename, '/' . $this->folder . '/' . $this->folder . '/' . $this->filename); + + $this->assertTrue($this->rootView->file_exists('/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $this->filename)); + $this->assertTrue($this->rootView->file_exists('/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $this->folder . '/' . $this->folder . '/' . $this->filename)); + + // keys should be copied too + $this->assertTrue($this->rootView->file_exists( + '/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/share-keys/' + . $this->filename . '.' . self::TEST_ENCRYPTION_HOOKS_USER1 . '.shareKey')); + $this->assertTrue($this->rootView->file_exists( + '/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/keyfiles/' + . $this->filename . '.key')); + + $this->assertTrue($this->rootView->file_exists( + '/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/share-keys/' . $this->folder . '/' . $this->folder . '/' + . $this->filename . '.' . self::TEST_ENCRYPTION_HOOKS_USER1 . '.shareKey')); + $this->assertTrue($this->rootView->file_exists( + '/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files_encryption/keyfiles/' . $this->folder . '/' . $this->folder . '/' + . $this->filename . '.key')); + + // cleanup + $this->rootView->unlink('/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $this->folder); + $this->rootView->unlink('/' . self::TEST_ENCRYPTION_HOOKS_USER1 . '/files/' . $this->filename); + } + /** * @brief replacing encryption keys during password change should be allowed * until the user logged in for the first time diff --git a/apps/files_versions/appinfo/app.php b/apps/files_versions/appinfo/app.php index 6020d401ef6..8c517d4d0ff 100644 --- a/apps/files_versions/appinfo/app.php +++ b/apps/files_versions/appinfo/app.php @@ -8,10 +8,4 @@ OC::$CLASSPATH['OCA\Files_Versions\Capabilities'] = 'files_versions/lib/capabili OCP\Util::addscript('files_versions', 'versions'); OCP\Util::addStyle('files_versions', 'versions'); -// Listen to write signals -OCP\Util::connectHook('OC_Filesystem', 'write', "OCA\Files_Versions\Hooks", "write_hook"); -// Listen to delete and rename signals -OCP\Util::connectHook('OC_Filesystem', 'post_delete', "OCA\Files_Versions\Hooks", "remove_hook"); -OCP\Util::connectHook('OC_Filesystem', 'delete', "OCA\Files_Versions\Hooks", "pre_remove_hook"); -OCP\Util::connectHook('OC_Filesystem', 'rename', "OCA\Files_Versions\Hooks", "rename_hook"); -OCP\Util::connectHook('OC_Filesystem', 'copy', "OCA\Files_Versions\Hooks", "copy_hook"); +\OCA\Files_Versions\Hooks::connectHooks(); diff --git a/apps/files_versions/lib/hooks.php b/apps/files_versions/lib/hooks.php index 17eacc6a6ed..1a584232ba7 100644 --- a/apps/files_versions/lib/hooks.php +++ b/apps/files_versions/lib/hooks.php @@ -14,6 +14,16 @@ namespace OCA\Files_Versions; class Hooks { + public static function connectHooks() { + // Listen to write signals + \OCP\Util::connectHook('OC_Filesystem', 'write', "OCA\Files_Versions\Hooks", "write_hook"); + // Listen to delete and rename signals + \OCP\Util::connectHook('OC_Filesystem', 'post_delete', "OCA\Files_Versions\Hooks", "remove_hook"); + \OCP\Util::connectHook('OC_Filesystem', 'delete', "OCA\Files_Versions\Hooks", "pre_remove_hook"); + \OCP\Util::connectHook('OC_Filesystem', 'rename', "OCA\Files_Versions\Hooks", "rename_hook"); + \OCP\Util::connectHook('OC_Filesystem', 'copy', "OCA\Files_Versions\Hooks", "copy_hook"); + } + /** * listen to write event. */ diff --git a/apps/files_versions/tests/versions.php b/apps/files_versions/tests/versions.php index aa66faffcbf..03432276358 100644 --- a/apps/files_versions/tests/versions.php +++ b/apps/files_versions/tests/versions.php @@ -20,6 +20,7 @@ * */ +require_once __DIR__ . '/../appinfo/app.php'; require_once __DIR__ . '/../lib/versions.php'; /** @@ -28,6 +29,32 @@ require_once __DIR__ . '/../lib/versions.php'; */ class Test_Files_Versioning extends \PHPUnit_Framework_TestCase { + const TEST_VERSIONS_USER = 'test-versions-user'; + const USERS_VERSIONS_ROOT = '/test-versions-user/files_versions'; + + private $rootView; + + public static function setUpBeforeClass() { + // create test user + self::loginHelper(self::TEST_VERSIONS_USER, true); + } + + public static function tearDownAfterClass() { + // cleanup test user + \OC_User::deleteUser(self::TEST_VERSIONS_USER); + } + + function setUp() { + self::loginHelper(self::TEST_VERSIONS_USER); + $this->rootView = new \OC\Files\View(); + if (!$this->rootView->file_exists(self::USERS_VERSIONS_ROOT)) { + $this->rootView->mkdir(self::USERS_VERSIONS_ROOT); + } + } + + function tearDown() { + $this->rootView->deleteAll(self::USERS_VERSIONS_ROOT); + } /** * @medium @@ -176,6 +203,87 @@ class Test_Files_Versioning extends \PHPUnit_Framework_TestCase { ); } + function testRename() { + + \OC\Files\Filesystem::file_put_contents("test.txt", "test file"); + + $t1 = time(); + // second version is two weeks older, this way we make sure that no + // version will be expired + $t2 = $t1 - 60 * 60 * 24 * 14; + + // create some versions + $v1 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t1; + $v2 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t2; + $v1Renamed = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t1; + $v2Renamed = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t2; + + $this->rootView->file_put_contents($v1, 'version1'); + $this->rootView->file_put_contents($v2, 'version2'); + + // execute rename hook of versions app + \OCA\Files_Versions\Storage::renameOrCopy("test.txt", "test2.txt", 'rename'); + + $this->assertFalse($this->rootView->file_exists($v1)); + $this->assertFalse($this->rootView->file_exists($v2)); + + $this->assertTrue($this->rootView->file_exists($v1Renamed)); + $this->assertTrue($this->rootView->file_exists($v2Renamed)); + + //cleanup + \OC\Files\Filesystem::unlink('test2.txt'); + } + + function testCopy() { + + \OC\Files\Filesystem::file_put_contents("test.txt", "test file"); + + $t1 = time(); + // second version is two weeks older, this way we make sure that no + // version will be expired + $t2 = $t1 - 60 * 60 * 24 * 14; + + // create some versions + $v1 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t1; + $v2 = self::USERS_VERSIONS_ROOT . '/test.txt.v' . $t2; + $v1Copied = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t1; + $v2Copied = self::USERS_VERSIONS_ROOT . '/test2.txt.v' . $t2; + + $this->rootView->file_put_contents($v1, 'version1'); + $this->rootView->file_put_contents($v2, 'version2'); + + // execute copy hook of versions app + \OCA\Files_Versions\Storage::renameOrCopy("test.txt", "test2.txt", 'copy'); + + $this->assertTrue($this->rootView->file_exists($v1)); + $this->assertTrue($this->rootView->file_exists($v2)); + + $this->assertTrue($this->rootView->file_exists($v1Copied)); + $this->assertTrue($this->rootView->file_exists($v2Copied)); + + //cleanup + \OC\Files\Filesystem::unlink('test.txt'); + \OC\Files\Filesystem::unlink('test2.txt'); + } + + /** + * @param string $user + * @param bool $create + * @param bool $password + */ + public static function loginHelper($user, $create = false) { + + if ($create) { + \OC_User::createUser($user, $user); + } + + \OC_Util::tearDownFS(); + \OC_User::setUserId(''); + \OC\Files\Filesystem::tearDown(); + \OC_User::setUserId($user); + \OC_Util::setupFS($user); + } + } // extend the original class to make it possible to test protected methods -- cgit v1.2.3 From 19bad71da2584450de3eed1e8f61337031a9466e Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Thu, 24 Jul 2014 18:54:12 +0200 Subject: make sure that we always find all versions --- apps/files_versions/lib/versions.php | 59 ++++++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 22 deletions(-) diff --git a/apps/files_versions/lib/versions.php b/apps/files_versions/lib/versions.php index 97926f5bbcb..a9d51b2c58b 100644 --- a/apps/files_versions/lib/versions.php +++ b/apps/files_versions/lib/versions.php @@ -191,8 +191,6 @@ class Storage { return self::store($new_path); } - self::expire($newpath); - if ( $files_view->is_dir($oldpath) && $versions_view->is_dir($oldpath) ) { $versions_view->$operation($oldpath, $newpath); } else if ( ($versions = Storage::getVersions($uid, $oldpath)) ) { @@ -203,6 +201,11 @@ class Storage { $versions_view->$operation($oldpath.'.v'.$v['version'], $newpath.'.v'.$v['version']); } } + + if (!$files_view->is_dir($newpath)) { + self::expire($newpath); + } + } /** @@ -257,34 +260,46 @@ class Storage { public static function getVersions($uid, $filename, $userFullPath = '') { $versions = array(); // fetch for old versions - $view = new \OC\Files\View('/' . $uid . '/' . self::VERSIONS_ROOT); + $view = new \OC\Files\View('/' . $uid . '/'); $pathinfo = pathinfo($filename); + $versionedFile = $pathinfo['basename']; - $files = $view->getDirectoryContent($pathinfo['dirname']); + $dir = self::VERSIONS_ROOT . '/' . $pathinfo['dirname']; - $versionedFile = $pathinfo['basename']; + $dirContent = false; + if ($view->is_dir($dir)) { + $dirContent = $view->opendir($dir); + } - foreach ($files as $file) { - if ($file['type'] === 'file') { - $pos = strrpos($file['path'], '.v'); - $currentFile = substr($file['name'], 0, strrpos($file['name'], '.v')); - if ($currentFile === $versionedFile) { - $version = substr($file['path'], $pos + 2); - $key = $version . '#' . $filename; - $versions[$key]['cur'] = 0; - $versions[$key]['version'] = $version; - $versions[$key]['humanReadableTimestamp'] = self::getHumanReadableTimestamp($version); - if (empty($userFullPath)) { - $versions[$key]['preview'] = ''; - } else { - $versions[$key]['preview'] = \OCP\Util::linkToRoute('core_ajax_versions_preview', array('file' => $userFullPath, 'version' => $version)); + if ($dirContent === false) { + return $versions; + } + + if (is_resource($dirContent)) { + while (($entryName = readdir($dirContent)) !== false) { + if (!\OC\Files\Filesystem::isIgnoredDir($entryName)) { + $pathparts = pathinfo($entryName); + $filename = $pathparts['filename']; + if ($filename === $versionedFile) { + $pathparts = pathinfo($entryName); + $timestamp = substr($pathparts['extension'], 1); + $filename = $pathparts['filename']; + $key = $timestamp . '#' . $filename; + $versions[$key]['version'] = $timestamp; + $versions[$key]['humanReadableTimestamp'] = self::getHumanReadableTimestamp($timestamp); + if (empty($userFullPath)) { + $versions[$key]['preview'] = ''; + } else { + $versions[$key]['preview'] = \OCP\Util::linkToRoute('core_ajax_versions_preview', array('file' => $userFullPath, 'version' => $timestamp)); + } + $versions[$key]['path'] = $filename; + $versions[$key]['name'] = $versionedFile; + $versions[$key]['size'] = $view->filesize($dir . '/' . $entryName); } - $versions[$key]['path'] = $filename; - $versions[$key]['name'] = $versionedFile; - $versions[$key]['size'] = $file['size']; } } + closedir($dirContent); } // sort with newest version first -- cgit v1.2.3 From 4f40cde66afe2376ede177020882e5236a5a3373 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Tue, 29 Jul 2014 17:23:44 +0200 Subject: Also keep maxY into account when scaling a preview while preserving aspect ratio --- apps/files_sharing/ajax/publicpreview.php | 4 ---- core/ajax/preview.php | 4 ---- lib/private/preview.php | 10 ++++++++-- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/apps/files_sharing/ajax/publicpreview.php b/apps/files_sharing/ajax/publicpreview.php index 0b2af7a6e59..f5343a7ef26 100644 --- a/apps/files_sharing/ajax/publicpreview.php +++ b/apps/files_sharing/ajax/publicpreview.php @@ -70,10 +70,6 @@ if(substr($path, 0, 1) === '/') { $path = substr($path, 1); } -if ($keepAspect === true) { - $maxY = $maxX; -} - if($maxX === 0 || $maxY === 0) { \OC_Response::setStatus(\OC_Response::STATUS_BAD_REQUEST); \OC_Log::write('core-preview', 'x and/or y set to 0', \OC_Log::DEBUG); diff --git a/core/ajax/preview.php b/core/ajax/preview.php index d38043707ac..edbd41d2db4 100644 --- a/core/ajax/preview.php +++ b/core/ajax/preview.php @@ -21,10 +21,6 @@ if ($file === '') { exit; } -if ($keepAspect === true) { - $maxY = $maxX; -} - if ($maxX === 0 || $maxY === 0) { //400 Bad Request \OC_Response::setStatus(400); diff --git a/lib/private/preview.php b/lib/private/preview.php index 8089379bde5..6172519c7d1 100755 --- a/lib/private/preview.php +++ b/lib/private/preview.php @@ -561,9 +561,15 @@ class Preview { $realX = (int)$image->width(); $realY = (int)$image->height(); - // compute $maxY using the aspect of the generated preview + // compute $maxY and $maxX using the aspect of the generated preview if ($this->keepAspect) { - $y = $x / ($realX / $realY); + $ratio = $realX / $realY; + if($x / $ratio < $y) { + // width restricted + $y = $x / $ratio; + } else { + $x = $y * $ratio; + } } if ($x === $realX && $y === $realY) { -- cgit v1.2.3 From f68fa072c7f63b15268f1166c81704aaf0a4e8a8 Mon Sep 17 00:00:00 2001 From: Georg Ehrke Date: Wed, 30 Jul 2014 12:16:03 +0200 Subject: extract transparency fix from #8050 --- lib/private/image.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/private/image.php b/lib/private/image.php index 5331c399159..0dff8c5a9da 100644 --- a/lib/private/image.php +++ b/lib/private/image.php @@ -870,6 +870,14 @@ class OC_Image { imagedestroy($process); return false; } + + // preserve transparency + if($this->imageType == IMAGETYPE_GIF or $this->imageType == IMAGETYPE_PNG) { + imagecolortransparent($process, imagecolorallocatealpha($process, 0, 0, 0, 127)); + imagealphablending($process, false); + imagesavealpha($process, true); + } + imagecopyresampled($process, $this->resource, 0, 0, $x, $y, $w, $h, $w, $h); if ($process == false) { OC_Log::write('core', __METHOD__.'(): Error resampling process image '.$w.'x'.$h, OC_Log::ERROR); -- cgit v1.2.3 From 24c8774b7ff448c12f6514933b86ebfbc2019440 Mon Sep 17 00:00:00 2001 From: Georg Ehrke Date: Wed, 30 Jul 2014 17:06:41 +0200 Subject: don't preload videos on public sharing, fixes #10042 --- apps/files_sharing/templates/public.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/files_sharing/templates/public.php b/apps/files_sharing/templates/public.php index 386fa7e17cd..8406b79cf1a 100644 --- a/apps/files_sharing/templates/public.php +++ b/apps/files_sharing/templates/public.php @@ -42,7 +42,7 @@

-
-- cgit v1.2.3 From 57c954a345a21b0e9b2815b9ae015888ce66acbd Mon Sep 17 00:00:00 2001 From: Jan-Christoph Borchardt Date: Wed, 30 Jul 2014 18:00:46 +0200 Subject: fix notification preventing top bar clickability, fix #9680 --- core/css/styles.css | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/core/css/styles.css b/core/css/styles.css index 17ed3fbc74c..4ac43f439b4 100644 --- a/core/css/styles.css +++ b/core/css/styles.css @@ -680,7 +680,13 @@ label.infield { .center { text-align:center; } .inlineblock { display: inline-block; } -#notification-container { position: fixed; top: 0px; width: 100%; text-align: center; z-index: 101; line-height: 1.2;} +#notification-container { + position: absolute; + top: 0; + width: 100%; + text-align: center; + line-height: 1.2; +} #notification, #update-notification { z-index: 101; background-color: #fc4; -- cgit v1.2.3 From 32cec6cae43ff0d72632657749c789d4c4d36279 Mon Sep 17 00:00:00 2001 From: Jan-Christoph Borchardt Date: Wed, 30 Jul 2014 18:02:53 +0200 Subject: fix yellow notification style --- core/css/styles.css | 13 ++++++++++--- core/js/js.js | 4 ++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/core/css/styles.css b/core/css/styles.css index 4ac43f439b4..066126b41eb 100644 --- a/core/css/styles.css +++ b/core/css/styles.css @@ -685,20 +685,27 @@ label.infield { top: 0; width: 100%; text-align: center; - line-height: 1.2; } #notification, #update-notification { + margin: 0 auto; z-index: 101; background-color: #fc4; border: 0; - padding: 0 .7em .3em; + padding: 1px 8px; display: none; position: relative; top: 0; border-bottom-left-radius: 3px; border-bottom-right-radius: 3px; + -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=90)"; + filter:alpha(opacity=90); + opacity: .9; +} +#notification span, #update-notification span { + cursor: pointer; + font-weight: bold; + margin-left: 1em; } -#notification span, #update-notification span { cursor:pointer; font-weight:bold; margin-left:1em; } tr .action:not(.permanent), .selectedActions a { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; filter:alpha(opacity=0); opacity:0; } tr:hover .action, tr .action.permanent, .selectedActions a { -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=50)"; filter:alpha(opacity=50); opacity:.5; } diff --git a/core/js/js.js b/core/js/js.js index 5c9d7bf0141..60f9cc11a58 100644 --- a/core/js/js.js +++ b/core/js/js.js @@ -696,7 +696,7 @@ OC.Notification={ var notification = $('#notification'); if((notification.filter('span.undo').length == 1) || OC.Notification.isHidden()){ notification.html(html); - notification.fadeIn().css("display","inline"); + notification.fadeIn().css('display','inline-block'); }else{ OC.Notification.queuedNotifications.push(html); } @@ -710,7 +710,7 @@ OC.Notification={ var notification = $('#notification'); if((notification.filter('span.undo').length == 1) || OC.Notification.isHidden()){ notification.text(text); - notification.fadeIn().css("display","inline"); + notification.fadeIn().css('display','inline-block'); }else{ OC.Notification.queuedNotifications.push($('
').text(text).html()); } -- cgit v1.2.3 From a0f024fec22854509de19a1aa735a1567c3b534c Mon Sep 17 00:00:00 2001 From: Jan-Christoph Borchardt Date: Wed, 30 Jul 2014 18:16:32 +0200 Subject: set max width for notifications so they won't overlap the whole header --- core/css/styles.css | 1 + 1 file changed, 1 insertion(+) diff --git a/core/css/styles.css b/core/css/styles.css index 066126b41eb..3390b3000e2 100644 --- a/core/css/styles.css +++ b/core/css/styles.css @@ -688,6 +688,7 @@ label.infield { } #notification, #update-notification { margin: 0 auto; + max-width: 60%; z-index: 101; background-color: #fc4; border: 0; -- cgit v1.2.3