diff options
26 files changed, 859 insertions, 184 deletions
diff --git a/.gitignore b/.gitignore index a6baffc6a59..531e372e607 100644 --- a/.gitignore +++ b/.gitignore @@ -115,3 +115,4 @@ Vagrantfile /tests/data/testimage-copy.png /config/config-autotest-backup.php /config/autoconfig.php +clover.xml diff --git a/apps/files/js/favoritesfilelist.js b/apps/files/js/favoritesfilelist.js index 4e7db9f17ba..e6532ab188c 100644 --- a/apps/files/js/favoritesfilelist.js +++ b/apps/files/js/favoritesfilelist.js @@ -71,6 +71,10 @@ $(document).ready(function() { if (this._reloadCall) { this._reloadCall.abort(); } + + // there is only root + this._setCurrentDir('/', false); + this._reloadCall = $.ajax({ url: OC.generateUrl('/apps/files/api/v1/tags/{tagName}/files', {tagName: tagName}), type: 'GET', @@ -86,10 +90,9 @@ $(document).ready(function() { if (result.files) { this.setFiles(result.files.sort(this._sortComparator)); + return true; } - else { - // TODO: error handling - } + return false; } }); diff --git a/apps/files_external/controller/storagescontroller.php b/apps/files_external/controller/storagescontroller.php index f754565f628..048f3588ed7 100644 --- a/apps/files_external/controller/storagescontroller.php +++ b/apps/files_external/controller/storagescontroller.php @@ -237,9 +237,21 @@ abstract class StoragesController extends Controller { ) ); } catch (InsufficientDataForMeaningfulAnswerException $e) { - $storage->setStatus(\OC_Mount_Config::STATUS_INDETERMINATE); + $storage->setStatus( + \OC_Mount_Config::STATUS_INDETERMINATE, + $this->l10n->t('Insufficient data: %s', [$e->getMessage()]) + ); } catch (StorageNotAvailableException $e) { - $storage->setStatus(\OC_Mount_Config::STATUS_ERROR); + $storage->setStatus( + \OC_Mount_Config::STATUS_ERROR, + $e->getMessage() + ); + } catch (\Exception $e) { + // FIXME: convert storage exceptions to StorageNotAvailableException + $storage->setStatus( + \OC_Mount_Config::STATUS_ERROR, + get_class($e).': '.$e->getMessage() + ); } } diff --git a/apps/files_external/js/mountsfilelist.js b/apps/files_external/js/mountsfilelist.js index c45faafd9bf..35aef751fef 100644 --- a/apps/files_external/js/mountsfilelist.js +++ b/apps/files_external/js/mountsfilelist.js @@ -86,6 +86,10 @@ if (this._reloadCall) { this._reloadCall.abort(); } + + // there is only root + this._setCurrentDir('/', false); + this._reloadCall = $.ajax({ url: OC.linkToOCS('apps/files_external/api/v1') + 'mounts', data: { @@ -106,10 +110,9 @@ if (result.ocs && result.ocs.data) { this.setFiles(this._makeFiles(result.ocs.data)); + return true; } - else { - // TODO: error handling - } + return false; }, /** diff --git a/apps/files_external/js/settings.js b/apps/files_external/js/settings.js index 5da34c52193..a839f396b9b 100644 --- a/apps/files_external/js/settings.js +++ b/apps/files_external/js/settings.js @@ -643,6 +643,10 @@ MountConfigListView.prototype = _.extend({ }); addSelect2(this.$el.find('tr:not(#addMountPoint) .applicableUsers'), this._userListLimit); + this.$el.tooltip({ + selector: '.status span', + container: 'body' + }); this._initEvents(); @@ -709,11 +713,12 @@ MountConfigListView.prototype = _.extend({ } highlightInput($target); var $tr = $target.closest('tr'); + this.updateStatus($tr, null); var timer = $tr.data('save-timer'); clearTimeout(timer); timer = setTimeout(function() { - self.saveStorageConfig($tr); + self.saveStorageConfig($tr, null, timer); }, 2000); $tr.data('save-timer', timer); }, @@ -931,8 +936,9 @@ MountConfigListView.prototype = _.extend({ * * @param $tr storage row * @param Function callback callback to call after save + * @param concurrentTimer only update if the timer matches this */ - saveStorageConfig:function($tr, callback) { + saveStorageConfig:function($tr, callback, concurrentTimer) { var self = this; var storage = this.getStorageConfig($tr); if (!storage.validate()) { @@ -942,15 +948,23 @@ MountConfigListView.prototype = _.extend({ this.updateStatus($tr, StorageConfig.Status.IN_PROGRESS); storage.save({ success: function(result) { - self.updateStatus($tr, result.status); - $tr.attr('data-id', result.id); - - if (_.isFunction(callback)) { - callback(storage); + if (concurrentTimer === undefined + || $tr.data('save-timer') === concurrentTimer + ) { + self.updateStatus($tr, result.status, result.statusMessage); + $tr.attr('data-id', result.id); + + if (_.isFunction(callback)) { + callback(storage); + } } }, error: function() { - self.updateStatus($tr, StorageConfig.Status.ERROR); + if (concurrentTimer === undefined + || $tr.data('save-timer') === concurrentTimer + ) { + self.updateStatus($tr, StorageConfig.Status.ERROR); + } } }); }, @@ -971,7 +985,7 @@ MountConfigListView.prototype = _.extend({ this.updateStatus($tr, StorageConfig.Status.IN_PROGRESS); storage.recheck({ success: function(result) { - self.updateStatus($tr, result.status); + self.updateStatus($tr, result.status, result.statusMessage); }, error: function() { self.updateStatus($tr, StorageConfig.Status.ERROR); @@ -984,11 +998,15 @@ MountConfigListView.prototype = _.extend({ * * @param {jQuery} $tr * @param {int} status + * @param {string} message */ - updateStatus: function($tr, status) { + updateStatus: function($tr, status, message) { var $statusSpan = $tr.find('.status span'); $statusSpan.removeClass('loading-small success indeterminate error'); switch (status) { + case null: + // remove status + break; case StorageConfig.Status.IN_PROGRESS: $statusSpan.addClass('loading-small'); break; @@ -1001,6 +1019,7 @@ MountConfigListView.prototype = _.extend({ default: $statusSpan.addClass('error'); } + $statusSpan.attr('data-original-title', (typeof message === 'string') ? message : ''); }, /** diff --git a/apps/files_external/lib/config.php b/apps/files_external/lib/config.php index d9fdb748fcd..56a7e547ec6 100644 --- a/apps/files_external/lib/config.php +++ b/apps/files_external/lib/config.php @@ -269,6 +269,7 @@ class OC_Mount_Config { } } catch (Exception $exception) { \OCP\Util::logException('files_external', $exception); + throw $e; } } return self::STATUS_ERROR; diff --git a/apps/files_external/lib/storageconfig.php b/apps/files_external/lib/storageconfig.php index 70aaa186783..86a7e6ffa12 100644 --- a/apps/files_external/lib/storageconfig.php +++ b/apps/files_external/lib/storageconfig.php @@ -73,6 +73,13 @@ class StorageConfig implements \JsonSerializable { private $status; /** + * Status message + * + * @var string + */ + private $statusMessage; + + /** * Priority * * @var int @@ -295,7 +302,7 @@ class StorageConfig implements \JsonSerializable { } /** - * Sets the storage status, whether the config worked last time + * Gets the storage status, whether the config worked last time * * @return int $status status */ @@ -304,12 +311,23 @@ class StorageConfig implements \JsonSerializable { } /** + * Gets the message describing the storage status + * + * @return string|null + */ + public function getStatusMessage() { + return $this->statusMessage; + } + + /** * Sets the storage status, whether the config worked last time * * @param int $status status + * @param string|null $message optional message */ - public function setStatus($status) { + public function setStatus($status, $message = null) { $this->status = $status; + $this->statusMessage = $message; } /** @@ -341,6 +359,9 @@ class StorageConfig implements \JsonSerializable { if (!is_null($this->status)) { $result['status'] = $this->status; } + if (!is_null($this->statusMessage)) { + $result['statusMessage'] = $this->statusMessage; + } return $result; } } diff --git a/apps/files_external/lib/swift.php b/apps/files_external/lib/swift.php index beb47ce0bce..e946e7feb77 100644 --- a/apps/files_external/lib/swift.php +++ b/apps/files_external/lib/swift.php @@ -106,7 +106,10 @@ class Swift extends \OC\Files\Storage\Common { $this->getContainer()->getPartialObject($path); return true; } catch (ClientErrorResponseException $e) { - \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR); + // Expected response is "404 Not Found", so only log if it isn't + if ($e->getResponse()->getStatusCode() !== 404) { + \OCP\Util::writeLog('files_external', $e->getMessage(), \OCP\Util::ERROR); + } return false; } } diff --git a/apps/files_sharing/js/sharedfilelist.js b/apps/files_sharing/js/sharedfilelist.js index 2e798a92578..edf138b97ae 100644 --- a/apps/files_sharing/js/sharedfilelist.js +++ b/apps/files_sharing/js/sharedfilelist.js @@ -123,6 +123,9 @@ this._reloadCall.abort(); } + // there is only root + this._setCurrentDir('/', false); + var promises = []; var shares = $.ajax({ url: OC.linkToOCS('apps/files_sharing/api/v1') + 'shares', @@ -173,17 +176,14 @@ if (shares[0].ocs && shares[0].ocs.data) { files = files.concat(this._makeFilesFromShares(shares[0].ocs.data)); - } else { - // TODO: error handling } if (remoteShares && remoteShares[0].ocs && remoteShares[0].ocs.data) { files = files.concat(this._makeFilesFromRemoteShares(remoteShares[0].ocs.data)); - } else { - // TODO: error handling } this.setFiles(files); + return true; }, _makeFilesFromRemoteShares: function(data) { diff --git a/apps/provisioning_api/appinfo/app.php b/apps/provisioning_api/appinfo/app.php deleted file mode 100644 index 40d8d5d04d3..00000000000 --- a/apps/provisioning_api/appinfo/app.php +++ /dev/null @@ -1,20 +0,0 @@ -<?php -/** - - * - * @copyright Copyright (c) 2015, ownCloud, Inc. - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ diff --git a/core/command/maintenance/repair.php b/core/command/maintenance/repair.php index 5df362f8111..f7c0cc46048 100644 --- a/core/command/maintenance/repair.php +++ b/core/command/maintenance/repair.php @@ -26,6 +26,7 @@ namespace OC\Core\Command\Maintenance; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; class Repair extends Command { @@ -49,10 +50,24 @@ class Repair extends Command { protected function configure() { $this ->setName('maintenance:repair') - ->setDescription('repair this installation'); + ->setDescription('repair this installation') + ->addOption( + 'include-expensive', + null, + InputOption::VALUE_NONE, + 'Use this option when you want to include resource and load expensive tasks' + ) + ; } protected function execute(InputInterface $input, OutputInterface $output) { + $includeExpensive = $input->getOption('include-expensive'); + if ($includeExpensive) { + foreach ($this->repair->getExpensiveRepairSteps() as $step) { + $this->repair->addStep($step); + } + } + $maintenanceMode = $this->config->getSystemValue('maintenance', false); $this->config->setSystemValue('maintenance', true); diff --git a/core/js/tests/specs/sharedialogshareelistview.js b/core/js/tests/specs/sharedialogshareelistview.js new file mode 100644 index 00000000000..d468ce790dc --- /dev/null +++ b/core/js/tests/specs/sharedialogshareelistview.js @@ -0,0 +1,158 @@ +/** + * ownCloud + * + * @author Tom Needham + * @copyright 2015 Tom Needham <tom@owncloud.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ + +/* global oc_appconfig */ +describe('OC.Share.ShareDialogShareeListView', function () { + + var oldCurrentUser; + var fileInfoModel; + var configModel; + var shareModel; + var listView; + var setPermissionsStub; + + beforeEach(function () { + /* jshint camelcase:false */ + oldAppConfig = _.extend({}, oc_appconfig.core); + oc_appconfig.core.enforcePasswordForPublicLink = false; + + $('#testArea').append('<input id="mailPublicNotificationEnabled" name="mailPublicNotificationEnabled" type="hidden" value="yes">'); + + fileInfoModel = new OCA.Files.FileInfoModel({ + id: 123, + name: 'shared_file_name.txt', + path: '/subdir', + size: 100, + mimetype: 'text/plain', + permissions: 31, + sharePermissions: 31 + }); + + var attributes = { + itemType: fileInfoModel.isDirectory() ? 'folder' : 'file', + itemSource: fileInfoModel.get('id'), + possiblePermissions: 31, + permissions: 31 + }; + + shareModel = new OC.Share.ShareItemModel(attributes, { + configModel: configModel, + fileInfoModel: fileInfoModel + }); + + configModel = new OC.Share.ShareConfigModel({ + enforcePasswordForPublicLink: false, + isResharingAllowed: true, + enforcePasswordForPublicLink: false, + isDefaultExpireDateEnabled: false, + isDefaultExpireDateEnforced: false, + defaultExpireDate: 7 + }); + + listView = new OC.Share.ShareDialogShareeListView({ + configModel: configModel, + model: shareModel + }); + + // required for proper event propagation when simulating clicks in some cases (jquery bugs) + $('#testArea').append(listView.$el); + + shareModel.set({ + linkShare: {isLinkShare: false} + }); + + oldCurrentUser = OC.currentUser; + OC.currentUser = 'user0'; + setPermissionsStub = sinon.stub(listView.model, 'setPermissions'); + }); + + afterEach(function () { + OC.currentUser = oldCurrentUser; + /* jshint camelcase:false */ + oc_appconfig.core = oldAppConfig; + listView.remove(); + setPermissionsStub.restore(); + }); + + describe('Manages checkbox events correctly', function () { + it('Checks cruds boxes when edit box checked', function () { + shareModel.set('shares', [{ + id: 100, + item_source: 123, + permissions: 1, + share_type: OC.Share.SHARE_TYPE_USER, + share_with: 'user1', + share_with_displayname: 'User One' + }]); + listView.render(); + listView.$el.find("input[name='edit']").click(); + expect(listView.$el.find("input[name='update']").is(':checked')).toEqual(true); + expect(setPermissionsStub.called).toEqual(true); + }); + + it('Checks edit box when create/update/delete are checked', function () { + shareModel.set('shares', [{ + id: 100, + item_source: 123, + permissions: 1, + share_type: OC.Share.SHARE_TYPE_USER, + share_with: 'user1', + share_with_displayname: 'User One' + }]); + listView.render(); + listView.$el.find("input[name='update']").click(); + expect(listView.$el.find("input[name='edit']").is(':checked')).toEqual(true); + expect(setPermissionsStub.called).toEqual(true); + }); + + it('shows cruds checkboxes when toggled', function () { + shareModel.set('shares', [{ + id: 100, + item_source: 123, + permissions: 1, + share_type: OC.Share.SHARE_TYPE_USER, + share_with: 'user1', + share_with_displayname: 'User One' + }]); + listView.render(); + listView.$el.find('a.showCruds').click(); + expect(listView.$el.find('li.cruds').hasClass('hidden')).toEqual(false); + }); + + it('sends notification to user when checkbox clicked', function () { + shareModel.set('shares', [{ + id: 100, + item_source: 123, + permissions: 1, + share_type: OC.Share.SHARE_TYPE_USER, + share_with: 'user1', + share_with_displayname: 'User One' + }]); + listView.render(); + var notificationStub = sinon.stub(listView.model, 'sendNotificationForShare'); + listView.$el.find("input[name='mailNotification']").click(); + expect(notificationStub.called).toEqual(true); + notificationStub.restore(); + }); + + }); + +}); diff --git a/core/templates/internalaltmail.php b/core/templates/internalaltmail.php new file mode 100644 index 00000000000..38531d109b7 --- /dev/null +++ b/core/templates/internalaltmail.php @@ -0,0 +1,13 @@ +<?php +print_unescaped($l->t("Hey there,\n\njust letting you know that %s shared %s with you.\nView it: %s\n\n", array($_['user_displayname'], $_['filename'], $_['link']))); +if ( isset($_['expiration']) ) { + print_unescaped($l->t("The share will expire on %s.", array($_['expiration']))); + print_unescaped("\n\n"); +} +// TRANSLATORS term at the end of a mail +p($l->t("Cheers!")); +?> + +-- +<?php p($theme->getName() . ' - ' . $theme->getSlogan()); ?> +<?php print_unescaped("\n".$theme->getBaseUrl()); diff --git a/core/templates/internalmail.php b/core/templates/internalmail.php new file mode 100644 index 00000000000..0e73a601857 --- /dev/null +++ b/core/templates/internalmail.php @@ -0,0 +1,39 @@ +<table cellspacing="0" cellpadding="0" border="0" width="100%"> +<tr><td> +<table cellspacing="0" cellpadding="0" border="0" width="600px"> +<tr> +<td bgcolor="<?php p($theme->getMailHeaderColor());?>" width="20px"> </td> +<td bgcolor="<?php p($theme->getMailHeaderColor());?>"> +<img src="<?php p(OC_Helper::makeURLAbsolute(image_path('', 'logo-mail.gif'))); ?>" alt="<?php p($theme->getName()); ?>"/> +</td> +</tr> +<tr><td colspan="2"> </td></tr> +<tr> +<td width="20px"> </td> +<td style="font-weight:normal; font-size:0.8em; line-height:1.2em; font-family:verdana,'arial',sans;"> +<?php +print_unescaped($l->t('Hey there,<br><br>just letting you know that %s shared <strong>%s</strong> with you.<br><a href="%s">View it!</a><br><br>', array($_['user_displayname'], $_['filename'], $_['link']))); +if ( isset($_['expiration']) ) { + p($l->t("The share will expire on %s.", array($_['expiration']))); + print_unescaped('<br><br>'); +} +// TRANSLATORS term at the end of a mail +p($l->t('Cheers!')); +?> +</td> +</tr> +<tr><td colspan="2"> </td></tr> +<tr> +<td width="20px"> </td> +<td style="font-weight:normal; font-size:0.8em; line-height:1.2em; font-family:verdana,'arial',sans;">--<br> +<?php p($theme->getName()); ?> - +<?php p($theme->getSlogan()); ?> +<br><a href="<?php p($theme->getBaseUrl()); ?>"><?php p($theme->getBaseUrl());?></a> +</td> +</tr> +<tr> +<td colspan="2"> </td> +</tr> +</table> +</td></tr> +</table> diff --git a/lib/private/files/cache/cache.php b/lib/private/files/cache/cache.php index f3e22701f40..231dbe37695 100644 --- a/lib/private/files/cache/cache.php +++ b/lib/private/files/cache/cache.php @@ -313,6 +313,14 @@ class Cache { $fields = array( 'path', 'parent', 'name', 'mimetype', 'size', 'mtime', 'storage_mtime', 'encrypted', 'etag', 'permissions'); + + $doNotCopyStorageMTime = false; + if (array_key_exists('mtime', $data) && $data['mtime'] === null) { + // this horrific magic tells it to not copy storage_mtime to mtime + unset($data['mtime']); + $doNotCopyStorageMTime = true; + } + $params = array(); $queryParts = array(); foreach ($data as $name => $value) { @@ -325,7 +333,7 @@ class Cache { $queryParts[] = '`mimepart`'; $value = $this->mimetypeLoader->getId($value); } elseif ($name === 'storage_mtime') { - if (!isset($data['mtime'])) { + if (!$doNotCopyStorageMTime && !isset($data['mtime'])) { $params[] = $value; $queryParts[] = '`mtime`'; } diff --git a/lib/private/files/cache/updater.php b/lib/private/files/cache/updater.php index 4cc5f511565..85dcc8589de 100644 --- a/lib/private/files/cache/updater.php +++ b/lib/private/files/cache/updater.php @@ -194,12 +194,26 @@ class Updater { $targetCache->correctFolderSize($targetInternalPath); $this->correctParentStorageMtime($sourceStorage, $sourceInternalPath); $this->correctParentStorageMtime($targetStorage, $targetInternalPath); + $this->updateStorageMTimeOnly($targetStorage, $targetInternalPath); $this->propagator->addChange($source); $this->propagator->addChange($target); $this->propagator->propagateChanges(); } } + private function updateStorageMTimeOnly($storage, $internalPath) { + $cache = $storage->getCache(); + $fileId = $cache->getId($internalPath); + if ($fileId !== -1) { + $cache->update( + $fileId, [ + 'mtime' => null, // this magic tells it to not overwrite mtime + 'storage_mtime' => $storage->filemtime($internalPath) + ] + ); + } + } + /** * update the storage_mtime of the direct parent in the cache to the mtime from the storage * diff --git a/lib/private/repair.php b/lib/private/repair.php index 20219e313fd..f6ac7ebe65b 100644 --- a/lib/private/repair.php +++ b/lib/private/repair.php @@ -34,6 +34,7 @@ use OC\Repair\AssetCache; use OC\Repair\CleanTags; use OC\Repair\Collation; use OC\Repair\DropOldJobs; +use OC\Repair\OldGroupMembershipShares; use OC\Repair\RemoveGetETagEntries; use OC\Repair\SqliteAutoincrement; use OC\Repair\DropOldTables; @@ -119,6 +120,18 @@ class Repair extends BasicEmitter { } /** + * Returns expensive repair steps to be run on the + * command line with a special option. + * + * @return array of RepairStep instances + */ + public static function getExpensiveRepairSteps() { + return [ + new OldGroupMembershipShares(\OC::$server->getDatabaseConnection(), \OC::$server->getGroupManager()), + ]; + } + + /** * Returns the repair steps to be run before an * upgrade. * diff --git a/lib/private/share/mailnotifications.php b/lib/private/share/mailnotifications.php index 8056260bf17..2797e5ed99b 100644 --- a/lib/private/share/mailnotifications.php +++ b/lib/private/share/mailnotifications.php @@ -136,7 +136,7 @@ class MailNotifications { $link = \OCP\Util::linkToAbsolute('files', 'index.php', $args); - list($htmlBody, $textBody) = $this->createMailBody($filename, $link, $expiration); + list($htmlBody, $textBody) = $this->createMailBody($filename, $link, $expiration, 'internal'); // send it out now try { @@ -210,20 +210,20 @@ class MailNotifications { * @param string $filename the shared file * @param string $link link to the shared file * @param int $expiration expiration date (timestamp) + * @param bool $prefix prefix of mail template files * @return array an array of the html mail body and the plain text mail body */ - private function createMailBody($filename, $link, $expiration) { - + private function createMailBody($filename, $link, $expiration, $prefix = '') { $formattedDate = $expiration ? $this->l->l('date', $expiration) : null; - $html = new \OC_Template("core", "mail", ""); + $html = new \OC_Template('core', $prefix . 'mail', ''); $html->assign ('link', $link); $html->assign ('user_displayname', $this->senderDisplayName); $html->assign ('filename', $filename); $html->assign('expiration', $formattedDate); $htmlMail = $html->fetchPage(); - $plainText = new \OC_Template("core", "altmail", ""); + $plainText = new \OC_Template('core', $prefix . 'altmail', ''); $plainText->assign ('link', $link); $plainText->assign ('user_displayname', $this->senderDisplayName); $plainText->assign ('filename', $filename); diff --git a/lib/repair/oldgroupmembershipshares.php b/lib/repair/oldgroupmembershipshares.php new file mode 100644 index 00000000000..2d701ac9fb7 --- /dev/null +++ b/lib/repair/oldgroupmembershipshares.php @@ -0,0 +1,117 @@ +<?php +/** + * @author Joas Schilling <nickvergessen@owncloud.com> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OC\Repair; + + +use OC\Hooks\BasicEmitter; +use OC\RepairStep; +use OCP\IDBConnection; +use OCP\IGroupManager; +use OCP\Share; + +class OldGroupMembershipShares extends BasicEmitter implements RepairStep { + + /** @var \OCP\IDBConnection */ + protected $connection; + + /** @var \OCP\IGroupManager */ + protected $groupManager; + + /** + * @var array [gid => [uid => (bool)]] + */ + protected $memberships; + + /** + * @param IDBConnection $connection + * @param IGroupManager $groupManager + */ + public function __construct(IDBConnection $connection, IGroupManager $groupManager) { + $this->connection = $connection; + $this->groupManager = $groupManager; + } + + /** + * Returns the step's name + * + * @return string + */ + public function getName() { + return 'Remove shares of old group memberships'; + } + + /** + * Run repair step. + * Must throw exception on error. + * + * @throws \Exception in case of failure + */ + public function run() { + $deletedEntries = 0; + + $query = $this->connection->getQueryBuilder(); + $query->select(['s1.id', $query->createFunction('s1.`share_with` AS `user`'), $query->createFunction('s2.`share_with` AS `group`')]) + ->from('share', 's1') + ->where($query->expr()->isNotNull('s1.parent')) + // \OC\Share\Constant::$shareTypeGroupUserUnique === 2 + ->andWhere($query->expr()->eq('s1.share_type', $query->expr()->literal(2))) + ->andWhere($query->expr()->isNotNull('s2.id')) + ->andWhere($query->expr()->eq('s2.share_type', $query->expr()->literal(Share::SHARE_TYPE_GROUP))) + ->leftJoin('s1', 'share', 's2', $query->expr()->eq('s1.parent', 's2.id')); + + $deleteQuery = $this->connection->getQueryBuilder(); + $deleteQuery->delete('share') + ->where($query->expr()->eq('id', $deleteQuery->createParameter('share'))); + + $result = $query->execute(); + while ($row = $result->fetch()) { + if (!$this->isMember($row['group'], $row['user'])) { + $deletedEntries += $deleteQuery->setParameter('share', (int) $row['id']) + ->execute(); + } + } + $result->closeCursor(); + + if ($deletedEntries) { + $this->emit('\OC\Repair', 'info', array('Removed ' . $deletedEntries . ' shares where user is not a member of the group anymore')); + } + } + + /** + * @param string $gid + * @param string $uid + * @return bool + */ + protected function isMember($gid, $uid) { + if (isset($this->memberships[$gid][$uid])) { + return $this->memberships[$gid][$uid]; + } + + $isMember = $this->groupManager->isInGroup($uid, $gid); + if (!isset($this->memberships[$gid])) { + $this->memberships[$gid] = []; + } + $this->memberships[$gid][$uid] = $isMember; + + return $isMember; + } +} diff --git a/lib/repair/repairinvalidshares.php b/lib/repair/repairinvalidshares.php index 4b0aeb70c12..5a4cb445ce9 100644 --- a/lib/repair/repairinvalidshares.php +++ b/lib/repair/repairinvalidshares.php @@ -70,11 +70,43 @@ class RepairInvalidShares extends BasicEmitter implements \OC\RepairStep { } } + /** + * Remove shares where the parent share does not exist anymore + */ + private function removeSharesNonExistingParent() { + $deletedEntries = 0; + + $query = $this->connection->getQueryBuilder(); + $query->select('s1.parent') + ->from('share', 's1') + ->where($query->expr()->isNotNull('s1.parent')) + ->andWhere($query->expr()->isNull('s2.id')) + ->leftJoin('s1', 'share', 's2', $query->expr()->eq('s1.parent', 's2.id')) + ->groupBy('s1.parent'); + + $deleteQuery = $this->connection->getQueryBuilder(); + $deleteQuery->delete('share') + ->where($query->expr()->eq('parent', $deleteQuery->createParameter('parent'))); + + $result = $query->execute(); + while ($row = $result->fetch()) { + $deletedEntries += $deleteQuery->setParameter('parent', (int) $row['parent']) + ->execute(); + } + $result->closeCursor(); + + if ($deletedEntries) { + $this->emit('\OC\Repair', 'info', array('Removed ' . $deletedEntries . ' shares where the parent did not exist')); + } + } + public function run() { $ocVersionFromBeforeUpdate = $this->config->getSystemValue('version', '0.0.0'); if (version_compare($ocVersionFromBeforeUpdate, '8.2.0.7', '<')) { // this situation was only possible before 8.2 $this->removeExpirationDateFromNonLinkShares(); } + + $this->removeSharesNonExistingParent(); } } diff --git a/resources/config/ca-bundle.crt b/resources/config/ca-bundle.crt index c93d3c4d4a7..21e90911fd3 100644 --- a/resources/config/ca-bundle.crt +++ b/resources/config/ca-bundle.crt @@ -1,7 +1,7 @@ ## ## Bundle of CA Root Certificates ## -## Certificate data from Mozilla as of: Mon Apr 27 08:58:04 2015 +## Certificate data from Mozilla as of: Wed Oct 28 22:42:42 2015 ## ## This is a bundle of X.509 certificates of public Certificate Authorities ## (CA). These were automatically extracted from Mozilla's root certificates @@ -14,7 +14,7 @@ ## Just configure this file as the SSLCACertificateFile. ## ## Conversion done with mk-ca-bundle.pl version 1.25. -## SHA1: ed3c0bbfb7912bcc00cd2033b0cb85c98d10559c +## SHA1: 6d7d2f0a4fae587e7431be191a081ac1257d300a ## @@ -1142,29 +1142,6 @@ vFcj4jjSm2jzVhKIT0J8uDHEtdvkyCE06UgRNe76x5JXxZ805Mf29w4LTJxoeHtxMcfrHuBnQfO3 oKfN5XozNmr6mis= -----END CERTIFICATE----- -TURKTRUST Certificate Services Provider Root 1 -============================================== ------BEGIN CERTIFICATE----- -MIID+zCCAuOgAwIBAgIBATANBgkqhkiG9w0BAQUFADCBtzE/MD0GA1UEAww2VMOcUktUUlVTVCBF -bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxMQswCQYDVQQGDAJUUjEP -MA0GA1UEBwwGQU5LQVJBMVYwVAYDVQQKDE0oYykgMjAwNSBUw5xSS1RSVVNUIEJpbGdpIMSwbGV0 -acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLjAeFw0wNTA1MTMx -MDI3MTdaFw0xNTAzMjIxMDI3MTdaMIG3MT8wPQYDVQQDDDZUw5xSS1RSVVNUIEVsZWt0cm9uaWsg -U2VydGlmaWthIEhpem1ldCBTYcSfbGF5xLFjxLFzxLExCzAJBgNVBAYMAlRSMQ8wDQYDVQQHDAZB -TktBUkExVjBUBgNVBAoMTShjKSAyMDA1IFTDnFJLVFJVU1QgQmlsZ2kgxLBsZXRpxZ9pbSB2ZSBC -aWxpxZ9pbSBHw7x2ZW5sacSfaSBIaXptZXRsZXJpIEEuxZ4uMIIBIjANBgkqhkiG9w0BAQEFAAOC -AQ8AMIIBCgKCAQEAylIF1mMD2Bxf3dJ7XfIMYGFbazt0K3gNfUW9InTojAPBxhEqPZW8qZSwu5GX -yGl8hMW0kWxsE2qkVa2kheiVfrMArwDCBRj1cJ02i67L5BuBf5OI+2pVu32Fks66WJ/bMsW9Xe8i -Si9BB35JYbOG7E6mQW6EvAPs9TscyB/C7qju6hJKjRTP8wrgUDn5CDX4EVmt5yLqS8oUBt5CurKZ -8y1UiBAG6uEaPj1nH/vO+3yC6BFdSsG5FOpU2WabfIl9BJpiyelSPJ6c79L1JuTm5Rh8i27fbMx4 -W09ysstcP4wFjdFMjK2Sx+F4f2VsSQZQLJ4ywtdKxnWKWU51b0dewQIDAQABoxAwDjAMBgNVHRME -BTADAQH/MA0GCSqGSIb3DQEBBQUAA4IBAQAV9VX/N5aAWSGk/KEVTCD21F/aAyT8z5Aa9CEKmu46 -sWrv7/hg0Uw2ZkUd82YCdAR7kjCo3gp2D++Vbr3JN+YaDayJSFvMgzbC9UZcWYJWtNX+I7TYVBxE -q8Sn5RTOPEFhfEPmzcSBCYsk+1Ql1haolgxnB2+zUEfjHCQo3SqYpGH+2+oSN7wBGjSFvW5P55Fy -B0SFHljKVETd96y5y4khctuPwGkplyqjrhgjlxxBKot8KsF8kOipKMDTkcatKIdAaLX/7KfS0zgY -nNN9aV3wxqUeJBujR/xpB2jn5Jq07Q+hh4cCzofSSE7hvP/L8XKSRGQDJereW26fyfJOrN3H ------END CERTIFICATE----- - TURKTRUST Certificate Services Provider Root 2 ============================================== -----BEGIN CERTIFICATE----- @@ -1589,56 +1566,6 @@ PBS1xp81HlDQwY9qcEQCYsuuHWhBp6pX6FOqB9IG9tUUBguRA3UsbHK1YZWaDYu5Def131TN3ubY WyH8EZE0vkHve52Xdf+XlcCWWC/qu0bXu+TZLg== -----END CERTIFICATE----- -TC TrustCenter Class 2 CA II -============================ ------BEGIN CERTIFICATE----- -MIIEqjCCA5KgAwIBAgIOLmoAAQACH9dSISwRXDswDQYJKoZIhvcNAQEFBQAwdjELMAkGA1UEBhMC -REUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxIjAgBgNVBAsTGVRDIFRydXN0Q2VudGVy -IENsYXNzIDIgQ0ExJTAjBgNVBAMTHFRDIFRydXN0Q2VudGVyIENsYXNzIDIgQ0EgSUkwHhcNMDYw -MTEyMTQzODQzWhcNMjUxMjMxMjI1OTU5WjB2MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMgVHJ1 -c3RDZW50ZXIgR21iSDEiMCAGA1UECxMZVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQTElMCMGA1UE -AxMcVEMgVHJ1c3RDZW50ZXIgQ2xhc3MgMiBDQSBJSTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC -AQoCggEBAKuAh5uO8MN8h9foJIIRszzdQ2Lu+MNF2ujhoF/RKrLqk2jftMjWQ+nEdVl//OEd+DFw -IxuInie5e/060smp6RQvkL4DUsFJzfb95AhmC1eKokKguNV/aVyQMrKXDcpK3EY+AlWJU+MaWss2 -xgdW94zPEfRMuzBwBJWl9jmM/XOBCH2JXjIeIqkiRUuwZi4wzJ9l/fzLganx4Duvo4bRierERXlQ -Xa7pIXSSTYtZgo+U4+lK8edJsBTj9WLL1XK9H7nSn6DNqPoByNkN39r8R52zyFTfSUrxIan+GE7u -SNQZu+995OKdy1u2bv/jzVrndIIFuoAlOMvkaZ6vQaoahPUCAwEAAaOCATQwggEwMA8GA1UdEwEB -/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBTjq1RMgKHbVkO3kUrL84J6E1wIqzCB -7QYDVR0fBIHlMIHiMIHfoIHcoIHZhjVodHRwOi8vd3d3LnRydXN0Y2VudGVyLmRlL2NybC92Mi90 -Y19jbGFzc18yX2NhX0lJLmNybIaBn2xkYXA6Ly93d3cudHJ1c3RjZW50ZXIuZGUvQ049VEMlMjBU -cnVzdENlbnRlciUyMENsYXNzJTIwMiUyMENBJTIwSUksTz1UQyUyMFRydXN0Q2VudGVyJTIwR21i -SCxPVT1yb290Y2VydHMsREM9dHJ1c3RjZW50ZXIsREM9ZGU/Y2VydGlmaWNhdGVSZXZvY2F0aW9u -TGlzdD9iYXNlPzANBgkqhkiG9w0BAQUFAAOCAQEAjNfffu4bgBCzg/XbEeprS6iSGNn3Bzn1LL4G -dXpoUxUc6krtXvwjshOg0wn/9vYua0Fxec3ibf2uWWuFHbhOIprtZjluS5TmVfwLG4t3wVMTZonZ -KNaL80VKY7f9ewthXbhtvsPcW3nS7Yblok2+XnR8au0WOB9/WIFaGusyiC2y8zl3gK9etmF1Kdsj -TYjKUCjLhdLTEKJZbtOTVAB6okaVhgWcqRmY5TFyDADiZ9lA4CQze28suVyrZZ0srHbqNZn1l7kP -JOzHdiEoZa5X6AeIdUpWoNIFOqTmjZKILPPy4cHGYdtBxceb9w4aUUXCYWvcZCcXjFq32nQozZfk -vQ== ------END CERTIFICATE----- - -TC TrustCenter Universal CA I -============================= ------BEGIN CERTIFICATE----- -MIID3TCCAsWgAwIBAgIOHaIAAQAC7LdggHiNtgYwDQYJKoZIhvcNAQEFBQAweTELMAkGA1UEBhMC -REUxHDAaBgNVBAoTE1RDIFRydXN0Q2VudGVyIEdtYkgxJDAiBgNVBAsTG1RDIFRydXN0Q2VudGVy -IFVuaXZlcnNhbCBDQTEmMCQGA1UEAxMdVEMgVHJ1c3RDZW50ZXIgVW5pdmVyc2FsIENBIEkwHhcN -MDYwMzIyMTU1NDI4WhcNMjUxMjMxMjI1OTU5WjB5MQswCQYDVQQGEwJERTEcMBoGA1UEChMTVEMg -VHJ1c3RDZW50ZXIgR21iSDEkMCIGA1UECxMbVEMgVHJ1c3RDZW50ZXIgVW5pdmVyc2FsIENBMSYw -JAYDVQQDEx1UQyBUcnVzdENlbnRlciBVbml2ZXJzYWwgQ0EgSTCCASIwDQYJKoZIhvcNAQEBBQAD -ggEPADCCAQoCggEBAKR3I5ZEr5D0MacQ9CaHnPM42Q9e3s9B6DGtxnSRJJZ4Hgmgm5qVSkr1YnwC -qMqs+1oEdjneX/H5s7/zA1hV0qq34wQi0fiU2iIIAI3TfCZdzHd55yx4Oagmcw6iXSVphU9VDprv -xrlE4Vc93x9UIuVvZaozhDrzznq+VZeujRIPFDPiUHDDSYcTvFHe15gSWu86gzOSBnWLknwSaHtw -ag+1m7Z3W0hZneTvWq3zwZ7U10VOylY0Ibw+F1tvdwxIAUMpsN0/lm7mlaoMwCC2/T42J5zjXM9O -gdwZu5GQfezmlwQek8wiSdeXhrYTCjxDI3d+8NzmzSQfO4ObNDqDNOMCAwEAAaNjMGEwHwYDVR0j -BBgwFoAUkqR1LKSevoFE63n8isWVpesQdXMwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC -AYYwHQYDVR0OBBYEFJKkdSyknr6BROt5/IrFlaXrEHVzMA0GCSqGSIb3DQEBBQUAA4IBAQAo0uCG -1eb4e/CX3CJrO5UUVg8RMKWaTzqwOuAGy2X17caXJ/4l8lfmXpWMPmRgFVp/Lw0BxbFg/UU1z/Cy -vwbZ71q+s2IhtNerNXxTPqYn8aEt2hojnczd7Dwtnic0XQ/CNnm8yUpiLe1r2X1BQ3y2qsrtYbE3 -ghUJGooWMNjsydZHcnhLEEYUjl8Or+zHL6sQ17bxbuyGssLoDZJz3KL0Dzq/YSMQiZxIQG5wALPT -ujdEWBF6AmqI8Dc08BnprNRlc/ZpjGSUOnmFKbAWKwyCPwacx/0QK54PLLae4xW/2TYcuiUaUj0a -7CIMHOCkoj3w6DnPgcB77V0fb8XQC9eY ------END CERTIFICATE----- - Deutsche Telekom Root CA 2 ========================== -----BEGIN CERTIFICATE----- @@ -1661,28 +1588,6 @@ dyd1Lx+4ivn+xbrYNuSD7Odlt79jWvNGr4GUN9RBjNYj1h7P9WgbRGOiWrqnNVmh5XAFmw4jV5mU Cm26OWMohpLzGITY+9HPBVZkVw== -----END CERTIFICATE----- -ComSign Secured CA -================== ------BEGIN CERTIFICATE----- -MIIDqzCCApOgAwIBAgIRAMcoRwmzuGxFjB36JPU2TukwDQYJKoZIhvcNAQEFBQAwPDEbMBkGA1UE -AxMSQ29tU2lnbiBTZWN1cmVkIENBMRAwDgYDVQQKEwdDb21TaWduMQswCQYDVQQGEwJJTDAeFw0w -NDAzMjQxMTM3MjBaFw0yOTAzMTYxNTA0NTZaMDwxGzAZBgNVBAMTEkNvbVNpZ24gU2VjdXJlZCBD -QTEQMA4GA1UEChMHQ29tU2lnbjELMAkGA1UEBhMCSUwwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw -ggEKAoIBAQDGtWhfHZQVw6QIVS3joFd67+l0Kru5fFdJGhFeTymHDEjWaueP1H5XJLkGieQcPOqs -49ohgHMhCu95mGwfCP+hUH3ymBvJVG8+pSjsIQQPRbsHPaHA+iqYHU4Gk/v1iDurX8sWv+bznkqH -7Rnqwp9D5PGBpX8QTz7RSmKtUxvLg/8HZaWSLWapW7ha9B20IZFKF3ueMv5WJDmyVIRD9YTC2LxB -kMyd1mja6YJQqTtoz7VdApRgFrFD2UNd3V2Hbuq7s8lr9gOUCXDeFhF6K+h2j0kQmHe5Y1yLM5d1 -9guMsqtb3nQgJT/j8xH5h2iGNXHDHYwt6+UarA9z1YJZQIDTAgMBAAGjgacwgaQwDAYDVR0TBAUw -AwEB/zBEBgNVHR8EPTA7MDmgN6A1hjNodHRwOi8vZmVkaXIuY29tc2lnbi5jby5pbC9jcmwvQ29t -U2lnblNlY3VyZWRDQS5jcmwwDgYDVR0PAQH/BAQDAgGGMB8GA1UdIwQYMBaAFMFL7XC29z58ADsA -j8c+DkWfHl3sMB0GA1UdDgQWBBTBS+1wtvc+fAA7AI/HPg5Fnx5d7DANBgkqhkiG9w0BAQUFAAOC -AQEAFs/ukhNQq3sUnjO2QiBq1BW9Cav8cujvR3qQrFHBZE7piL1DRYHjZiM/EoZNGeQFsOY3wo3a -BijJD4mkU6l1P7CW+6tMM1X5eCZGbxs2mPtCdsGCuY7e+0X5YxtiOzkGynd6qDwJz2w2PQ8KRUtp -FhpFfTMDZflScZAmlaxMDPWLkz/MdXSFmLr/YnpNH4n+rr2UAJm/EaXc4HnFFgt9AmEd6oX5AhVP -51qJThRv4zdLhfXBPGHg/QVBspJ/wx2g0K5SZGBrGMYmnNj1ZOQ2GmKfig8+/21OGVZOIJFsnzQz -OjRXUDpvgV4GxvU+fE6OK85lBi5d0ipTdF7Tbieejw== ------END CERTIFICATE----- - Cybertrust Global Root ====================== -----BEGIN CERTIFICATE----- @@ -1784,26 +1689,6 @@ fZleHwzoq0QkKXJAPTZSr4xYkHPB7GEseaHsh7U/2k3ZIQAw3pDaDtMaSKk+hQsUi4y8QZ5q9w5w wDX3OaJdZtB7WZ+oRxKaJyOkLY4ng5IgodcVf/EuGO70SH8vf/GhGLWhC5SgYiAynB321O+/TIho -----END CERTIFICATE----- -Buypass Class 3 CA 1 -==================== ------BEGIN CERTIFICATE----- -MIIDUzCCAjugAwIBAgIBAjANBgkqhkiG9w0BAQUFADBLMQswCQYDVQQGEwJOTzEdMBsGA1UECgwU -QnV5cGFzcyBBUy05ODMxNjMzMjcxHTAbBgNVBAMMFEJ1eXBhc3MgQ2xhc3MgMyBDQSAxMB4XDTA1 -MDUwOTE0MTMwM1oXDTE1MDUwOTE0MTMwM1owSzELMAkGA1UEBhMCTk8xHTAbBgNVBAoMFEJ1eXBh -c3MgQVMtOTgzMTYzMzI3MR0wGwYDVQQDDBRCdXlwYXNzIENsYXNzIDMgQ0EgMTCCASIwDQYJKoZI -hvcNAQEBBQADggEPADCCAQoCggEBAKSO13TZKWTeXx+HgJHqTjnmGcZEC4DVC69TB4sSveZn8AKx -ifZgisRbsELRwCGoy+Gb72RRtqfPFfV0gGgEkKBYouZ0plNTVUhjP5JW3SROjvi6K//zNIqeKNc0 -n6wv1g/xpC+9UrJJhW05NfBEMJNGJPO251P7vGGvqaMU+8IXF4Rs4HyI+MkcVyzwPX6UvCWThOia -AJpFBUJXgPROztmuOfbIUxAMZTpHe2DC1vqRycZxbL2RhzyRhkmr8w+gbCZ2Xhysm3HljbybIR6c -1jh+JIAVMYKWsUnTYjdbiAwKYjT+p0h+mbEwi5A3lRyoH6UsjfRVyNvdWQrCrXig9IsCAwEAAaNC -MEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUOBTmyPCppAP0Tj4io1vy1uCtQHQwDgYDVR0P -AQH/BAQDAgEGMA0GCSqGSIb3DQEBBQUAA4IBAQABZ6OMySU9E2NdFm/soT4JXJEVKirZgCFPBdy7 -pYmrEzMqnji3jG8CcmPHc3ceCQa6Oyh7pEfJYWsICCD8igWKH7y6xsL+z27sEzNxZy5p+qksP2bA -EllNC1QCkoS72xLvg3BweMhT+t/Gxv/ciC8HwEmdMldg0/L2mSlf56oBzKwzqBwKu5HEA6BvtjT5 -htOzdlSY9EqBs1OdTUDs5XcTRa9bqh/YL0yCe/4qxFi7T/ye/QNlGioOw6UgFpRreaaiErS7GqQj -el/wroQk5PMr+4okoyeYZdowdXb8GZHo2+ubPzK/QJcHJrrM85SFSnonk8+QQtS4Wxam58tAA915 ------END CERTIFICATE----- - EBG Elektronik Sertifika Hizmet Sa\xC4\x9Flay\xc4\xb1\x63\xc4\xb1s\xc4\xb1 ========================================================================== -----BEGIN CERTIFICATE----- @@ -3985,4 +3870,83 @@ aH9dCi77o0cOPaYjesYBx4/IXr9tgFa+iiS6M+qf4TIRnvHST4D2G0CvOJ4RUHlzEhLN5mydLIhy PDCBBpEi6lmt2hkuIsKNuYyH4Ga8cyNfIWRjgEj1oDwYPZTISEEdQLpe/v5WOaHIz16eGWRGENoX kbcFgKyLmZJ956LYBws2J+dIeWCKw9cTXPhyQN9Ky8+ZAAoACxGV2lZFA4gKn2fQ1XmxqI1AbQ3C ekD6819kR5LLU7m7Wc5P/dAVUwHY3+vZ5nbv0CO7O6l5s9UCKc2Jo5YPSjXnTkLAdc0Hz+Ys63su ------END CERTIFICATE-----
\ No newline at end of file +-----END CERTIFICATE----- + +TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı H5 +========================================================= +-----BEGIN CERTIFICATE----- +MIIEJzCCAw+gAwIBAgIHAI4X/iQggTANBgkqhkiG9w0BAQsFADCBsTELMAkGA1UEBhMCVFIxDzAN +BgNVBAcMBkFua2FyYTFNMEsGA1UECgxEVMOcUktUUlVTVCBCaWxnaSDEsGxldGnFn2ltIHZlIEJp +bGnFn2ltIEfDvHZlbmxpxJ9pIEhpem1ldGxlcmkgQS7Fni4xQjBABgNVBAMMOVTDnFJLVFJVU1Qg +RWxla3Ryb25payBTZXJ0aWZpa2EgSGl6bWV0IFNhxJ9sYXnEsWPEsXPEsSBINTAeFw0xMzA0MzAw +ODA3MDFaFw0yMzA0MjgwODA3MDFaMIGxMQswCQYDVQQGEwJUUjEPMA0GA1UEBwwGQW5rYXJhMU0w +SwYDVQQKDERUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmlsacWfaW0gR8O8dmVubGnE +n2kgSGl6bWV0bGVyaSBBLsWeLjFCMEAGA1UEAww5VMOcUktUUlVTVCBFbGVrdHJvbmlrIFNlcnRp +ZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIEg1MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEApCUZ4WWe60ghUEoI5RHwWrom/4NZzkQqL/7hzmAD/I0Dpe3/a6i6zDQGn1k19uwsu537 +jVJp45wnEFPzpALFp/kRGml1bsMdi9GYjZOHp3GXDSHHmflS0yxjXVW86B8BSLlg/kJK9siArs1m +ep5Fimh34khon6La8eHBEJ/rPCmBp+EyCNSgBbGM+42WAA4+Jd9ThiI7/PS98wl+d+yG6w8z5UNP +9FR1bSmZLmZaQ9/LXMrI5Tjxfjs1nQ/0xVqhzPMggCTTV+wVunUlm+hkS7M0hO8EuPbJbKoCPrZV +4jI3X/xml1/N1p7HIL9Nxqw/dV8c7TKcfGkAaZHjIxhT6QIDAQABo0IwQDAdBgNVHQ4EFgQUVpkH +HtOsDGlktAxQR95DLL4gwPswDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI +hvcNAQELBQADggEBAJ5FdnsXSDLyOIspve6WSk6BGLFRRyDN0GSxDsnZAdkJzsiZ3GglE9Rc8qPo +BP5yCccLqh0lVX6Wmle3usURehnmp349hQ71+S4pL+f5bFgWV1Al9j4uPqrtd3GqqpmWRgqujuwq +URawXs3qZwQcWDD1YIq9pr1N5Za0/EKJAWv2cMhQOQwt1WbZyNKzMrcbGW3LM/nfpeYVhDfwwvJl +lpKQd/Ct9JDpEXjXk4nAPQu6KfTomZ1yju2dL+6SfaHx/126M2CFYv4HAqGEVka+lgqaE9chTLd8 +B59OTj+RdPsnnRHM3eaxynFNExc5JsUpISuTKWqW+qtB4Uu2NQvAmxU= +-----END CERTIFICATE----- + +TÜRKTRUST Elektronik Sertifika Hizmet Sağlayıcısı H6 +========================================================= +-----BEGIN CERTIFICATE----- +MIIEJjCCAw6gAwIBAgIGfaHyZeyKMA0GCSqGSIb3DQEBCwUAMIGxMQswCQYDVQQGEwJUUjEPMA0G +A1UEBwwGQW5rYXJhMU0wSwYDVQQKDERUw5xSS1RSVVNUIEJpbGdpIMSwbGV0acWfaW0gdmUgQmls +acWfaW0gR8O8dmVubGnEn2kgSGl6bWV0bGVyaSBBLsWeLjFCMEAGA1UEAww5VMOcUktUUlVTVCBF +bGVrdHJvbmlrIFNlcnRpZmlrYSBIaXptZXQgU2HEn2xhecSxY8Sxc8SxIEg2MB4XDTEzMTIxODA5 +MDQxMFoXDTIzMTIxNjA5MDQxMFowgbExCzAJBgNVBAYTAlRSMQ8wDQYDVQQHDAZBbmthcmExTTBL +BgNVBAoMRFTDnFJLVFJVU1QgQmlsZ2kgxLBsZXRpxZ9pbSB2ZSBCaWxpxZ9pbSBHw7x2ZW5sacSf +aSBIaXptZXRsZXJpIEEuxZ4uMUIwQAYDVQQDDDlUw5xSS1RSVVNUIEVsZWt0cm9uaWsgU2VydGlm +aWthIEhpem1ldCBTYcSfbGF5xLFjxLFzxLEgSDYwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK +AoIBAQCdsGjW6L0UlqMACprx9MfMkU1xeHe59yEmFXNRFpQJRwXiM/VomjX/3EsvMsew7eKC5W/a +2uqsxgbPJQ1BgfbBOCK9+bGlprMBvD9QFyv26WZV1DOzXPhDIHiTVRZwGTLmiddk671IUP320EED +wnS3/faAz1vFq6TWlRKb55cTMgPp1KtDWxbtMyJkKbbSk60vbNg9tvYdDjTu0n2pVQ8g9P0pu5Fb +HH3GQjhtQiht1AH7zYiXSX6484P4tZgvsycLSF5W506jM7NE1qXyGJTtHB6plVxiSvgNZ1GpryHV ++DKdeboaX+UEVU0TRv/yz3THGmNtwx8XEsMeED5gCLMxAgMBAAGjQjBAMB0GA1UdDgQWBBTdVRcT +9qzoSCHK77Wv0QAy7Z6MtTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG +9w0BAQsFAAOCAQEAb1gNl0OqFlQ+v6nfkkU/hQu7VtMMUszIv3ZnXuaqs6fvuay0EBQNdH49ba3R +fdCaqaXKGDsCQC4qnFAUi/5XfldcEQlLNkVS9z2sFP1E34uXI9TDwe7UU5X+LEr+DXCqu4svLcsy +o4LyVN/Y8t3XSHLuSqMplsNEzm61kod2pLv0kmzOLBQJZo6NrRa1xxsJYTvjIKIDgI6tflEATseW +hvtDmHd9KMeP2Cpu54Rvl0EpABZeTeIT6lnAY2c6RPuY/ATTMHKm9ocJV612ph1jmv3XZch4gyt1 +O6VbuA1df74jrlZVlFjvH4GMKrLN5ptjnhi85WsGtAuYSyher4hYyw== +-----END CERTIFICATE----- + +Certinomis - Root CA +==================== +-----BEGIN CERTIFICATE----- +MIIFkjCCA3qgAwIBAgIBATANBgkqhkiG9w0BAQsFADBaMQswCQYDVQQGEwJGUjETMBEGA1UEChMK +Q2VydGlub21pczEXMBUGA1UECxMOMDAwMiA0MzM5OTg5MDMxHTAbBgNVBAMTFENlcnRpbm9taXMg +LSBSb290IENBMB4XDTEzMTAyMTA5MTcxOFoXDTMzMTAyMTA5MTcxOFowWjELMAkGA1UEBhMCRlIx +EzARBgNVBAoTCkNlcnRpbm9taXMxFzAVBgNVBAsTDjAwMDIgNDMzOTk4OTAzMR0wGwYDVQQDExRD +ZXJ0aW5vbWlzIC0gUm9vdCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANTMCQos +P5L2fxSeC5yaah1AMGT9qt8OHgZbn1CF6s2Nq0Nn3rD6foCWnoR4kkjW4znuzuRZWJflLieY6pOo +d5tK8O90gC3rMB+12ceAnGInkYjwSond3IjmFPnVAy//ldu9n+ws+hQVWZUKxkd8aRi5pwP5ynap +z8dvtF4F/u7BUrJ1Mofs7SlmO/NKFoL21prbcpjp3vDFTKWrteoB4owuZH9kb/2jJZOLyKIOSY00 +8B/sWEUuNKqEUL3nskoTuLAPrjhdsKkb5nPJWqHZZkCqqU2mNAKthH6yI8H7KsZn9DS2sJVqM09x +RLWtwHkziOC/7aOgFLScCbAK42C++PhmiM1b8XcF4LVzbsF9Ri6OSyemzTUK/eVNfaoqoynHWmgE +6OXWk6RiwsXm9E/G+Z8ajYJJGYrKWUM66A0ywfRMEwNvbqY/kXPLynNvEiCL7sCCeN5LLsJJwx3t +FvYk9CcbXFcx3FXuqB5vbKziRcxXV4p1VxngtViZSTYxPDMBbRZKzbgqg4SGm/lg0h9tkQPTYKbV +PZrdd5A9NaSfD171UkRpucC63M9933zZxKyGIjK8e2uR73r4F2iw4lNVYC2vPsKD2NkJK/DAZNuH +i5HMkesE/Xa0lZrmFAYb1TQdvtj/dBxThZngWVJKYe2InmtJiUZ+IFrZ50rlau7SZRFDAgMBAAGj +YzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTvkUz1pcMw6C8I +6tNxIqSSaHh02TAfBgNVHSMEGDAWgBTvkUz1pcMw6C8I6tNxIqSSaHh02TANBgkqhkiG9w0BAQsF +AAOCAgEAfj1U2iJdGlg+O1QnurrMyOMaauo++RLrVl89UM7g6kgmJs95Vn6RHJk/0KGRHCwPT5iV +WVO90CLYiF2cN/z7ZMF4jIuaYAnq1fohX9B0ZedQxb8uuQsLrbWwF6YSjNRieOpWauwK0kDDPAUw +Pk2Ut59KA9N9J0u2/kTO+hkzGm2kQtHdzMjI1xZSg081lLMSVX3l4kLr5JyTCcBMWwerx20RoFAX +lCOotQqSD7J6wWAsOMwaplv/8gzjqh8c3LigkyfeY+N/IZ865Z764BNqdeuWXGKRlI5nU7aJ+BIJ +y29SWwNyhlCVCNSNh4YVH5Uk2KRvms6knZtt0rJ2BobGVgjF6wnaNsIbW0G+YSrjcOa4pvi2WsS9 +Iff/ql+hbHY5ZtbqTFXhADObE5hjyW/QASAJN1LnDE8+zbz1X5YnpyACleAu6AdBBR8Vbtaw5Bng +DwKTACdyxYvRVB9dSsNAl35VpnzBMwQUAR1JIGkLGZOdblgi90AMRgwjY/M50n92Uaf0yKHxDHYi +I0ZSKS3io0EHVmmY0gUJvGnHWmHNj4FgFU2A3ZDifcRQ8ow7bkrHxuaAKzyBvBGAFhAn1/DNP3nM +cyrDflOR1m749fPH0FFNjkulW+YZFzvWgQncItzujrnEj1PhZ7szuIgVRs/taTX/dQ1G885x4cVr +hkIGuUE= +-----END CERTIFICATE----- diff --git a/settings/templates/users/main.php b/settings/templates/users/main.php index 0abe31f4a59..f50f83b38b3 100644 --- a/settings/templates/users/main.php +++ b/settings/templates/users/main.php @@ -46,35 +46,35 @@ translation('settings'); <div id="userlistoptions"> <p> <input type="checkbox" name="StorageLocation" value="StorageLocation" id="CheckboxStorageLocation" - <?php if ($_['show_storage_location'] === 'true') print_unescaped('checked="checked"'); ?> /> + class="checkbox" <?php if ($_['show_storage_location'] === 'true') print_unescaped('checked="checked"'); ?> /> <label for="CheckboxStorageLocation"> <?php p($l->t('Show storage location')) ?> </label> </p> <p> <input type="checkbox" name="LastLogin" value="LastLogin" id="CheckboxLastLogin" - <?php if ($_['show_last_login'] === 'true') print_unescaped('checked="checked"'); ?> /> + class="checkbox" <?php if ($_['show_last_login'] === 'true') print_unescaped('checked="checked"'); ?> /> <label for="CheckboxLastLogin"> <?php p($l->t('Show last log in')) ?> </label> </p> <p> <input type="checkbox" name="UserBackend" value="UserBackend" id="CheckboxUserBackend" - <?php if ($_['show_backend'] === 'true') print_unescaped('checked="checked"'); ?> /> + class="checkbox" <?php if ($_['show_backend'] === 'true') print_unescaped('checked="checked"'); ?> /> <label for="CheckboxUserBackend"> <?php p($l->t('Show user backend')) ?> </label> </p> <p> <input type="checkbox" name="MailOnUserCreate" value="MailOnUserCreate" id="CheckboxMailOnUserCreate" - <?php if ($_['send_email'] === 'true') print_unescaped('checked="checked"'); ?> /> + class="checkbox" <?php if ($_['send_email'] === 'true') print_unescaped('checked="checked"'); ?> /> <label for="CheckboxMailOnUserCreate"> <?php p($l->t('Send email to new user')) ?> </label> </p> <p> <input type="checkbox" name="EmailAddress" value="EmailAddress" id="CheckboxEmailAddress" - <?php if ($_['show_email'] === 'true') print_unescaped('checked="checked"'); ?> /> + class="checkbox" <?php if ($_['show_email'] === 'true') print_unescaped('checked="checked"'); ?> /> <label for="CheckboxEmailAddress"> <?php p($l->t('Show email address')) ?> </label> diff --git a/tests/lib/files/cache/updater.php b/tests/lib/files/cache/updater.php index e3fa26829b4..b7e76aeace4 100644 --- a/tests/lib/files/cache/updater.php +++ b/tests/lib/files/cache/updater.php @@ -161,6 +161,47 @@ class Updater extends \Test\TestCase { $this->assertEquals($cached['fileid'], $cachedTarget['fileid']); } + public function testUpdateStorageMTime() { + $this->storage->mkdir('sub'); + $this->storage->mkdir('sub2'); + $this->storage->file_put_contents('sub/foo.txt', 'qwerty'); + + $this->updater->update('sub'); + $this->updater->update('sub/foo.txt'); + $this->updater->update('sub2'); + + $cachedSourceParent = $this->cache->get('sub'); + $cachedSource = $this->cache->get('sub/foo.txt'); + + $this->storage->rename('sub/foo.txt', 'sub2/bar.txt'); + + // simulate storage having a different mtime + $testmtime = 1433323578; + + // source storage mtime change + $this->storage->touch('sub', $testmtime); + + // target storage mtime change + $this->storage->touch('sub2', $testmtime); + // some storages (like Dropbox) change storage mtime on rename + $this->storage->touch('sub2/bar.txt', $testmtime); + + $this->updater->rename('sub/foo.txt', 'sub2/bar.txt'); + + $cachedTargetParent = $this->cache->get('sub2'); + $cachedTarget = $this->cache->get('sub2/bar.txt'); + + $this->assertEquals($cachedSource['mtime'], $cachedTarget['mtime'], 'file mtime preserved'); + + $this->assertNotEquals($cachedTarget['storage_mtime'], $cachedTarget['mtime'], 'mtime is not storage_mtime for moved file'); + + $this->assertEquals($testmtime, $cachedTarget['storage_mtime'], 'target file storage_mtime propagated'); + $this->assertNotEquals($testmtime, $cachedTarget['mtime'], 'target file mtime changed, not from storage'); + + $this->assertEquals($testmtime, $cachedTargetParent['storage_mtime'], 'target parent storage_mtime propagated'); + $this->assertNotEquals($testmtime, $cachedTargetParent['mtime'], 'target folder mtime changed, not from storage'); + } + public function testNewFileDisabled() { $this->storage->file_put_contents('foo.txt', 'bar'); $this->assertFalse($this->cache->inCache('foo.txt')); diff --git a/tests/lib/files/view.php b/tests/lib/files/view.php index a84b8badd5a..a7979146b85 100644 --- a/tests/lib/files/view.php +++ b/tests/lib/files/view.php @@ -1976,9 +1976,13 @@ class View extends \Test\TestCase { $view = new \OC\Files\View('/' . $this->user . '/files/'); $storage = $this->getMockBuilder('\OC\Files\Storage\Temporary') - ->setMethods([$operation]) + ->setMethods([$operation, 'filemtime']) ->getMock(); + $storage->expects($this->any()) + ->method('filemtime') + ->will($this->returnValue(123456789)); + $sourcePath = 'original.txt'; $targetPath = 'target.txt'; @@ -2117,9 +2121,13 @@ class View extends \Test\TestCase { ->setMethods([$storageOperation]) ->getMock(); $storage2 = $this->getMockBuilder('\OC\Files\Storage\Temporary') - ->setMethods([$storageOperation]) + ->setMethods([$storageOperation, 'filemtime']) ->getMock(); + $storage2->expects($this->any()) + ->method('filemtime') + ->will($this->returnValue(123456789)); + $sourcePath = 'original.txt'; $targetPath = 'substorage/target.txt'; diff --git a/tests/lib/repair/oldgroupmembershipsharestest.php b/tests/lib/repair/oldgroupmembershipsharestest.php new file mode 100644 index 00000000000..74f68bd7899 --- /dev/null +++ b/tests/lib/repair/oldgroupmembershipsharestest.php @@ -0,0 +1,138 @@ +<?php +/** + * Copyright (c) 2015 Vincent Petry <pvince81@owncloud.com> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ + +namespace Test\Repair; + +use OC\Repair\OldGroupMembershipShares; +use OC\Share\Constants; + +class OldGroupMembershipSharesTest extends \Test\TestCase { + + /** @var OldGroupMembershipShares */ + protected $repair; + + /** @var \OCP\IDBConnection */ + protected $connection; + + /** @var \OCP\IGroupManager|\PHPUnit_Framework_MockObject_MockObject */ + protected $groupManager; + + protected function setUp() { + parent::setUp(); + + /** \OCP\IGroupManager|\PHPUnit_Framework_MockObject_MockObject */ + $this->groupManager = $this->getMockBuilder('OCP\IGroupManager') + ->disableOriginalConstructor() + ->getMock(); + $this->connection = \OC::$server->getDatabaseConnection(); + + $this->deleteAllShares(); + } + + protected function tearDown() { + $this->deleteAllShares(); + + parent::tearDown(); + } + + protected function deleteAllShares() { + $qb = $this->connection->getQueryBuilder(); + $qb->delete('share')->execute(); + } + + public function testRun() { + $repair = new OldGroupMembershipShares( + $this->connection, + $this->groupManager + ); + + $this->groupManager->expects($this->exactly(2)) + ->method('isInGroup') + ->willReturnMap([ + ['member', 'group', true], + ['not-a-member', 'group', false], + ]); + + $parent = $this->createShare(Constants::SHARE_TYPE_GROUP, 'group', null); + $group2 = $this->createShare(Constants::SHARE_TYPE_GROUP, 'group2', $parent); + $user1 = $this->createShare(Constants::SHARE_TYPE_USER, 'user1', $parent); + + // \OC\Share\Constant::$shareTypeGroupUserUnique === 2 + $member = $this->createShare(2, 'member', $parent); + $notAMember = $this->createShare(2, 'not-a-member', $parent); + + $query = $this->connection->getQueryBuilder(); + $result = $query->select('id') + ->from('share') + ->orderBy('id', 'ASC') + ->execute(); + $rows = $result->fetchAll(); + $this->assertSame([['id' => $parent], ['id' => $group2], ['id' => $user1], ['id' => $member], ['id' => $notAMember]], $rows); + $result->closeCursor(); + + $repair->run(); + + $query = $this->connection->getQueryBuilder(); + $result = $query->select('id') + ->from('share') + ->orderBy('id', 'ASC') + ->execute(); + $rows = $result->fetchAll(); + $this->assertSame([['id' => $parent], ['id' => $group2], ['id' => $user1], ['id' => $member]], $rows); + $result->closeCursor(); + } + + /** + * @param string $shareType + * @param string $shareWith + * @param null|int $parent + * @return int + */ + protected function createShare($shareType, $shareWith, $parent) { + $qb = $this->connection->getQueryBuilder(); + $shareValues = [ + 'share_type' => $qb->expr()->literal($shareType), + 'share_with' => $qb->expr()->literal($shareWith), + 'uid_owner' => $qb->expr()->literal('user1'), + 'item_type' => $qb->expr()->literal('folder'), + 'item_source' => $qb->expr()->literal(123), + 'item_target' => $qb->expr()->literal('/123'), + 'file_source' => $qb->expr()->literal(123), + 'file_target' => $qb->expr()->literal('/test'), + 'permissions' => $qb->expr()->literal(1), + 'stime' => $qb->expr()->literal(time()), + 'expiration' => $qb->expr()->literal('2015-09-25 00:00:00'), + ]; + + if ($parent) { + $shareValues['parent'] = $qb->expr()->literal($parent); + } + + $qb = $this->connection->getQueryBuilder(); + $qb->insert('share') + ->values($shareValues) + ->execute(); + + return $this->getLastShareId(); + } + + /** + * @return int + */ + protected function getLastShareId() { + // select because lastInsertId does not work with OCI + $query = $this->connection->getQueryBuilder(); + $result = $query->select('id') + ->from('share') + ->orderBy('id', 'DESC') + ->execute(); + $row = $result->fetch(); + $result->closeCursor(); + return $row['id']; + } +} diff --git a/tests/lib/repair/repairinvalidsharestest.php b/tests/lib/repair/repairinvalidsharestest.php index 89a5ba470e1..005a2db2344 100644 --- a/tests/lib/repair/repairinvalidsharestest.php +++ b/tests/lib/repair/repairinvalidsharestest.php @@ -77,13 +77,7 @@ class RepairInvalidSharesTest extends TestCase { ]) ->execute(); - // select because lastInsertId does not work with OCI - $results = $this->connection->getQueryBuilder() - ->select('id') - ->from('share') - ->execute() - ->fetchAll(); - $bogusShareId = $results[0]['id']; + $bogusShareId = $this->getLastShareId(); // link share with expiration date $qb = $this->connection->getQueryBuilder(); @@ -119,5 +113,83 @@ class RepairInvalidSharesTest extends TestCase { $this->assertNull($userShare['expiration'], 'bogus expiration date was removed'); $this->assertNotNull($linkShare['expiration'], 'valid link share expiration date still there'); } + + /** + * Test remove shares where the parent share does not exist anymore + */ + public function testSharesNonExistingParent() { + $qb = $this->connection->getQueryBuilder(); + $shareValues = [ + 'share_type' => $qb->expr()->literal(Constants::SHARE_TYPE_USER), + 'share_with' => $qb->expr()->literal('recipientuser1'), + 'uid_owner' => $qb->expr()->literal('user1'), + 'item_type' => $qb->expr()->literal('folder'), + 'item_source' => $qb->expr()->literal(123), + 'item_target' => $qb->expr()->literal('/123'), + 'file_source' => $qb->expr()->literal(123), + 'file_target' => $qb->expr()->literal('/test'), + 'permissions' => $qb->expr()->literal(1), + 'stime' => $qb->expr()->literal(time()), + 'expiration' => $qb->expr()->literal('2015-09-25 00:00:00') + ]; + + // valid share + $qb = $this->connection->getQueryBuilder(); + $qb->insert('share') + ->values($shareValues) + ->execute(); + $parent = $this->getLastShareId(); + + // share with existing parent + $qb = $this->connection->getQueryBuilder(); + $qb->insert('share') + ->values(array_merge($shareValues, [ + 'parent' => $qb->expr()->literal($parent), + ]))->execute(); + $validChild = $this->getLastShareId(); + + // share with non-existing parent + $qb = $this->connection->getQueryBuilder(); + $qb->insert('share') + ->values(array_merge($shareValues, [ + 'parent' => $qb->expr()->literal($parent + 100), + ]))->execute(); + $invalidChild = $this->getLastShareId(); + + $query = $this->connection->getQueryBuilder(); + $result = $query->select('id') + ->from('share') + ->orderBy('id', 'ASC') + ->execute(); + $rows = $result->fetchAll(); + $this->assertSame([['id' => $parent], ['id' => $validChild], ['id' => $invalidChild]], $rows); + $result->closeCursor(); + + $this->repair->run(); + + $query = $this->connection->getQueryBuilder(); + $result = $query->select('id') + ->from('share') + ->orderBy('id', 'ASC') + ->execute(); + $rows = $result->fetchAll(); + $this->assertSame([['id' => $parent], ['id' => $validChild]], $rows); + $result->closeCursor(); + } + + /** + * @return int + */ + protected function getLastShareId() { + // select because lastInsertId does not work with OCI + $query = $this->connection->getQueryBuilder(); + $result = $query->select('id') + ->from('share') + ->orderBy('id', 'DESC') + ->execute(); + $row = $result->fetch(); + $result->closeCursor(); + return $row['id']; + } } |