aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--apps/files/js/favoritesfilelist.js9
-rw-r--r--apps/files_external/controller/storagescontroller.php16
-rw-r--r--apps/files_external/js/mountsfilelist.js9
-rw-r--r--apps/files_external/js/settings.js39
-rw-r--r--apps/files_external/lib/config.php1
-rw-r--r--apps/files_external/lib/storageconfig.php25
-rw-r--r--apps/files_external/lib/swift.php5
-rw-r--r--apps/files_sharing/js/sharedfilelist.js8
-rw-r--r--apps/provisioning_api/appinfo/app.php20
-rw-r--r--core/command/maintenance/repair.php17
-rw-r--r--core/js/tests/specs/sharedialogshareelistview.js158
-rw-r--r--core/templates/internalaltmail.php13
-rw-r--r--core/templates/internalmail.php39
-rw-r--r--lib/private/files/cache/cache.php10
-rw-r--r--lib/private/files/cache/updater.php14
-rw-r--r--lib/private/repair.php13
-rw-r--r--lib/private/share/mailnotifications.php10
-rw-r--r--lib/repair/oldgroupmembershipshares.php117
-rw-r--r--lib/repair/repairinvalidshares.php32
-rw-r--r--resources/config/ca-bundle.crt200
-rw-r--r--settings/templates/users/main.php10
-rw-r--r--tests/lib/files/cache/updater.php41
-rw-r--r--tests/lib/files/view.php12
-rw-r--r--tests/lib/repair/oldgroupmembershipsharestest.php138
-rw-r--r--tests/lib/repair/repairinvalidsharestest.php86
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">&nbsp;</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">&nbsp;</td></tr>
+<tr>
+<td width="20px">&nbsp;</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">&nbsp;</td></tr>
+<tr>
+<td width="20px">&nbsp;</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">&nbsp;</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'];
+ }
}