aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--apps/comments/appinfo/app.php34
-rw-r--r--apps/comments/appinfo/info.xml16
-rw-r--r--apps/comments/css/comments.css51
-rw-r--r--apps/comments/js/app.js20
-rw-r--r--apps/comments/js/commentcollection.js110
-rw-r--r--apps/comments/js/commentmodel.js48
-rw-r--r--apps/comments/js/commentstabview.js236
-rw-r--r--apps/comments/js/filesplugin.js41
-rw-r--r--apps/comments/tests/js/commentscollectionSpec.js104
-rw-r--r--apps/comments/tests/js/commentstabviewSpec.js198
-rw-r--r--apps/dav/lib/carddav/addressbookimpl.php7
-rw-r--r--apps/dav/lib/carddav/contactsmanager.php14
-rw-r--r--apps/dav/lib/carddav/converter.php17
-rw-r--r--apps/dav/lib/comments/commentnode.php39
-rw-r--r--apps/dav/lib/comments/commentsplugin.php4
-rw-r--r--apps/dav/lib/comments/entitycollection.php57
-rw-r--r--apps/dav/lib/comments/entitytypecollection.php5
-rw-r--r--apps/dav/lib/comments/rootcollection.php1
-rw-r--r--apps/dav/lib/connector/sabre/commentpropertiesplugin.php130
-rw-r--r--apps/dav/lib/connector/sabre/serverfactory.php1
-rw-r--r--apps/dav/lib/rootcollection.php3
-rw-r--r--apps/dav/lib/systemtag/systemtagplugin.php7
-rw-r--r--apps/dav/lib/systemtag/systemtagsobjecttypecollection.php23
-rw-r--r--apps/dav/lib/systemtag/systemtagsrelationscollection.php8
-rw-r--r--apps/dav/tests/unit/appinfo/applicationtest.php17
-rw-r--r--apps/dav/tests/unit/carddav/contactsmanagertest.php2
-rw-r--r--apps/dav/tests/unit/comments/commentnode.php48
-rw-r--r--apps/dav/tests/unit/comments/entitycollection.php3
-rw-r--r--apps/dav/tests/unit/comments/entitytypecollection.php3
-rw-r--r--apps/dav/tests/unit/connector/sabre/commentpropertiesplugin.php148
-rw-r--r--apps/dav/tests/unit/systemtag/systemtagplugin.php34
-rw-r--r--apps/dav/tests/unit/systemtag/systemtagsobjecttypecollection.php47
-rw-r--r--apps/federation/api/ocsauthapi.php8
-rw-r--r--apps/federation/backgroundjob/getsharedsecret.php3
-rw-r--r--apps/federation/backgroundjob/requestsharedsecret.php3
-rw-r--r--apps/federation/lib/syncfederationaddressbooks.php2
-rw-r--r--apps/files/command/scan.php74
-rw-r--r--apps/files/css/files.css2
-rw-r--r--apps/files/l10n/de_DE.js2
-rw-r--r--apps/files/l10n/de_DE.json2
-rw-r--r--apps/files_external/controller/storagescontroller.php2
-rw-r--r--apps/files_external/js/settings.js40
-rw-r--r--apps/files_external/l10n/cs_CZ.js3
-rw-r--r--apps/files_external/l10n/cs_CZ.json3
-rw-r--r--apps/files_external/l10n/da.js1
-rw-r--r--apps/files_external/l10n/da.json1
-rw-r--r--apps/files_external/l10n/de_DE.js1
-rw-r--r--apps/files_external/l10n/de_DE.json1
-rw-r--r--apps/files_external/l10n/el.js1
-rw-r--r--apps/files_external/l10n/el.json1
-rw-r--r--apps/files_external/l10n/es.js1
-rw-r--r--apps/files_external/l10n/es.json1
-rw-r--r--apps/files_external/l10n/fi_FI.js4
-rw-r--r--apps/files_external/l10n/fi_FI.json4
-rw-r--r--apps/files_external/l10n/fr.js3
-rw-r--r--apps/files_external/l10n/fr.json3
-rw-r--r--apps/files_external/l10n/he.js6
-rw-r--r--apps/files_external/l10n/he.json6
-rw-r--r--apps/files_external/l10n/id.js1
-rw-r--r--apps/files_external/l10n/id.json1
-rw-r--r--apps/files_external/l10n/it.js6
-rw-r--r--apps/files_external/l10n/it.json6
-rw-r--r--apps/files_external/l10n/ja.js2
-rw-r--r--apps/files_external/l10n/ja.json2
-rw-r--r--apps/files_external/l10n/ko.js1
-rw-r--r--apps/files_external/l10n/ko.json1
-rw-r--r--apps/files_external/l10n/nb_NO.js1
-rw-r--r--apps/files_external/l10n/nb_NO.json1
-rw-r--r--apps/files_external/l10n/nds.js1
-rw-r--r--apps/files_external/l10n/nds.json1
-rw-r--r--apps/files_external/l10n/nl.js3
-rw-r--r--apps/files_external/l10n/nl.json3
-rw-r--r--apps/files_external/l10n/oc.js1
-rw-r--r--apps/files_external/l10n/oc.json1
-rw-r--r--apps/files_external/l10n/pt_BR.js3
-rw-r--r--apps/files_external/l10n/pt_BR.json3
-rw-r--r--apps/files_external/l10n/pt_PT.js6
-rw-r--r--apps/files_external/l10n/pt_PT.json6
-rw-r--r--apps/files_external/l10n/ru.js5
-rw-r--r--apps/files_external/l10n/ru.json5
-rw-r--r--apps/files_external/l10n/sk_SK.js1
-rw-r--r--apps/files_external/l10n/sk_SK.json1
-rw-r--r--apps/files_external/l10n/sl.js2
-rw-r--r--apps/files_external/l10n/sl.json2
-rw-r--r--apps/files_external/l10n/sq.js3
-rw-r--r--apps/files_external/l10n/sq.json3
-rw-r--r--apps/files_external/l10n/th_TH.js2
-rw-r--r--apps/files_external/l10n/th_TH.json2
-rw-r--r--apps/files_external/l10n/tr.js1
-rw-r--r--apps/files_external/l10n/tr.json1
-rw-r--r--apps/files_external/lib/auth/password/logincredentials.php2
-rw-r--r--apps/files_external/lib/auth/password/sessioncredentials.php2
-rw-r--r--apps/files_external/lib/auth/password/userprovided.php2
-rw-r--r--apps/files_external/lib/definitionparameter.php26
-rw-r--r--apps/files_external/tests/definitionparameterttest.php24
-rw-r--r--apps/files_external/tests/js/settingsSpec.js49
-rw-r--r--apps/files_sharing/api/share20ocs.php18
-rw-r--r--apps/files_sharing/api/sharees.php9
-rw-r--r--apps/files_sharing/lib/sharedstorage.php24
-rw-r--r--apps/systemtags/activity/extension.php300
-rw-r--r--apps/systemtags/activity/listener.php223
-rw-r--r--apps/systemtags/appinfo/app.php34
-rw-r--r--apps/systemtags/appinfo/info.xml5
-rw-r--r--apps/user_ldap/l10n/he.js31
-rw-r--r--apps/user_ldap/l10n/he.json31
-rw-r--r--apps/user_ldap/lib/connection.php2
-rw-r--r--build/integration/features/provisioning-v1.feature3
-rw-r--r--build/integration/features/sharing-v1.feature22
-rw-r--r--console.php4
-rw-r--r--core/command/base.php38
-rw-r--r--core/js/config.php10
-rw-r--r--core/js/js.js36
-rw-r--r--core/js/oc-backbone-webdav.js24
-rw-r--r--core/js/setupchecks.js31
-rw-r--r--core/js/tests/specs/setupchecksSpec.js67
-rw-r--r--core/l10n/cs_CZ.js3
-rw-r--r--core/l10n/cs_CZ.json3
-rw-r--r--core/l10n/de_DE.js3
-rw-r--r--core/l10n/de_DE.json3
-rw-r--r--core/l10n/fi_FI.js3
-rw-r--r--core/l10n/fi_FI.json3
-rw-r--r--core/l10n/fr.js7
-rw-r--r--core/l10n/fr.json7
-rw-r--r--core/l10n/he.js53
-rw-r--r--core/l10n/he.json53
-rw-r--r--core/l10n/it.js3
-rw-r--r--core/l10n/it.json3
-rw-r--r--core/l10n/ja.js4
-rw-r--r--core/l10n/ja.json4
-rw-r--r--core/l10n/nl.js3
-rw-r--r--core/l10n/nl.json3
-rw-r--r--core/l10n/pt_BR.js3
-rw-r--r--core/l10n/pt_BR.json3
-rw-r--r--core/l10n/pt_PT.js3
-rw-r--r--core/l10n/pt_PT.json3
-rw-r--r--core/l10n/ru.js7
-rw-r--r--core/l10n/ru.json7
-rw-r--r--core/l10n/sq.js3
-rw-r--r--core/l10n/sq.json3
-rw-r--r--core/shipped.json1
-rw-r--r--core/templates/layout.user.php2
-rw-r--r--core/vendor/davclient.js/lib/client.js31
-rw-r--r--db_structure.xml43
-rw-r--r--lib/base.php9
-rw-r--r--lib/l10n/de_DE.js2
-rw-r--r--lib/l10n/de_DE.json2
-rw-r--r--lib/l10n/ja.js3
-rw-r--r--lib/l10n/ja.json3
-rw-r--r--lib/l10n/nl.js1
-rw-r--r--lib/l10n/nl.json1
-rw-r--r--lib/l10n/pt_BR.js1
-rw-r--r--lib/l10n/pt_BR.json1
-rw-r--r--lib/l10n/ru.js1
-rw-r--r--lib/l10n/ru.json1
-rw-r--r--lib/private/activitymanager.php9
-rw-r--r--lib/private/appframework/dependencyinjection/dicontainer.php14
-rw-r--r--lib/private/appframework/middleware/security/securitymiddleware.php37
-rw-r--r--lib/private/comments/manager.php145
-rw-r--r--lib/private/comments/managerfactory.php3
-rw-r--r--lib/private/contacts/localaddressbook.php118
-rw-r--r--lib/private/files/view.php5
-rw-r--r--lib/private/security/csp/contentsecuritypolicy.php199
-rw-r--r--lib/private/security/csp/contentsecuritypolicymanager.php73
-rw-r--r--lib/private/server.php12
-rw-r--r--lib/private/share20/defaultshareprovider.php117
-rw-r--r--lib/private/share20/manager.php80
-rw-r--r--lib/private/systemtag/managerfactory.php13
-rw-r--r--lib/private/systemtag/systemtagmanager.php72
-rw-r--r--lib/private/systemtag/systemtagobjectmapper.php35
-rw-r--r--lib/private/templatelayout.php2
-rw-r--r--lib/private/user/user.php1
-rw-r--r--lib/private/util.php10
-rw-r--r--lib/public/appframework/controller.php2
-rw-r--r--lib/public/appframework/http/contentsecuritypolicy.php335
-rw-r--r--lib/public/appframework/http/emptycontentsecuritypolicy.php387
-rw-r--r--lib/public/comments/icommentsmanager.php48
-rw-r--r--lib/public/files/storagenotavailableexception.php2
-rw-r--r--lib/public/iservercontainer.php7
-rw-r--r--lib/public/security/icontentsecuritypolicymanager.php50
-rw-r--r--lib/public/share/imanager.php34
-rw-r--r--lib/public/share/ishareprovider.php20
-rw-r--r--lib/public/systemtag/managerevent.php85
-rw-r--r--lib/public/systemtag/mapperevent.php93
-rw-r--r--settings/controller/checksetupcontroller.php1
-rw-r--r--settings/js/admin.js7
-rw-r--r--settings/l10n/de_DE.js7
-rw-r--r--settings/l10n/de_DE.json7
-rw-r--r--settings/l10n/fr.js1
-rw-r--r--settings/l10n/fr.json1
-rw-r--r--settings/l10n/ja.js2
-rw-r--r--settings/l10n/ja.json2
-rw-r--r--settings/l10n/nl.js2
-rw-r--r--settings/l10n/nl.json2
-rw-r--r--settings/l10n/pt_BR.js2
-rw-r--r--settings/l10n/pt_BR.json2
-rw-r--r--settings/l10n/ru.js2
-rw-r--r--settings/l10n/ru.json2
-rw-r--r--settings/l10n/zh_TW.js2
-rw-r--r--settings/l10n/zh_TW.json2
-rw-r--r--settings/personal.php2
-rw-r--r--settings/users.php2
-rw-r--r--tests/karma.config.js12
-rw-r--r--tests/lib/appframework/http/ContentSecurityPolicyTest.php17
-rw-r--r--tests/lib/appframework/http/EmptyContentSecurityPolicyTest.php430
-rw-r--r--tests/lib/appframework/middleware/security/SecurityMiddlewareTest.php37
-rw-r--r--tests/lib/comments/fakemanager.php10
-rw-r--r--tests/lib/comments/manager.php69
-rw-r--r--tests/lib/contacts/localadressbook.php114
-rw-r--r--tests/lib/security/csp/ContentSecurityPolicyManagerTest.php66
-rw-r--r--tests/lib/server.php2
-rw-r--r--tests/lib/share20/defaultshareprovidertest.php185
-rw-r--r--tests/lib/share20/managertest.php224
-rw-r--r--tests/lib/systemtag/systemtagmanagertest.php17
-rw-r--r--tests/lib/systemtag/systemtagobjectmappertest.php27
-rw-r--r--tests/settings/controller/CheckSetupControllerTest.php5
-rw-r--r--version.php2
217 files changed, 5282 insertions, 1033 deletions
diff --git a/.gitignore b/.gitignore
index 237f0f44e81..2e42105ad83 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,6 +9,7 @@
# ignore all apps except core ones
/apps*/*
+!/apps/comments
!/apps/dav
!/apps/files
!/apps/federation
diff --git a/apps/comments/appinfo/app.php b/apps/comments/appinfo/app.php
new file mode 100644
index 00000000000..c6f36567c51
--- /dev/null
+++ b/apps/comments/appinfo/app.php
@@ -0,0 +1,34 @@
+<?php
+/**
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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/>
+ *
+ */
+
+$eventDispatcher = \OC::$server->getEventDispatcher();
+$eventDispatcher->addListener(
+ 'OCA\Files::loadAdditionalScripts',
+ function() {
+ \OCP\Util::addScript('oc-backbone-webdav');
+ \OCP\Util::addScript('comments', 'app');
+ \OCP\Util::addScript('comments', 'commentmodel');
+ \OCP\Util::addScript('comments', 'commentcollection');
+ \OCP\Util::addScript('comments', 'commentstabview');
+ \OCP\Util::addScript('comments', 'filesplugin');
+ \OCP\Util::addStyle('comments', 'comments');
+ }
+);
diff --git a/apps/comments/appinfo/info.xml b/apps/comments/appinfo/info.xml
new file mode 100644
index 00000000000..550c79448cf
--- /dev/null
+++ b/apps/comments/appinfo/info.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0"?>
+<info>
+ <id>comments</id>
+ <name>Comments</name>
+ <description>Files app plugin to add comments to files</description>
+ <licence>AGPL</licence>
+ <author>Arthur Shiwon, Vincent Petry</author>
+ <default_enable/>
+ <version>0.1</version>
+ <dependencies>
+ <owncloud min-version="9.0" max-version="9.0" />
+ </dependencies>
+ <documentation>
+ <user>user-comments</user>
+ </documentation>
+</info>
diff --git a/apps/comments/css/comments.css b/apps/comments/css/comments.css
new file mode 100644
index 00000000000..c1624dcc57b
--- /dev/null
+++ b/apps/comments/css/comments.css
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2016
+ *
+ * This file is licensed under the Affero General Public License version 3
+ * or later.
+ *
+ * See the COPYING-README file.
+ *
+ */
+
+#commentsTabView .newCommentForm {
+ margin-bottom: 20px;
+}
+
+#commentsTabView .newCommentForm .message {
+ width: 90%;
+ resize: none;
+}
+
+#commentsTabView .newCommentForm .submitLoading {
+ background-position: left;
+}
+
+#commentsTabView .comment {
+ margin-bottom: 30px;
+}
+
+#commentsTabView .comment .avatar {
+ width: 28px;
+ height: 28px;
+ line-height: 28px;
+}
+
+#commentsTabView .authorRow>div {
+ display: inline-block;
+ vertical-align: middle;
+}
+
+#commentsTabView .comment .authorRow {
+ margin-bottom: 5px;
+ position: relative;
+}
+
+#commentsTabView .comment .author {
+ font-weight: bold;
+}
+
+#commentsTabView .comment .date {
+ position: absolute;
+ right: 0;
+}
diff --git a/apps/comments/js/app.js b/apps/comments/js/app.js
new file mode 100644
index 00000000000..547059393a5
--- /dev/null
+++ b/apps/comments/js/app.js
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2016 Vincent Petry <pvince81@owncloud.com>
+ *
+ * This file is licensed under the Affero General Public License version 3
+ * or later.
+ *
+ * See the COPYING-README file.
+ *
+ */
+
+(function() {
+ if (!OCA.Comments) {
+ /**
+ * @namespace
+ */
+ OCA.Comments = {};
+ }
+
+})();
+
diff --git a/apps/comments/js/commentcollection.js b/apps/comments/js/commentcollection.js
new file mode 100644
index 00000000000..d10e5e00865
--- /dev/null
+++ b/apps/comments/js/commentcollection.js
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2016
+ *
+ * This file is licensed under the Affero General Public License version 3
+ * or later.
+ *
+ * See the COPYING-README file.
+ *
+ */
+
+(function(OC, OCA) {
+
+ var NS_OWNCLOUD = 'http://owncloud.org/ns';
+
+ /**
+ * @class OCA.Comments.CommentCollection
+ * @classdesc
+ *
+ * Collection of comments assigned to a file
+ *
+ */
+ var CommentCollection = OC.Backbone.Collection.extend(
+ /** @lends OCA.Comments.CommentCollection.prototype */ {
+
+ sync: OC.Backbone.davSync,
+
+ model: OCA.Comments.CommentModel,
+
+ _objectType: 'files',
+ _objectId: null,
+
+ _endReached: false,
+ _limit : 20,
+
+ initialize: function(models, options) {
+ options = options || {};
+ if (options.objectType) {
+ this._objectType = options.objectType;
+ }
+ if (options.objectId) {
+ this._objectId = options.objectId;
+ }
+ },
+
+ url: function() {
+ return OC.linkToRemote('dav') + '/comments/' +
+ encodeURIComponent(this._objectType) + '/' +
+ encodeURIComponent(this._objectId) + '/';
+ },
+
+ setObjectId: function(objectId) {
+ this._objectId = objectId;
+ },
+
+ hasMoreResults: function() {
+ return !this._endReached;
+ },
+
+ reset: function() {
+ this._endReached = false;
+ return OC.Backbone.Collection.prototype.reset.apply(this, arguments);
+ },
+
+ /**
+ * Fetch the next set of results
+ */
+ fetchNext: function(options) {
+ var self = this;
+ if (!this.hasMoreResults()) {
+ return null;
+ }
+
+ var body = '<?xml version="1.0" encoding="utf-8" ?>\n' +
+ '<oc:filter-comments xmlns:D="DAV:" xmlns:oc="http://owncloud.org/ns">\n' +
+ // load one more so we know there is more
+ ' <oc:limit>' + (this._limit + 1) + '</oc:limit>\n' +
+ ' <oc:offset>' + this.length + '</oc:offset>\n' +
+ '</oc:filter-comments>\n';
+
+ options = options || {};
+ var success = options.success;
+ options = _.extend({
+ remove: false,
+ data: body,
+ davProperties: CommentCollection.prototype.model.prototype.davProperties,
+ success: function(resp) {
+ if (resp.length <= self._limit) {
+ // no new entries, end reached
+ self._endReached = true;
+ } else {
+ // remove last entry, for next page load
+ resp = _.initial(resp);
+ }
+ if (!self.set(resp, options)) {
+ return false;
+ }
+ if (success) {
+ success.apply(null, arguments);
+ }
+ self.trigger('sync', 'REPORT', self, options);
+ }
+ }, options);
+
+ return this.sync('REPORT', this, options);
+ }
+ });
+
+ OCA.Comments.CommentCollection = CommentCollection;
+})(OC, OCA);
+
diff --git a/apps/comments/js/commentmodel.js b/apps/comments/js/commentmodel.js
new file mode 100644
index 00000000000..b945f71fdd2
--- /dev/null
+++ b/apps/comments/js/commentmodel.js
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2016
+ *
+ * This file is licensed under the Affero General Public License version 3
+ * or later.
+ *
+ * See the COPYING-README file.
+ *
+ */
+
+(function(OC, OCA) {
+ var NS_OWNCLOUD = 'http://owncloud.org/ns';
+ /**
+ * @class OCA.Comments.CommentModel
+ * @classdesc
+ *
+ * Comment
+ *
+ */
+ var CommentModel = OC.Backbone.Model.extend(
+ /** @lends OCA.Comments.CommentModel.prototype */ {
+ sync: OC.Backbone.davSync,
+
+ defaults: {
+ actorType: 'users',
+ objectType: 'files'
+ },
+
+ davProperties: {
+ 'id': '{' + NS_OWNCLOUD + '}id',
+ 'message': '{' + NS_OWNCLOUD + '}message',
+ 'actorType': '{' + NS_OWNCLOUD + '}actorType',
+ 'actorId': '{' + NS_OWNCLOUD + '}actorId',
+ 'actorDisplayName': '{' + NS_OWNCLOUD + '}actorDisplayName',
+ 'creationDateTime': '{' + NS_OWNCLOUD + '}creationDateTime',
+ 'objectType': '{' + NS_OWNCLOUD + '}objectType',
+ 'objectId': '{' + NS_OWNCLOUD + '}objectId'
+ },
+
+ parse: function(data) {
+ // TODO: parse non-string values
+ return data;
+ }
+ });
+
+ OCA.Comments.CommentModel = CommentModel;
+})(OC, OCA);
+
diff --git a/apps/comments/js/commentstabview.js b/apps/comments/js/commentstabview.js
new file mode 100644
index 00000000000..463ac2d76ef
--- /dev/null
+++ b/apps/comments/js/commentstabview.js
@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 2016
+ *
+ * This file is licensed under the Affero General Public License version 3
+ * or later.
+ *
+ * See the COPYING-README file.
+ *
+ */
+
+(function(OC, OCA) {
+ var TEMPLATE =
+ '<div class="newCommentRow comment">' +
+ ' <div class="authorRow">' +
+ ' {{#if avatarEnabled}}' +
+ ' <div class="avatar" data-username="{{userId}}"></div>' +
+ ' {{/if}}' +
+ ' <div class="author">{{userDisplayName}}</div>' +
+ ' </div>' +
+ ' <form class="newCommentForm">' +
+ ' <textarea class="message" placeholder="{{newMessagePlaceholder}}"></textarea>' +
+ ' <input class="submit" type="submit" value="{{submitText}}" />' +
+ ' <div class="submitLoading icon-loading-small hidden"></div>'+
+ ' </form>' +
+ ' <ul class="comments">' +
+ ' </ul>' +
+ '</div>' +
+ '<div class="empty hidden">{{emptyResultLabel}}</div>' +
+ '<input type="button" class="showMore hidden" value="{{moreLabel}}"' +
+ ' name="show-more" id="show-more" />' +
+ '<div class="loading hidden" style="height: 50px"></div>';
+
+ var COMMENT_TEMPLATE =
+ '<li class="comment">' +
+ ' <div class="authorRow">' +
+ ' {{#if avatarEnabled}}' +
+ ' <div class="avatar" data-username="{{actorId}}"> </div>' +
+ ' {{/if}}' +
+ ' <div class="author">{{actorDisplayName}}</div>' +
+ ' <div class="date has-tooltip" title="{{altDate}}">{{date}}</div>' +
+ ' </div>' +
+ ' <div class="message">{{{formattedMessage}}}</div>' +
+ '</li>';
+
+ /**
+ * @memberof OCA.Comments
+ */
+ var CommentsTabView = OCA.Files.DetailTabView.extend(
+ /** @lends OCA.Comments.CommentsTabView.prototype */ {
+ id: 'commentsTabView',
+ className: 'tab commentsTabView',
+
+ events: {
+ 'submit .newCommentForm': '_onSubmitComment',
+ 'click .showMore': '_onClickShowMore'
+ },
+
+ initialize: function() {
+ OCA.Files.DetailTabView.prototype.initialize.apply(this, arguments);
+ this.collection = new OCA.Comments.CommentCollection();
+ this.collection.on('request', this._onRequest, this);
+ this.collection.on('sync', this._onEndRequest, this);
+ this.collection.on('add', this._onAddModel, this);
+
+ this._avatarsEnabled = !!OC.config.enable_avatars;
+
+ // TODO: error handling
+ _.bindAll(this, '_onSubmitComment');
+ },
+
+ template: function(params) {
+ if (!this._template) {
+ this._template = Handlebars.compile(TEMPLATE);
+ }
+ var currentUser = OC.getCurrentUser();
+ return this._template(_.extend({
+ avatarEnabled: this._avatarsEnabled,
+ userId: currentUser.uid,
+ userDisplayName: currentUser.displayName,
+ newMessagePlaceholder: t('comments', 'Type in a new comment...'),
+ submitText: t('comments', 'Post')
+ }, params));
+ },
+
+ commentTemplate: function(params) {
+ if (!this._commentTemplate) {
+ this._commentTemplate = Handlebars.compile(COMMENT_TEMPLATE);
+ }
+ return this._commentTemplate(_.extend({
+ avatarEnabled: this._avatarsEnabled
+ }, params));
+ },
+
+ getLabel: function() {
+ return t('comments', 'Comments');
+ },
+
+ setFileInfo: function(fileInfo) {
+ if (fileInfo) {
+ this.render();
+ this.collection.setObjectId(fileInfo.id);
+ // reset to first page
+ this.collection.reset([], {silent: true});
+ this.nextPage();
+ } else {
+ this.render();
+ this.collection.reset();
+ }
+ },
+
+ render: function() {
+ this.$el.html(this.template({
+ emptyResultLabel: t('comments', 'No other comments available'),
+ moreLabel: t('comments', 'More comments...')
+ }));
+ this.$el.find('.has-tooltip').tooltip();
+ this.$container = this.$el.find('ul.comments');
+ this.$el.find('.avatar').avatar(OC.getCurrentUser().uid, 28);
+ this.delegateEvents();
+ },
+
+ _formatItem: function(commentModel) {
+ var timestamp = new Date(commentModel.get('creationDateTime')).getTime();
+ var data = _.extend({
+ date: OC.Util.relativeModifiedDate(timestamp),
+ altDate: OC.Util.formatDate(timestamp),
+ formattedMessage: this._formatMessage(commentModel.get('message'))
+ }, commentModel.attributes);
+ return data;
+ },
+
+ _toggleLoading: function(state) {
+ this._loading = state;
+ this.$el.find('.loading').toggleClass('hidden', !state);
+ },
+
+ _onRequest: function() {
+ this._toggleLoading(true);
+ this.$el.find('.showMore').addClass('hidden');
+ },
+
+ _onEndRequest: function() {
+ this._toggleLoading(false);
+ this.$el.find('.empty').toggleClass('hidden', !!this.collection.length);
+ this.$el.find('.showMore').toggleClass('hidden', !this.collection.hasMoreResults());
+ },
+
+ _onAddModel: function(model, collection, options) {
+ var $el = $(this.commentTemplate(this._formatItem(model)));
+ if (!_.isUndefined(options.at) && collection.length > 1) {
+ this.$container.find('li').eq(options.at).before($el);
+ } else {
+ this.$container.append($el);
+ }
+
+ this._postRenderItem($el);
+ },
+
+ _postRenderItem: function($el) {
+ $el.find('.has-tooltip').tooltip();
+ if(this._avatarsEnabled) {
+ $el.find('.avatar').each(function() {
+ var $this = $(this);
+ $this.avatar($this.attr('data-username'), 28);
+ });
+ }
+ },
+
+ /**
+ * Convert a message to be displayed in HTML,
+ * converts newlines to <br> tags.
+ */
+ _formatMessage: function(message) {
+ return escapeHTML(message).replace(/\n/g, '<br/>');
+ },
+
+ nextPage: function() {
+ if (this._loading || !this.collection.hasMoreResults()) {
+ return;
+ }
+
+ this.collection.fetchNext();
+ },
+
+ _onClickShowMore: function(ev) {
+ ev.preventDefault();
+ this.nextPage();
+ },
+
+ _onSubmitComment: function(e) {
+ var $form = $(e.target);
+ var currentUser = OC.getCurrentUser();
+ var $submit = $form.find('.submit');
+ var $loading = $form.find('.submitLoading');
+ var $textArea = $form.find('textarea');
+ var message = $textArea.val().trim();
+ e.preventDefault();
+
+ if (!message.length) {
+ return;
+ }
+
+ $textArea.prop('disabled', true);
+ $submit.addClass('hidden');
+ $loading.removeClass('hidden');
+
+ this.collection.create({
+ actorId: currentUser.uid,
+ actorDisplayName: currentUser.displayName,
+ actorType: 'users',
+ verb: 'comment',
+ message: $textArea.val(),
+ creationDateTime: (new Date()).getTime()
+ }, {
+ at: 0,
+ success: function() {
+ $submit.removeClass('hidden');
+ $loading.addClass('hidden');
+ $textArea.val('').prop('disabled', false);
+ },
+ error: function(msg) {
+ $submit.removeClass('hidden');
+ $loading.addClass('hidden');
+ $textArea.prop('disabled', false);
+
+ OC.Notification.showTemporary(msg);
+ }
+ });
+
+ return false;
+ }
+ });
+
+ OCA.Comments.CommentsTabView = CommentsTabView;
+})(OC, OCA);
+
diff --git a/apps/comments/js/filesplugin.js b/apps/comments/js/filesplugin.js
new file mode 100644
index 00000000000..c8d91e0ede3
--- /dev/null
+++ b/apps/comments/js/filesplugin.js
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2016 Vincent Petry <pvince81@owncloud.com>
+ *
+ * This file is licensed under the Affero General Public License version 3
+ * or later.
+ *
+ * See the COPYING-README file.
+ *
+ */
+
+(function() {
+ OCA.Comments = _.extend({}, OCA.Comments);
+ if (!OCA.Comments) {
+ /**
+ * @namespace
+ */
+ OCA.Comments = {};
+ }
+
+ /**
+ * @namespace
+ */
+ OCA.Comments.FilesPlugin = {
+ allowedLists: [
+ 'files',
+ 'favorites'
+ ],
+
+ attach: function(fileList) {
+ if (this.allowedLists.indexOf(fileList.id) < 0) {
+ return;
+ }
+
+ fileList.registerTabView(new OCA.Comments.CommentsTabView('commentsTabView'));
+ }
+ };
+
+})();
+
+OC.Plugins.register('OCA.Files.FileList', OCA.Comments.FilesPlugin);
+
diff --git a/apps/comments/tests/js/commentscollectionSpec.js b/apps/comments/tests/js/commentscollectionSpec.js
new file mode 100644
index 00000000000..0dc68cc167c
--- /dev/null
+++ b/apps/comments/tests/js/commentscollectionSpec.js
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2016
+ *
+ * This file is licensed under the Affero General Public License comment 3
+ * or later.
+ *
+ * See the COPYING-README file.
+ *
+ */
+describe('OCA.Comments.CommentCollection', function() {
+ var CommentCollection = OCA.Comments.CommentCollection;
+ var collection, syncStub;
+ var comment1, comment2, comment3;
+
+ beforeEach(function() {
+ syncStub = sinon.stub(CommentCollection.prototype, 'sync');
+ collection = new CommentCollection();
+ collection.setObjectId(5);
+
+ comment1 = {
+ id: 1,
+ actorType: 'users',
+ actorId: 'user1',
+ actorDisplayName: 'User One',
+ objectType: 'files',
+ objectId: 5,
+ message: 'First',
+ creationDateTime: Date.UTC(2016, 1, 3, 10, 5, 0)
+ };
+ comment2 = {
+ id: 2,
+ actorType: 'users',
+ actorId: 'user2',
+ actorDisplayName: 'User Two',
+ objectType: 'files',
+ objectId: 5,
+ message: 'Second\nNewline',
+ creationDateTime: Date.UTC(2016, 1, 3, 10, 0, 0)
+ };
+ comment3 = {
+ id: 3,
+ actorType: 'users',
+ actorId: 'user3',
+ actorDisplayName: 'User Three',
+ objectType: 'files',
+ objectId: 5,
+ message: 'Third',
+ creationDateTime: Date.UTC(2016, 1, 3, 5, 0, 0)
+ };
+ });
+ afterEach(function() {
+ syncStub.restore();
+ });
+
+ it('fetches the next page', function() {
+ collection._limit = 2;
+ collection.fetchNext();
+
+ expect(syncStub.calledOnce).toEqual(true);
+ expect(syncStub.lastCall.args[0]).toEqual('REPORT');
+ var options = syncStub.lastCall.args[2];
+ expect(options.remove).toEqual(false);
+
+ var parser = new DOMParser();
+ var doc = parser.parseFromString(options.data, "application/xml");
+ expect(doc.getElementsByTagNameNS('http://owncloud.org/ns', 'limit')[0].textContent).toEqual('3');
+ expect(doc.getElementsByTagNameNS('http://owncloud.org/ns', 'offset')[0].textContent).toEqual('0');
+
+ syncStub.yieldTo('success', [comment1, comment2, comment3]);
+
+ expect(collection.length).toEqual(2);
+ expect(collection.hasMoreResults()).toEqual(true);
+
+ collection.fetchNext();
+
+ expect(syncStub.calledTwice).toEqual(true);
+ options = syncStub.lastCall.args[2];
+ doc = parser.parseFromString(options.data, "application/xml");
+ expect(doc.getElementsByTagNameNS('http://owncloud.org/ns', 'limit')[0].textContent).toEqual('3');
+ expect(doc.getElementsByTagNameNS('http://owncloud.org/ns', 'offset')[0].textContent).toEqual('2');
+
+ syncStub.yieldTo('success', [comment3]);
+
+ expect(collection.length).toEqual(3);
+ expect(collection.hasMoreResults()).toEqual(false);
+
+ collection.fetchNext();
+
+ // no further requests
+ expect(syncStub.calledTwice).toEqual(true);
+ });
+ it('resets page counted when calling reset', function() {
+ collection.fetchNext();
+
+ syncStub.yieldTo('success', [comment1]);
+
+ expect(collection.hasMoreResults()).toEqual(false);
+
+ collection.reset();
+
+ expect(collection.hasMoreResults()).toEqual(true);
+ });
+});
+
diff --git a/apps/comments/tests/js/commentstabviewSpec.js b/apps/comments/tests/js/commentstabviewSpec.js
new file mode 100644
index 00000000000..0fb5eec0653
--- /dev/null
+++ b/apps/comments/tests/js/commentstabviewSpec.js
@@ -0,0 +1,198 @@
+/**
+* ownCloud
+*
+* @author Vincent Petry
+* @copyright 2016 Vincent Petry <pvince81@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
+* comment 3 of the License, or any later comment.
+*
+* 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/>.
+*
+*/
+
+describe('OCA.Comments.CommentsTabView tests', function() {
+ var view, fileInfoModel;
+ var fetchStub;
+ var testComments;
+ var clock;
+
+ beforeEach(function() {
+ clock = sinon.useFakeTimers(Date.UTC(2016, 1, 3, 10, 5, 9));
+ fetchStub = sinon.stub(OCA.Comments.CommentCollection.prototype, 'fetchNext');
+ view = new OCA.Comments.CommentsTabView();
+ fileInfoModel = new OCA.Files.FileInfoModel({
+ id: 5,
+ name: 'One.txt',
+ mimetype: 'text/plain',
+ permissions: 31,
+ path: '/subdir',
+ size: 123456789,
+ etag: 'abcdefg',
+ mtime: Date.UTC(2016, 1, 0, 0, 0, 0)
+ });
+ view.render();
+ var comment1 = new OCA.Comments.CommentModel({
+ id: 1,
+ actorType: 'users',
+ actorId: 'user1',
+ actorDisplayName: 'User One',
+ objectType: 'files',
+ objectId: 5,
+ message: 'First',
+ creationDateTime: Date.UTC(2016, 1, 3, 10, 5, 0)
+ });
+ var comment2 = new OCA.Comments.CommentModel({
+ id: 2,
+ actorType: 'users',
+ actorId: 'user2',
+ actorDisplayName: 'User Two',
+ objectType: 'files',
+ objectId: 5,
+ message: 'Second\nNewline',
+ creationDateTime: Date.UTC(2016, 1, 3, 10, 0, 0)
+ });
+
+ testComments = [comment1, comment2];
+ });
+ afterEach(function() {
+ view.remove();
+ view = undefined;
+ fetchStub.restore();
+ clock.restore();
+ });
+ describe('rendering', function() {
+ it('reloads matching comments when setting file info model', function() {
+ view.setFileInfo(fileInfoModel);
+ expect(fetchStub.calledOnce).toEqual(true);
+ });
+
+ it('renders loading icon while fetching comments', function() {
+ view.setFileInfo(fileInfoModel);
+ view.collection.trigger('request');
+
+ expect(view.$el.find('.loading').length).toEqual(1);
+ expect(view.$el.find('.comments li').length).toEqual(0);
+ });
+
+ it('renders comments', function() {
+
+ view.setFileInfo(fileInfoModel);
+ view.collection.set(testComments);
+
+ var $comments = view.$el.find('.comments>li');
+ expect($comments.length).toEqual(2);
+ var $item = $comments.eq(0);
+ expect($item.find('.author').text()).toEqual('User One');
+ expect($item.find('.date').text()).toEqual('seconds ago');
+ expect($item.find('.message').text()).toEqual('First');
+
+ $item = $comments.eq(1);
+ expect($item.find('.author').text()).toEqual('User Two');
+ expect($item.find('.date').text()).toEqual('5 minutes ago');
+ expect($item.find('.message').html()).toEqual('Second<br>Newline');
+ });
+ });
+ describe('more comments', function() {
+ var hasMoreResultsStub;
+
+ beforeEach(function() {
+ view.collection.set(testComments);
+ hasMoreResultsStub = sinon.stub(OCA.Comments.CommentCollection.prototype, 'hasMoreResults');
+ });
+ afterEach(function() {
+ hasMoreResultsStub.restore();
+ });
+
+ it('shows "More comments" button when more comments are available', function() {
+ hasMoreResultsStub.returns(true);
+ view.collection.trigger('sync');
+
+ expect(view.$el.find('.showMore').hasClass('hidden')).toEqual(false);
+ });
+ it('does not show "More comments" button when more comments are available', function() {
+ hasMoreResultsStub.returns(false);
+ view.collection.trigger('sync');
+
+ expect(view.$el.find('.showMore').hasClass('hidden')).toEqual(true);
+ });
+ it('fetches and appends the next page when clicking the "More" button', function() {
+ hasMoreResultsStub.returns(true);
+
+ expect(fetchStub.notCalled).toEqual(true);
+
+ view.$el.find('.showMore').click();
+
+ expect(fetchStub.calledOnce).toEqual(true);
+ });
+ it('appends comment to the list when added to collection', function() {
+ var comment3 = new OCA.Comments.CommentModel({
+ id: 3,
+ actorType: 'users',
+ actorId: 'user3',
+ actorDisplayName: 'User Three',
+ objectType: 'files',
+ objectId: 5,
+ message: 'Third',
+ creationDateTime: Date.UTC(2016, 1, 3, 5, 0, 0)
+ });
+
+ view.collection.add(comment3);
+
+ expect(view.$el.find('.comments>li').length).toEqual(3);
+
+ var $item = view.$el.find('.comments>li').eq(2);
+ expect($item.find('.author').text()).toEqual('User Three');
+ expect($item.find('.date').text()).toEqual('5 hours ago');
+ expect($item.find('.message').html()).toEqual('Third');
+ });
+ });
+ describe('posting comments', function() {
+ var createStub;
+ var currentUserStub;
+
+ beforeEach(function() {
+ view.collection.set(testComments);
+ createStub = sinon.stub(OCA.Comments.CommentCollection.prototype, 'create');
+ currentUserStub = sinon.stub(OC, 'getCurrentUser');
+ currentUserStub.returns({
+ uid: 'testuser',
+ displayName: 'Test User'
+ });
+ });
+ afterEach(function() {
+ createStub.restore();
+ currentUserStub.restore();
+ });
+
+ it('creates a new comment when clicking post button', function() {
+ view.$el.find('.message').val('New message');
+ view.$el.find('form').submit();
+
+ expect(createStub.calledOnce).toEqual(true);
+ expect(createStub.lastCall.args[0]).toEqual({
+ actorId: 'testuser',
+ actorDisplayName: 'Test User',
+ actorType: 'users',
+ verb: 'comment',
+ message: 'New message',
+ creationDateTime: Date.UTC(2016, 1, 3, 10, 5, 9)
+ });
+ });
+ it('does not create a comment if the field is empty', function() {
+ view.$el.find('.message').val(' ');
+ view.$el.find('form').submit();
+
+ expect(createStub.notCalled).toEqual(true);
+ });
+
+ });
+});
diff --git a/apps/dav/lib/carddav/addressbookimpl.php b/apps/dav/lib/carddav/addressbookimpl.php
index 240e40c9688..1d7b55c1a5d 100644
--- a/apps/dav/lib/carddav/addressbookimpl.php
+++ b/apps/dav/lib/carddav/addressbookimpl.php
@@ -178,7 +178,8 @@ class AddressBookImpl implements IAddressBook {
protected function createUid() {
do {
$uid = $this->getUid();
- } while (!empty($this->backend->getContact($uid . '.vcf')));
+ $contact = $this->backend->getContact($uid . '.vcf');
+ } while (!empty($contact));
return $uid;
}
@@ -213,6 +214,10 @@ class AddressBookImpl implements IAddressBook {
foreach ($vCard->children as $property) {
$result[$property->name] = $property->getValue();
}
+ if ($this->addressBookInfo['principaluri'] === 'principals/system/system' &&
+ $this->addressBookInfo['uri'] === 'system') {
+ $result['isLocalSystemBook'] = true;
+ }
return $result;
}
}
diff --git a/apps/dav/lib/carddav/contactsmanager.php b/apps/dav/lib/carddav/contactsmanager.php
index 3fe4de4a15e..7900c6ccae0 100644
--- a/apps/dav/lib/carddav/contactsmanager.php
+++ b/apps/dav/lib/carddav/contactsmanager.php
@@ -39,8 +39,18 @@ class ContactsManager {
* @param string $userId
*/
public function setupContactsProvider(IManager $cm, $userId) {
- $addressBooks = $this->backend->getAddressBooksForUser("principals/$userId");
- foreach ($addressBooks as $addressBookInfo) {
+ $addressBooks = $this->backend->getAddressBooksForUser("principals/users/$userId");
+ $this->register($cm, $addressBooks);
+ $addressBooks = $this->backend->getAddressBooksForUser("principals/system/system");
+ $this->register($cm, $addressBooks);
+ }
+
+ /**
+ * @param IManager $cm
+ * @param $addressBooks
+ */
+ private function register(IManager $cm, $addressBooks) {
+ foreach ($addressBooks as $addressBookInfo) {
$addressBook = new \OCA\DAV\CardDAV\AddressBook($this->backend, $addressBookInfo);
$cm->registerAddressBook(
new AddressBookImpl(
diff --git a/apps/dav/lib/carddav/converter.php b/apps/dav/lib/carddav/converter.php
index 06d3cb4f18e..c8d9b94c267 100644
--- a/apps/dav/lib/carddav/converter.php
+++ b/apps/dav/lib/carddav/converter.php
@@ -39,7 +39,7 @@ class Converter {
$displayName = empty($displayName ) ? $uid : $displayName;
$emailAddress = $user->getEMailAddress();
$cloudId = $user->getCloudId();
- $image = $user->getAvatarImage(-1);
+ $image = $this->getAvatarImage($user);
$vCard = new VCard();
$vCard->add(new Text($vCard, 'UID', $uid));
@@ -72,7 +72,7 @@ class Converter {
$displayName = empty($displayName ) ? $uid : $displayName;
$emailAddress = $user->getEMailAddress();
$cloudId = $user->getCloudId();
- $image = $user->getAvatarImage(-1);
+ $image = $this->getAvatarImage($user);
$updated = false;
if($this->propertyNeedsUpdate($vCard, 'FN', $displayName)) {
@@ -155,4 +155,17 @@ class Converter {
return $result;
}
+ /**
+ * @param IUser $user
+ * @return null|IImage
+ */
+ private function getAvatarImage(IUser $user) {
+ try {
+ $image = $user->getAvatarImage(-1);
+ return $image;
+ } catch (\Exception $ex) {
+ return null;
+ }
+ }
+
}
diff --git a/apps/dav/lib/comments/commentnode.php b/apps/dav/lib/comments/commentnode.php
index eb26e350a42..a5d508dd198 100644
--- a/apps/dav/lib/comments/commentnode.php
+++ b/apps/dav/lib/comments/commentnode.php
@@ -26,12 +26,17 @@ use OCP\Comments\IComment;
use OCP\Comments\ICommentsManager;
use OCP\ILogger;
use OCP\IUserManager;
+use OCP\IUserSession;
use Sabre\DAV\Exception\MethodNotAllowed;
use Sabre\DAV\PropPatch;
class CommentNode implements \Sabre\DAV\INode, \Sabre\DAV\IProperties {
const NS_OWNCLOUD = 'http://owncloud.org/ns';
+ const PROPERTY_NAME_UNREAD = '{http://owncloud.org/ns}isUnread';
+ const PROPERTY_NAME_MESSAGE = '{http://owncloud.org/ns}message';
+ const PROPERTY_NAME_ACTOR_DISPLAYNAME = '{http://owncloud.org/ns}actorDisplayName';
+
/** @var IComment */
public $comment;
@@ -47,18 +52,23 @@ class CommentNode implements \Sabre\DAV\INode, \Sabre\DAV\IProperties {
/** @var IUserManager */
protected $userManager;
+ /** @var IUserSession */
+ protected $userSession;
+
/**
* CommentNode constructor.
*
* @param ICommentsManager $commentsManager
* @param IComment $comment
* @param IUserManager $userManager
+ * @param IUserSession $userSession
* @param ILogger $logger
*/
public function __construct(
ICommentsManager $commentsManager,
IComment $comment,
IUserManager $userManager,
+ IUserSession $userSession,
ILogger $logger
) {
$this->commentsManager = $commentsManager;
@@ -74,6 +84,7 @@ class CommentNode implements \Sabre\DAV\INode, \Sabre\DAV\IProperties {
$this->properties[$name] = $getter;
}
$this->userManager = $userManager;
+ $this->userSession = $userSession;
}
/**
@@ -87,15 +98,17 @@ class CommentNode implements \Sabre\DAV\INode, \Sabre\DAV\IProperties {
'{http://owncloud.org/ns}parentId',
'{http://owncloud.org/ns}topmostParentId',
'{http://owncloud.org/ns}childrenCount',
- '{http://owncloud.org/ns}message',
'{http://owncloud.org/ns}verb',
'{http://owncloud.org/ns}actorType',
'{http://owncloud.org/ns}actorId',
- '{http://owncloud.org/ns}actorDisplayName',
'{http://owncloud.org/ns}creationDateTime',
'{http://owncloud.org/ns}latestChildDateTime',
'{http://owncloud.org/ns}objectType',
'{http://owncloud.org/ns}objectId',
+ // re-used property names are defined as constants
+ self::PROPERTY_NAME_MESSAGE,
+ self::PROPERTY_NAME_ACTOR_DISPLAYNAME,
+ self::PROPERTY_NAME_UNREAD
];
}
@@ -169,7 +182,7 @@ class CommentNode implements \Sabre\DAV\INode, \Sabre\DAV\IProperties {
*/
function propPatch(PropPatch $propPatch) {
// other properties than 'message' are read only
- $propPatch->handle('{'.self::NS_OWNCLOUD.'}message', [$this, 'updateComment']);
+ $propPatch->handle(self::PROPERTY_NAME_MESSAGE, [$this, 'updateComment']);
}
/**
@@ -201,8 +214,26 @@ class CommentNode implements \Sabre\DAV\INode, \Sabre\DAV\IProperties {
if($this->comment->getActorType() === 'users') {
$user = $this->userManager->get($this->comment->getActorId());
$displayName = is_null($user) ? null : $user->getDisplayName();
- $result['{' . self::NS_OWNCLOUD . '}actorDisplayName'] = $displayName;
+ $result[self::PROPERTY_NAME_ACTOR_DISPLAYNAME] = $displayName;
+ }
+
+ $unread = null;
+ $user = $this->userSession->getUser();
+ if(!is_null($user)) {
+ $readUntil = $this->commentsManager->getReadMark(
+ $this->comment->getObjectType(),
+ $this->comment->getObjectId(),
+ $user
+ );
+ if(is_null($readUntil)) {
+ $unread = 'true';
+ } else {
+ $unread = $this->comment->getCreationDateTime() > $readUntil;
+ // re-format for output
+ $unread = $unread ? 'true' : 'false';
+ }
}
+ $result[self::PROPERTY_NAME_UNREAD] = $unread;
return $result;
}
diff --git a/apps/dav/lib/comments/commentsplugin.php b/apps/dav/lib/comments/commentsplugin.php
index 7e227fd2914..282c14df1e8 100644
--- a/apps/dav/lib/comments/commentsplugin.php
+++ b/apps/dav/lib/comments/commentsplugin.php
@@ -2,7 +2,7 @@
/**
* @author Arthur Schiwon <blizzz@owncloud.com>
*
- * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
* @license AGPL-3.0
*
* This code is free software: you can redistribute it and/or modify
@@ -88,7 +88,7 @@ class CommentsPlugin extends ServerPlugin {
$this->server->xml->namespaceMap[self::NS_OWNCLOUD] = 'oc';
$this->server->xml->classMap['DateTime'] = function(Writer $writer, \DateTime $value) {
- $writer->write($value->format('Y-m-d H:m:i'));
+ $writer->write(\Sabre\HTTP\toDate($value));
};
$this->server->on('report', [$this, 'onReport']);
diff --git a/apps/dav/lib/comments/entitycollection.php b/apps/dav/lib/comments/entitycollection.php
index a93569f6e4f..a55a18c00c0 100644
--- a/apps/dav/lib/comments/entitycollection.php
+++ b/apps/dav/lib/comments/entitycollection.php
@@ -25,7 +25,9 @@ use OCP\Comments\ICommentsManager;
use OCP\Files\Folder;
use OCP\ILogger;
use OCP\IUserManager;
+use OCP\IUserSession;
use Sabre\DAV\Exception\NotFound;
+use Sabre\DAV\PropPatch;
/**
* Class EntityCollection
@@ -35,7 +37,9 @@ use Sabre\DAV\Exception\NotFound;
*
* @package OCA\DAV\Comments
*/
-class EntityCollection extends RootCollection {
+class EntityCollection extends RootCollection implements \Sabre\DAV\IProperties {
+ const PROPERTY_NAME_READ_MARKER = '{http://owncloud.org/ns}readMarker';
+
/** @var Folder */
protected $fileRoot;
@@ -51,6 +55,7 @@ class EntityCollection extends RootCollection {
* @param ICommentsManager $commentsManager
* @param Folder $fileRoot
* @param IUserManager $userManager
+ * @param IUserSession $userSession
* @param ILogger $logger
*/
public function __construct(
@@ -59,6 +64,7 @@ class EntityCollection extends RootCollection {
ICommentsManager $commentsManager,
Folder $fileRoot,
IUserManager $userManager,
+ IUserSession $userSession,
ILogger $logger
) {
foreach(['id', 'name'] as $property) {
@@ -73,6 +79,7 @@ class EntityCollection extends RootCollection {
$this->fileRoot = $fileRoot;
$this->logger = $logger;
$this->userManager = $userManager;
+ $this->userSession = $userSession;
}
/**
@@ -97,7 +104,13 @@ class EntityCollection extends RootCollection {
function getChild($name) {
try {
$comment = $this->commentsManager->get($name);
- return new CommentNode($this->commentsManager, $comment, $this->userManager, $this->logger);
+ return new CommentNode(
+ $this->commentsManager,
+ $comment,
+ $this->userManager,
+ $this->userSession,
+ $this->logger
+ );
} catch (\OCP\Comments\NotFoundException $e) {
throw new NotFound();
}
@@ -125,7 +138,13 @@ class EntityCollection extends RootCollection {
$comments = $this->commentsManager->getForObject($this->name, $this->id, $limit, $offset, $datetime);
$result = [];
foreach($comments as $comment) {
- $result[] = new CommentNode($this->commentsManager, $comment, $this->userManager, $this->logger);
+ $result[] = new CommentNode(
+ $this->commentsManager,
+ $comment,
+ $this->userManager,
+ $this->userSession,
+ $this->logger
+ );
}
return $result;
}
@@ -144,5 +163,37 @@ class EntityCollection extends RootCollection {
return false;
}
}
+
+ /**
+ * Sets the read marker to the specified date for the logged in user
+ *
+ * @param \DateTime $value
+ * @return bool
+ */
+ public function setReadMarker($value) {
+ $dateTime = new \DateTime($value);
+ $user = $this->userSession->getUser();
+ $this->commentsManager->setReadMark($this->name, $this->id, $dateTime, $user);
+ return true;
+ }
+
+ /**
+ * @inheritdoc
+ */
+ function propPatch(PropPatch $propPatch) {
+ $propPatch->handle(self::PROPERTY_NAME_READ_MARKER, [$this, 'setReadMarker']);
+ }
+
+ /**
+ * @inheritdoc
+ */
+ function getProperties($properties) {
+ $marker = null;
+ $user = $this->userSession->getUser();
+ if(!is_null($user)) {
+ $marker = $this->commentsManager->getReadMark($this->name, $this->id, $user);
+ }
+ return [self::PROPERTY_NAME_READ_MARKER => $marker];
+ }
}
diff --git a/apps/dav/lib/comments/entitytypecollection.php b/apps/dav/lib/comments/entitytypecollection.php
index f49aac747c2..6bc42484207 100644
--- a/apps/dav/lib/comments/entitytypecollection.php
+++ b/apps/dav/lib/comments/entitytypecollection.php
@@ -25,6 +25,7 @@ use OCP\Comments\ICommentsManager;
use OCP\Files\Folder;
use OCP\ILogger;
use OCP\IUserManager;
+use OCP\IUserSession;
use Sabre\DAV\Exception\MethodNotAllowed;
use Sabre\DAV\Exception\NotFound;
@@ -51,6 +52,7 @@ class EntityTypeCollection extends RootCollection {
* @param ICommentsManager $commentsManager
* @param Folder $fileRoot
* @param IUserManager $userManager
+ * @param IUserSession $userSession
* @param ILogger $logger
*/
public function __construct(
@@ -58,6 +60,7 @@ class EntityTypeCollection extends RootCollection {
ICommentsManager $commentsManager,
Folder $fileRoot,
IUserManager $userManager,
+ IUserSession $userSession,
ILogger $logger
) {
$name = trim($name);
@@ -69,6 +72,7 @@ class EntityTypeCollection extends RootCollection {
$this->fileRoot = $fileRoot;
$this->logger = $logger;
$this->userManager = $userManager;
+ $this->userSession = $userSession;
}
/**
@@ -91,6 +95,7 @@ class EntityTypeCollection extends RootCollection {
$this->commentsManager,
$this->fileRoot,
$this->userManager,
+ $this->userSession,
$this->logger
);
}
diff --git a/apps/dav/lib/comments/rootcollection.php b/apps/dav/lib/comments/rootcollection.php
index aec8e655667..cda666f7162 100644
--- a/apps/dav/lib/comments/rootcollection.php
+++ b/apps/dav/lib/comments/rootcollection.php
@@ -98,6 +98,7 @@ class RootCollection implements ICollection {
$this->commentsManager,
$userFolder,
$this->userManager,
+ $this->userSession,
$this->logger
);
}
diff --git a/apps/dav/lib/connector/sabre/commentpropertiesplugin.php b/apps/dav/lib/connector/sabre/commentpropertiesplugin.php
new file mode 100644
index 00000000000..a8d5f771122
--- /dev/null
+++ b/apps/dav/lib/connector/sabre/commentpropertiesplugin.php
@@ -0,0 +1,130 @@
+<?php
+/**
+ * @author Arthur Schiwon <blizzz@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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 OCA\DAV\Connector\Sabre;
+
+use OCP\Comments\ICommentsManager;
+use OCP\IUserSession;
+use Sabre\DAV\PropFind;
+use Sabre\DAV\ServerPlugin;
+
+class CommentPropertiesPlugin extends ServerPlugin {
+
+ const PROPERTY_NAME_HREF = '{http://owncloud.org/ns}comments-href';
+ const PROPERTY_NAME_COUNT = '{http://owncloud.org/ns}comments-count';
+ const PROPERTY_NAME_UNREAD = '{http://owncloud.org/ns}comments-unread';
+
+ /** @var \Sabre\DAV\Server */
+ protected $server;
+
+ /** @var ICommentsManager */
+ private $commentsManager;
+
+ /** @var IUserSession */
+ private $userSession;
+
+ public function __construct(ICommentsManager $commentsManager, IUserSession $userSession) {
+ $this->commentsManager = $commentsManager;
+ $this->userSession = $userSession;
+ }
+
+ /**
+ * This initializes the plugin.
+ *
+ * This function is called by Sabre\DAV\Server, after
+ * addPlugin is called.
+ *
+ * This method should set up the required event subscriptions.
+ *
+ * @param \Sabre\DAV\Server $server
+ * @return void
+ */
+ function initialize(\Sabre\DAV\Server $server) {
+ $this->server = $server;
+ $this->server->on('propFind', array($this, 'handleGetProperties'));
+ }
+
+ /**
+ * Adds tags and favorites properties to the response,
+ * if requested.
+ *
+ * @param PropFind $propFind
+ * @param \Sabre\DAV\INode $node
+ * @return void
+ */
+ public function handleGetProperties(
+ PropFind $propFind,
+ \Sabre\DAV\INode $node
+ ) {
+ if (!($node instanceof File) && !($node instanceof Directory)) {
+ return;
+ }
+
+ $propFind->handle(self::PROPERTY_NAME_COUNT, function() use ($node) {
+ return $this->commentsManager->getNumberOfCommentsForObject('files', strval($node->getId()));
+ });
+
+ $propFind->handle(self::PROPERTY_NAME_HREF, function() use ($node) {
+ return $this->getCommentsLink($node);
+ });
+
+ $propFind->handle(self::PROPERTY_NAME_UNREAD, function() use ($node) {
+ return $this->getUnreadCount($node);
+ });
+ }
+
+ /**
+ * returns a reference to the comments node
+ *
+ * @param Node $node
+ * @return mixed|string
+ */
+ public function getCommentsLink(Node $node) {
+ $href = $this->server->getBaseUri();
+ $entryPoint = strrpos($href, '/webdav/');
+ if($entryPoint === false) {
+ // in case we end up somewhere else, unexpectedly.
+ return null;
+ }
+ $href = substr_replace($href, '/dav/', $entryPoint);
+ $href .= 'comments/files/' . rawurldecode($node->getId());
+ return $href;
+ }
+
+ /**
+ * returns the number of unread comments for the currently logged in user
+ * on the given file or directory node
+ *
+ * @param Node $node
+ * @return Int|null
+ */
+ public function getUnreadCount(Node $node) {
+ $user = $this->userSession->getUser();
+ if(is_null($user)) {
+ return null;
+ }
+
+ $lastRead = $this->commentsManager->getReadMark('files', strval($node->getId()), $user);
+
+ return $this->commentsManager->getNumberOfCommentsForObject('files', strval($node->getId()), $lastRead);
+ }
+
+}
diff --git a/apps/dav/lib/connector/sabre/serverfactory.php b/apps/dav/lib/connector/sabre/serverfactory.php
index ee782f79a81..fa4fda46870 100644
--- a/apps/dav/lib/connector/sabre/serverfactory.php
+++ b/apps/dav/lib/connector/sabre/serverfactory.php
@@ -134,6 +134,7 @@ class ServerFactory {
if($this->userSession->isLoggedIn()) {
$server->addPlugin(new \OCA\DAV\Connector\Sabre\TagsPlugin($objectTree, $this->tagManager));
+ $server->addPlugin(new \OCA\DAV\Connector\Sabre\CommentPropertiesPlugin(\OC::$server->getCommentsManager(), $this->userSession));
// custom properties plugin must be the last one
$server->addPlugin(
new \Sabre\DAV\PropertyStorage\Plugin(
diff --git a/apps/dav/lib/rootcollection.php b/apps/dav/lib/rootcollection.php
index b7c2995dbad..5be469e7b0c 100644
--- a/apps/dav/lib/rootcollection.php
+++ b/apps/dav/lib/rootcollection.php
@@ -68,7 +68,8 @@ class RootCollection extends SimpleCollection {
\OC::$server->getSystemTagManager(),
\OC::$server->getSystemTagObjectMapper(),
\OC::$server->getUserSession(),
- \OC::$server->getGroupManager()
+ \OC::$server->getGroupManager(),
+ \OC::$server->getRootFolder()
);
$commentsCollection = new Comments\RootCollection(
\OC::$server->getCommentsManager(),
diff --git a/apps/dav/lib/systemtag/systemtagplugin.php b/apps/dav/lib/systemtag/systemtagplugin.php
index 4636ed428b1..3348b431c47 100644
--- a/apps/dav/lib/systemtag/systemtagplugin.php
+++ b/apps/dav/lib/systemtag/systemtagplugin.php
@@ -104,12 +104,7 @@ class SystemTagPlugin extends \Sabre\DAV\ServerPlugin {
$path = $request->getPath();
// Making sure the node exists
- try {
- $node = $this->server->tree->getNodeForPath($path);
- } catch (NotFound $e) {
- return null;
- }
-
+ $node = $this->server->tree->getNodeForPath($path);
if ($node instanceof SystemTagsByIdCollection || $node instanceof SystemTagsObjectMappingCollection) {
$data = $request->getBodyAsString();
diff --git a/apps/dav/lib/systemtag/systemtagsobjecttypecollection.php b/apps/dav/lib/systemtag/systemtagsobjecttypecollection.php
index 166e3219bc1..93cc7561928 100644
--- a/apps/dav/lib/systemtag/systemtagsobjecttypecollection.php
+++ b/apps/dav/lib/systemtag/systemtagsobjecttypecollection.php
@@ -25,12 +25,14 @@ namespace OCA\DAV\SystemTag;
use Sabre\DAV\Exception\Forbidden;
use Sabre\DAV\Exception\MethodNotAllowed;
+use Sabre\DAV\Exception\NotFound;
use Sabre\DAV\ICollection;
use OCP\SystemTag\ISystemTagManager;
use OCP\SystemTag\ISystemTagObjectMapper;
use OCP\IUserSession;
use OCP\IGroupManager;
+use OCP\Files\IRootFolder;
/**
* Collection containing object ids by object type
@@ -63,6 +65,11 @@ class SystemTagsObjectTypeCollection implements ICollection {
private $userSession;
/**
+ * @var IRootFolder
+ **/
+ protected $fileRoot;
+
+ /**
* Constructor
*
* @param string $objectType object type
@@ -70,19 +77,22 @@ class SystemTagsObjectTypeCollection implements ICollection {
* @param ISystemTagObjectMapper $tagMapper
* @param IUserSession $userSession
* @param IGroupManager $groupManager
+ * @param IRootFolder $fileRoot
*/
public function __construct(
$objectType,
ISystemTagManager $tagManager,
ISystemTagObjectMapper $tagMapper,
IUserSession $userSession,
- IGroupManager $groupManager
+ IGroupManager $groupManager,
+ IRootFolder $fileRoot
) {
$this->tagManager = $tagManager;
$this->tagMapper = $tagMapper;
$this->objectType = $objectType;
$this->userSession = $userSession;
$this->groupManager = $groupManager;
+ $this->fileRoot = $fileRoot;
}
/**
@@ -116,6 +126,10 @@ class SystemTagsObjectTypeCollection implements ICollection {
* @param string $objectId
*/
function getChild($objectId) {
+ // make sure the object exists and is reachable
+ if(!$this->childExists($objectId)) {
+ throw new NotFound('Entity does not exist or is not available');
+ }
return new SystemTagsObjectMappingCollection(
$objectId,
$this->objectType,
@@ -134,6 +148,13 @@ class SystemTagsObjectTypeCollection implements ICollection {
* @param string $name
*/
function childExists($name) {
+ // TODO: make this more abstract
+ if ($this->objectType === 'files') {
+ // make sure the object is reachable for the current user
+ $userId = $this->userSession->getUser()->getUID();
+ $nodes = $this->fileRoot->getUserFolder($userId)->getById(intval($name));
+ return !empty($nodes);
+ }
return true;
}
diff --git a/apps/dav/lib/systemtag/systemtagsrelationscollection.php b/apps/dav/lib/systemtag/systemtagsrelationscollection.php
index 223aaac93d6..6af4edfc1ab 100644
--- a/apps/dav/lib/systemtag/systemtagsrelationscollection.php
+++ b/apps/dav/lib/systemtag/systemtagsrelationscollection.php
@@ -28,6 +28,7 @@ use Sabre\DAV\Exception\Forbidden;
use Sabre\DAV\SimpleCollection;
use OCP\IUserSession;
use OCP\IGroupManager;
+use OCP\Files\IRootFolder;
class SystemTagsRelationsCollection extends SimpleCollection {
@@ -38,12 +39,14 @@ class SystemTagsRelationsCollection extends SimpleCollection {
* @param ISystemTagObjectMapper $tagMapper
* @param IUserSession $userSession
* @param IGroupManager $groupManager
+ * @param IRootFolder $fileRoot
*/
public function __construct(
ISystemTagManager $tagManager,
ISystemTagObjectMapper $tagMapper,
IUserSession $userSession,
- IGroupManager $groupManager
+ IGroupManager $groupManager,
+ IRootFolder $fileRoot
) {
$children = [
new SystemTagsObjectTypeCollection(
@@ -51,7 +54,8 @@ class SystemTagsRelationsCollection extends SimpleCollection {
$tagManager,
$tagMapper,
$userSession,
- $groupManager
+ $groupManager,
+ $fileRoot
),
];
diff --git a/apps/dav/tests/unit/appinfo/applicationtest.php b/apps/dav/tests/unit/appinfo/applicationtest.php
index 36a75212ff5..7f533a185df 100644
--- a/apps/dav/tests/unit/appinfo/applicationtest.php
+++ b/apps/dav/tests/unit/appinfo/applicationtest.php
@@ -35,17 +35,28 @@ use Test\TestCase;
class ApplicationTest extends TestCase {
public function test() {
$app = new Application();
+ $c = $app->getContainer();
// assert service instances in the container are properly setup
- $s = $app->getContainer()->query('ContactsManager');
+ $s = $c->query('ContactsManager');
$this->assertInstanceOf('OCA\DAV\CardDAV\ContactsManager', $s);
- $s = $app->getContainer()->query('CardDavBackend');
+ $s = $c->query('CardDavBackend');
$this->assertInstanceOf('OCA\DAV\CardDAV\CardDavBackend', $s);
+ }
+
+ public function testContactsManagerSetup() {
+ $app = new Application();
+ $c = $app->getContainer();
+ $c->registerService('CardDavBackend', function($c) {
+ $service = $this->getMockBuilder('OCA\DAV\CardDAV\CardDavBackend')->disableOriginalConstructor()->getMock();
+ $service->method('getAddressBooksForUser')->willReturn([]);
+ return $service;
+ });
// assert setupContactsProvider() is proper
/** @var IManager | \PHPUnit_Framework_MockObject_MockObject $cm */
$cm = $this->getMockBuilder('OCP\Contacts\IManager')->disableOriginalConstructor()->getMock();
- $app->setupContactsProvider($cm, 'user01');
+ $app->setupContactsProvider($cm, 'xxx');
$this->assertTrue(true);
}
}
diff --git a/apps/dav/tests/unit/carddav/contactsmanagertest.php b/apps/dav/tests/unit/carddav/contactsmanagertest.php
index c4ec4c29e39..5a384550df5 100644
--- a/apps/dav/tests/unit/carddav/contactsmanagertest.php
+++ b/apps/dav/tests/unit/carddav/contactsmanagertest.php
@@ -30,7 +30,7 @@ class ContactsManagerTest extends TestCase {
public function test() {
/** @var IManager | \PHPUnit_Framework_MockObject_MockObject $cm */
$cm = $this->getMockBuilder('OCP\Contacts\IManager')->disableOriginalConstructor()->getMock();
- $cm->expects($this->once())->method('registerAddressBook');
+ $cm->expects($this->exactly(2))->method('registerAddressBook');
/** @var CardDavBackend | \PHPUnit_Framework_MockObject_MockObject $backEnd */
$backEnd = $this->getMockBuilder('OCA\DAV\CardDAV\CardDavBackend')->disableOriginalConstructor()->getMock();
$backEnd->method('getAddressBooksForUser')->willReturn([
diff --git a/apps/dav/tests/unit/comments/commentnode.php b/apps/dav/tests/unit/comments/commentnode.php
index aa75e35d665..44ac54ae937 100644
--- a/apps/dav/tests/unit/comments/commentnode.php
+++ b/apps/dav/tests/unit/comments/commentnode.php
@@ -30,6 +30,7 @@ class CommentsNode extends \Test\TestCase {
protected $node;
protected $userManager;
protected $logger;
+ protected $userSession;
public function setUp() {
parent::setUp();
@@ -37,9 +38,16 @@ class CommentsNode extends \Test\TestCase {
$this->commentsManager = $this->getMock('\OCP\Comments\ICommentsManager');
$this->comment = $this->getMock('\OCP\Comments\IComment');
$this->userManager = $this->getMock('\OCP\IUserManager');
+ $this->userSession = $this->getMock('\OCP\IUserSession');
$this->logger = $this->getMock('\OCP\ILogger');
- $this->node = new CommentNode($this->commentsManager, $this->comment, $this->userManager, $this->logger);
+ $this->node = new CommentNode(
+ $this->commentsManager,
+ $this->comment,
+ $this->userManager,
+ $this->userSession,
+ $this->logger
+ );
}
public function testDelete() {
@@ -133,6 +141,7 @@ class CommentsNode extends \Test\TestCase {
$ns . 'latestChildDateTime' => new \DateTime('2016-01-12 18:48:00'),
$ns . 'objectType' => 'files',
$ns . 'objectId' => '1848',
+ $ns . 'isUnread' => null,
];
$this->comment->expects($this->once())
@@ -198,10 +207,45 @@ class CommentsNode extends \Test\TestCase {
$properties = $this->node->getProperties(null);
foreach($properties as $name => $value) {
- $this->assertTrue(isset($expected[$name]));
+ $this->assertTrue(array_key_exists($name, $expected));
$this->assertSame($expected[$name], $value);
unset($expected[$name]);
}
$this->assertTrue(empty($expected));
}
+
+ public function readCommentProvider() {
+ $creationDT = new \DateTime('2016-01-19 18:48:00');
+ $diff = new \DateInterval('PT2H');
+ $readDT1 = clone $creationDT; $readDT1->sub($diff);
+ $readDT2 = clone $creationDT; $readDT2->add($diff);
+ return [
+ [$creationDT, $readDT1, 'true'],
+ [$creationDT, $readDT2, 'false'],
+ [$creationDT, null, 'true'],
+ ];
+ }
+
+ /**
+ * @dataProvider readCommentProvider
+ * @param $expected
+ */
+ public function testGetPropertiesUnreadProperty($creationDT, $readDT, $expected) {
+ $this->comment->expects($this->any())
+ ->method('getCreationDateTime')
+ ->will($this->returnValue($creationDT));
+
+ $this->commentsManager->expects($this->once())
+ ->method('getReadMark')
+ ->will($this->returnValue($readDT));
+
+ $this->userSession->expects($this->once())
+ ->method('getUser')
+ ->will($this->returnValue($this->getMock('\OCP\IUser')));
+
+ $properties = $this->node->getProperties(null);
+
+ $this->assertTrue(array_key_exists(CommentNode::PROPERTY_NAME_UNREAD, $properties));
+ $this->assertSame($properties[CommentNode::PROPERTY_NAME_UNREAD], $expected);
+ }
}
diff --git a/apps/dav/tests/unit/comments/entitycollection.php b/apps/dav/tests/unit/comments/entitycollection.php
index 81442c7a873..5bf155f12ba 100644
--- a/apps/dav/tests/unit/comments/entitycollection.php
+++ b/apps/dav/tests/unit/comments/entitycollection.php
@@ -28,6 +28,7 @@ class EntityCollection extends \Test\TestCase {
protected $userManager;
protected $logger;
protected $collection;
+ protected $userSession;
public function setUp() {
parent::setUp();
@@ -35,6 +36,7 @@ class EntityCollection extends \Test\TestCase {
$this->commentsManager = $this->getMock('\OCP\Comments\ICommentsManager');
$this->folder = $this->getMock('\OCP\Files\Folder');
$this->userManager = $this->getMock('\OCP\IUserManager');
+ $this->userSession = $this->getMock('\OCP\IUserSession');
$this->logger = $this->getMock('\OCP\ILogger');
$this->collection = new \OCA\DAV\Comments\EntityCollection(
@@ -43,6 +45,7 @@ class EntityCollection extends \Test\TestCase {
$this->commentsManager,
$this->folder,
$this->userManager,
+ $this->userSession,
$this->logger
);
}
diff --git a/apps/dav/tests/unit/comments/entitytypecollection.php b/apps/dav/tests/unit/comments/entitytypecollection.php
index e8a88c4e2cb..f3aa2dbd71f 100644
--- a/apps/dav/tests/unit/comments/entitytypecollection.php
+++ b/apps/dav/tests/unit/comments/entitytypecollection.php
@@ -30,6 +30,7 @@ class EntityTypeCollection extends \Test\TestCase {
protected $userManager;
protected $logger;
protected $collection;
+ protected $userSession;
public function setUp() {
parent::setUp();
@@ -37,6 +38,7 @@ class EntityTypeCollection extends \Test\TestCase {
$this->commentsManager = $this->getMock('\OCP\Comments\ICommentsManager');
$this->folder = $this->getMock('\OCP\Files\Folder');
$this->userManager = $this->getMock('\OCP\IUserManager');
+ $this->userSession = $this->getMock('\OCP\IUserSession');
$this->logger = $this->getMock('\OCP\ILogger');
$this->collection = new \OCA\DAV\Comments\EntityTypeCollection(
@@ -44,6 +46,7 @@ class EntityTypeCollection extends \Test\TestCase {
$this->commentsManager,
$this->folder,
$this->userManager,
+ $this->userSession,
$this->logger
);
}
diff --git a/apps/dav/tests/unit/connector/sabre/commentpropertiesplugin.php b/apps/dav/tests/unit/connector/sabre/commentpropertiesplugin.php
new file mode 100644
index 00000000000..f915c83c4a7
--- /dev/null
+++ b/apps/dav/tests/unit/connector/sabre/commentpropertiesplugin.php
@@ -0,0 +1,148 @@
+<?php
+/**
+ * @author Arthur Schiwon <blizzz@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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 OCA\DAV\Tests\Unit\Connector\Sabre;
+
+use \OCA\DAV\Connector\Sabre\CommentPropertiesPlugin as CommentPropertiesPluginImplementation;
+
+class CommentsPropertiesPlugin extends \Test\TestCase {
+
+ /** @var CommentPropertiesPluginImplementation */
+ protected $plugin;
+ protected $commentsManager;
+ protected $userSession;
+ protected $server;
+
+ public function setUp() {
+ parent::setUp();
+
+ $this->commentsManager = $this->getMock('\OCP\Comments\ICommentsManager');
+ $this->userSession = $this->getMock('\OCP\IUserSession');
+
+ $this->server = $this->getMockBuilder('\Sabre\DAV\Server')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->plugin = new CommentPropertiesPluginImplementation($this->commentsManager, $this->userSession);
+ $this->plugin->initialize($this->server);
+ }
+
+ public function nodeProvider() {
+ $mocks = [];
+ foreach(['\OCA\DAV\Connector\Sabre\File', '\OCA\DAV\Connector\Sabre\Directory', '\Sabre\DAV\INode'] as $class) {
+ $mocks[] = $this->getMockBuilder($class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ }
+
+ return [
+ [$mocks[0], true],
+ [$mocks[1], true],
+ [$mocks[2], false]
+ ];
+ }
+
+ /**
+ * @dataProvider nodeProvider
+ * @param $node
+ * @param $expectedSuccessful
+ */
+ public function testHandleGetProperties($node, $expectedSuccessful) {
+ $propFind = $this->getMockBuilder('\Sabre\DAV\PropFind')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ if($expectedSuccessful) {
+ $propFind->expects($this->exactly(3))
+ ->method('handle');
+ } else {
+ $propFind->expects($this->never())
+ ->method('handle');
+ }
+
+ $this->plugin->handleGetProperties($propFind, $node);
+ }
+
+ public function baseUriProvider() {
+ return [
+ ['owncloud/remote.php/webdav/', '4567', 'owncloud/remote.php/dav/comments/files/4567'],
+ ['owncloud/remote.php/wicked/', '4567', null]
+ ];
+ }
+
+ /**
+ * @dataProvider baseUriProvider
+ * @param $baseUri
+ * @param $fid
+ * @param $expectedHref
+ */
+ public function testGetCommentsLink($baseUri, $fid, $expectedHref) {
+ $node = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\File')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $node->expects($this->any())
+ ->method('getId')
+ ->will($this->returnValue($fid));
+
+ $this->server->expects($this->once())
+ ->method('getBaseUri')
+ ->will($this->returnValue($baseUri));
+
+ $href = $this->plugin->getCommentsLink($node);
+ $this->assertSame($expectedHref, $href);
+ }
+
+ public function userProvider() {
+ return [
+ [$this->getMock('\OCP\IUser')],
+ [null]
+ ];
+ }
+
+ /**
+ * @dataProvider userProvider
+ * @param $user
+ */
+ public function testGetUnreadCount($user) {
+ $node = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\File')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $node->expects($this->any())
+ ->method('getId')
+ ->will($this->returnValue('4567'));
+
+ $this->userSession->expects($this->once())
+ ->method('getUser')
+ ->will($this->returnValue($user));
+
+ $this->commentsManager->expects($this->any())
+ ->method('getNumberOfCommentsForObject')
+ ->will($this->returnValue(42));
+
+ $unread = $this->plugin->getUnreadCount($node);
+ if(is_null($user)) {
+ $this->assertNull($unread);
+ } else {
+ $this->assertSame($unread, 42);
+ }
+ }
+
+}
diff --git a/apps/dav/tests/unit/systemtag/systemtagplugin.php b/apps/dav/tests/unit/systemtag/systemtagplugin.php
index 873dd7088a8..b026451701f 100644
--- a/apps/dav/tests/unit/systemtag/systemtagplugin.php
+++ b/apps/dav/tests/unit/systemtag/systemtagplugin.php
@@ -272,6 +272,40 @@ class SystemTagPlugin extends \Test\TestCase {
}
/**
+ * @expectedException \Sabre\DAV\Exception\NotFound
+ */
+ public function testCreateTagToUnknownNode() {
+ $systemTag = new SystemTag(1, 'Test', true, false);
+
+ $node = $this->getMockBuilder('\OCA\DAV\SystemTag\SystemTagsObjectMappingCollection')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->tree->expects($this->any())
+ ->method('getNodeForPath')
+ ->will($this->throwException(new \Sabre\DAV\Exception\NotFound()));
+
+ $this->tagManager->expects($this->never())
+ ->method('createTag');
+
+ $node->expects($this->never())
+ ->method('createFile');
+
+ $request = $this->getMockBuilder('Sabre\HTTP\RequestInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $response = $this->getMockBuilder('Sabre\HTTP\ResponseInterface')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $request->expects($this->once())
+ ->method('getPath')
+ ->will($this->returnValue('/systemtags-relations/files/12'));
+
+ $this->plugin->httpPost($request, $response);
+ }
+
+ /**
* @dataProvider nodeClassProvider
* @expectedException Sabre\DAV\Exception\Conflict
*/
diff --git a/apps/dav/tests/unit/systemtag/systemtagsobjecttypecollection.php b/apps/dav/tests/unit/systemtag/systemtagsobjecttypecollection.php
index e6d94803cc0..1d4264f94f9 100644
--- a/apps/dav/tests/unit/systemtag/systemtagsobjecttypecollection.php
+++ b/apps/dav/tests/unit/systemtag/systemtagsobjecttypecollection.php
@@ -38,6 +38,11 @@ class SystemTagsObjectTypeCollection extends \Test\TestCase {
*/
private $tagMapper;
+ /**
+ * @var \OCP\Files\Folder
+ */
+ private $userFolder;
+
protected function setUp() {
parent::setUp();
@@ -58,12 +63,21 @@ class SystemTagsObjectTypeCollection extends \Test\TestCase {
->with('testuser')
->will($this->returnValue(true));
+ $this->userFolder = $this->getMock('\OCP\Files\Folder');
+
+ $fileRoot = $this->getMock('\OCP\Files\IRootFolder');
+ $fileRoot->expects($this->any())
+ ->method('getUserfolder')
+ ->with('testuser')
+ ->will($this->returnValue($this->userFolder));
+
$this->node = new \OCA\DAV\SystemTag\SystemTagsObjectTypeCollection(
'files',
$this->tagManager,
$this->tagMapper,
$userSession,
- $groupManager
+ $groupManager,
+ $fileRoot
);
}
@@ -82,10 +96,25 @@ class SystemTagsObjectTypeCollection extends \Test\TestCase {
}
public function testGetChild() {
- $childNode = $this->node->getChild('files');
+ $this->userFolder->expects($this->once())
+ ->method('getById')
+ ->with('555')
+ ->will($this->returnValue([true]));
+ $childNode = $this->node->getChild('555');
$this->assertInstanceOf('\OCA\DAV\SystemTag\SystemTagsObjectMappingCollection', $childNode);
- $this->assertEquals('files', $childNode->getName());
+ $this->assertEquals('555', $childNode->getName());
+ }
+
+ /**
+ * @expectedException Sabre\DAV\Exception\NotFound
+ */
+ public function testGetChildWithoutAccess() {
+ $this->userFolder->expects($this->once())
+ ->method('getById')
+ ->with('555')
+ ->will($this->returnValue([]));
+ $this->node->getChild('555');
}
/**
@@ -96,9 +125,21 @@ class SystemTagsObjectTypeCollection extends \Test\TestCase {
}
public function testChildExists() {
+ $this->userFolder->expects($this->once())
+ ->method('getById')
+ ->with('123')
+ ->will($this->returnValue([true]));
$this->assertTrue($this->node->childExists('123'));
}
+ public function testChildExistsWithoutAccess() {
+ $this->userFolder->expects($this->once())
+ ->method('getById')
+ ->with('555')
+ ->will($this->returnValue([]));
+ $this->assertFalse($this->node->childExists('555'));
+ }
+
/**
* @expectedException Sabre\DAV\Exception\Forbidden
*/
diff --git a/apps/federation/api/ocsauthapi.php b/apps/federation/api/ocsauthapi.php
index 083be1b7ecd..058a5966374 100644
--- a/apps/federation/api/ocsauthapi.php
+++ b/apps/federation/api/ocsauthapi.php
@@ -99,7 +99,7 @@ class OCSAuthAPI {
$token = $this->request->getParam('token');
if ($this->trustedServers->isTrustedServer($url) === false) {
- $this->logger->log(\OCP\Util::ERROR, 'remote server not trusted (' . $url . ') while requesting shared secret');
+ $this->logger->log(\OCP\Util::ERROR, 'remote server not trusted (' . $url . ') while requesting shared secret', ['app' => 'federation']);
return new \OC_OCS_Result(null, HTTP::STATUS_FORBIDDEN);
}
@@ -107,7 +107,7 @@ class OCSAuthAPI {
// token wins
$localToken = $this->dbHandler->getToken($url);
if (strcmp($localToken, $token) > 0) {
- $this->logger->log(\OCP\Util::ERROR, 'remote server (' . $url . ') presented lower token');
+ $this->logger->log(\OCP\Util::ERROR, 'remote server (' . $url . ') presented lower token', ['app' => 'federation']);
return new \OC_OCS_Result(null, HTTP::STATUS_FORBIDDEN);
}
@@ -134,12 +134,12 @@ class OCSAuthAPI {
$token = $this->request->getParam('token');
if ($this->trustedServers->isTrustedServer($url) === false) {
- $this->logger->log(\OCP\Util::ERROR, 'remote server not trusted (' . $url . ') while getting shared secret');
+ $this->logger->log(\OCP\Util::ERROR, 'remote server not trusted (' . $url . ') while getting shared secret', ['app' => 'federation']);
return new \OC_OCS_Result(null, HTTP::STATUS_FORBIDDEN);
}
if ($this->isValidToken($url, $token) === false) {
- $this->logger->log(\OCP\Util::ERROR, 'remote server (' . $url . ') didn\'t send a valid token (got ' . $token . ') while getting shared secret');
+ $this->logger->log(\OCP\Util::ERROR, 'remote server (' . $url . ') didn\'t send a valid token (got ' . $token . ') while getting shared secret', ['app' => 'federation']);
return new \OC_OCS_Result(null, HTTP::STATUS_FORBIDDEN);
}
diff --git a/apps/federation/backgroundjob/getsharedsecret.php b/apps/federation/backgroundjob/getsharedsecret.php
index cae446f915c..a98a17e323b 100644
--- a/apps/federation/backgroundjob/getsharedsecret.php
+++ b/apps/federation/backgroundjob/getsharedsecret.php
@@ -151,6 +151,9 @@ class GetSharedSecret extends QueuedJob{
} catch (ClientException $e) {
$status = $e->getCode();
$this->logger->logException($e);
+ } catch (\Exception $e) {
+ $status = HTTP::STATUS_INTERNAL_SERVER_ERROR;
+ $this->logger->logException($e);
}
// if we received a unexpected response we try again later
diff --git a/apps/federation/backgroundjob/requestsharedsecret.php b/apps/federation/backgroundjob/requestsharedsecret.php
index 92305b7e8ea..2db5d09545a 100644
--- a/apps/federation/backgroundjob/requestsharedsecret.php
+++ b/apps/federation/backgroundjob/requestsharedsecret.php
@@ -149,6 +149,9 @@ class RequestSharedSecret extends QueuedJob {
} catch (ClientException $e) {
$status = $e->getCode();
$this->logger->logException($e);
+ } catch (\Exception $e) {
+ $status = HTTP::STATUS_INTERNAL_SERVER_ERROR;
+ $this->logger->logException($e);
}
// if we received a unexpected response we try again later
diff --git a/apps/federation/lib/syncfederationaddressbooks.php b/apps/federation/lib/syncfederationaddressbooks.php
index 86fc4179dc4..6419fdddf8e 100644
--- a/apps/federation/lib/syncfederationaddressbooks.php
+++ b/apps/federation/lib/syncfederationaddressbooks.php
@@ -46,7 +46,7 @@ class SyncFederationAddressBooks {
'{DAV:}displayname' => $url
];
try {
- $newToken = $this->syncService->syncRemoteAddressBook($url, 'system', $sharedSecret, $syncToken, $targetPrincipal, $targetBookId, $targetBookProperties);
+ $newToken = $this->syncService->syncRemoteAddressBook($url, 'system', $sharedSecret, $syncToken, $targetBookId, $targetPrincipal, $targetBookProperties);
if ($newToken !== $syncToken) {
$this->dbHandler->setServerStatus($url, TrustedServers::STATUS_OK, $newToken);
}
diff --git a/apps/files/command/scan.php b/apps/files/command/scan.php
index 4a68429897a..3251682445c 100644
--- a/apps/files/command/scan.php
+++ b/apps/files/command/scan.php
@@ -26,20 +26,19 @@
namespace OCA\Files\Command;
+use OC\Core\Command\Base;
use OC\ForbiddenException;
use OCP\Files\StorageNotAvailableException;
-use Symfony\Component\Console\Command\Command;
+use OCP\IUserManager;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Helper\Table;
-class Scan extends Command {
+class Scan extends Base {
- /**
- * @var \OC\User\Manager $userManager
- */
+ /** @var IUserManager $userManager */
private $userManager;
/** @var float */
protected $execTime = 0;
@@ -47,19 +46,15 @@ class Scan extends Command {
protected $foldersCounter = 0;
/** @var int */
protected $filesCounter = 0;
- /** @var bool */
- protected $interrupted = false;
- /** @var bool */
- protected $php_pcntl_signal = true;
-
-
- public function __construct(\OC\User\Manager $userManager) {
+ public function __construct(IUserManager $userManager) {
$this->userManager = $userManager;
parent::__construct();
}
protected function configure() {
+ parent::configure();
+
$this
->setName('files:scan')
->setDescription('rescan filesystem')
@@ -96,7 +91,7 @@ class Scan extends Command {
protected function scanFiles($user, $path, $verbose, OutputInterface $output) {
$scanner = new \OC\Files\Utils\Scanner($user, \OC::$server->getDatabaseConnection(), \OC::$server->getLogger());
- # check on each file/folder if there was a user interrupt (ctrl-c) and throw an exeption
+ # check on each file/folder if there was a user interrupt (ctrl-c) and throw an exception
# printout and count
if ($verbose) {
$scanner->listen('\OC\Files\Utils\Scanner', 'scanFile', function ($path) use ($output) {
@@ -118,13 +113,13 @@ class Scan extends Command {
});
# count only
} else {
- $scanner->listen('\OC\Files\Utils\Scanner', 'scanFile', function ($path) use ($output) {
+ $scanner->listen('\OC\Files\Utils\Scanner', 'scanFile', function () use ($output) {
$this->filesCounter += 1;
if ($this->hasBeenInterrupted()) {
throw new \Exception('ctrl-c');
}
});
- $scanner->listen('\OC\Files\Utils\Scanner', 'scanFolder', function ($path) use ($output) {
+ $scanner->listen('\OC\Files\Utils\Scanner', 'scanFolder', function () use ($output) {
$this->foldersCounter += 1;
if ($this->hasBeenInterrupted()) {
throw new \Exception('ctrl-c');
@@ -194,7 +189,7 @@ class Scan extends Command {
$path = $inputPath ? $inputPath : '/' . $user;
$user_count += 1;
if ($this->userManager->userExists($user)) {
- # add an extra line when verbose is set to optical seperate users
+ # add an extra line when verbose is set to optical separate users
if ($verbose) {$output->writeln(""); }
$output->writeln("Starting scan for user $user_count out of $users_total ($user)");
# full: printout data if $verbose was set
@@ -212,10 +207,8 @@ class Scan extends Command {
if (!$quiet) {
$this->presentStats($output);
}
-
}
-
/**
* Initialises some useful tools for the Command
*/
@@ -224,46 +217,8 @@ class Scan extends Command {
$this->execTime = -microtime(true);
// Convert PHP errors to exceptions
set_error_handler([$this, 'exceptionErrorHandler'], E_ALL);
-
- // check if the php pcntl_signal functions are accessible
- if (function_exists('pcntl_signal')) {
- // Collect interrupts and notify the running command
- pcntl_signal(SIGTERM, [$this, 'cancelOperation']);
- pcntl_signal(SIGINT, [$this, 'cancelOperation']);
- } else {
- $this->php_pcntl_signal = false;
- }
- }
-
-
- /**
- * Changes the status of the command to "interrupted" if ctrl-c has been pressed
- *
- * Gives a chance to the command to properly terminate what it's doing
- */
- private function cancelOperation() {
- $this->interrupted = true;
}
-
- /**
- * @return bool
- */
- protected function hasBeenInterrupted() {
- // return always false if pcntl_signal functions are not accessible
- if ($this->php_pcntl_signal) {
- pcntl_signal_dispatch();
- if ($this->interrupted) {
- return true;
- } else {
- return false;
- }
- } else {
- return false;
- }
- }
-
-
/**
* Processes PHP errors as exceptions in order to be able to keep track of problems
*
@@ -284,7 +239,6 @@ class Scan extends Command {
throw new \ErrorException($message, 0, $severity, $file, $line);
}
-
/**
* @param OutputInterface $output
*/
@@ -300,7 +254,6 @@ class Scan extends Command {
$this->showSummary($headers, null, $output);
}
-
/**
* Shows a summary of operations
*
@@ -332,10 +285,9 @@ class Scan extends Command {
*/
protected function formatExecTime() {
list($secs, $tens) = explode('.', sprintf("%.1f", ($this->execTime)));
- # add the following to $niceDate if you want to have microsecons added: . '.' . $tens;
- $niceDate = date('H:i:s', $secs);
- return $niceDate;
+ # if you want to have microseconds add this: . '.' . $tens;
+ return date('H:i:s', $secs);
}
}
diff --git a/apps/files/css/files.css b/apps/files/css/files.css
index 61148428f79..4929039f837 100644
--- a/apps/files/css/files.css
+++ b/apps/files/css/files.css
@@ -59,7 +59,7 @@
/* fit app list view heights */
.app-files #app-content>.viewcontainer {
- min-height: 100%;
+ min-height: 0%;
}
.app-files #app-content {
diff --git a/apps/files/l10n/de_DE.js b/apps/files/l10n/de_DE.js
index 15b82e0f181..ce8a9132a0c 100644
--- a/apps/files/l10n/de_DE.js
+++ b/apps/files/l10n/de_DE.js
@@ -92,6 +92,7 @@ OC.L10N.register(
"%2$s deleted %1$s" : "%2$s hat %1$s gelöscht",
"You restored %1$s" : "Sie haben %1$s wiederhergestellt",
"%2$s restored %1$s" : "%2$s wiederhergestellt %1$s",
+ "Changed by %2$s" : "Geändert von %2$s",
"Deleted by %2$s" : "Gelöscht durch %2$s",
"Restored by %2$s" : "Wiederhergestellt durch %2$s",
"Upload (max. %s)" : "Hochladen (max. %s)",
@@ -99,6 +100,7 @@ OC.L10N.register(
"Maximum upload size" : "Maximale Upload-Größe",
"max. possible: " : "maximal möglich:",
"Save" : "Speichern",
+ "Missing permissions to edit from here." : "Fehlende Berechtigungen um von hier aus zu bearbeiten.",
"Settings" : "Einstellungen",
"WebDAV" : "WebDAV",
"Use this address to <a href=\"%s\" target=\"_blank\">access your Files via WebDAV</a>" : "Benutzen Sie diese Adresse, um <a href=\"%s\" target=\"_blank\">über WebDAV auf Ihre Dateien zuzugreifen</a>",
diff --git a/apps/files/l10n/de_DE.json b/apps/files/l10n/de_DE.json
index 87e47454b62..f6a3abc9891 100644
--- a/apps/files/l10n/de_DE.json
+++ b/apps/files/l10n/de_DE.json
@@ -90,6 +90,7 @@
"%2$s deleted %1$s" : "%2$s hat %1$s gelöscht",
"You restored %1$s" : "Sie haben %1$s wiederhergestellt",
"%2$s restored %1$s" : "%2$s wiederhergestellt %1$s",
+ "Changed by %2$s" : "Geändert von %2$s",
"Deleted by %2$s" : "Gelöscht durch %2$s",
"Restored by %2$s" : "Wiederhergestellt durch %2$s",
"Upload (max. %s)" : "Hochladen (max. %s)",
@@ -97,6 +98,7 @@
"Maximum upload size" : "Maximale Upload-Größe",
"max. possible: " : "maximal möglich:",
"Save" : "Speichern",
+ "Missing permissions to edit from here." : "Fehlende Berechtigungen um von hier aus zu bearbeiten.",
"Settings" : "Einstellungen",
"WebDAV" : "WebDAV",
"Use this address to <a href=\"%s\" target=\"_blank\">access your Files via WebDAV</a>" : "Benutzen Sie diese Adresse, um <a href=\"%s\" target=\"_blank\">über WebDAV auf Ihre Dateien zuzugreifen</a>",
diff --git a/apps/files_external/controller/storagescontroller.php b/apps/files_external/controller/storagescontroller.php
index d71c4ff5ef7..db1cdeb23b9 100644
--- a/apps/files_external/controller/storagescontroller.php
+++ b/apps/files_external/controller/storagescontroller.php
@@ -279,7 +279,7 @@ abstract class StoragesController extends Controller {
* @return DataResponse
*/
public function index() {
- $storages = $this->service->getAllStorages();
+ $storages = $this->service->getStorages();
return new DataResponse(
$storages,
diff --git a/apps/files_external/js/settings.js b/apps/files_external/js/settings.js
index ccb1e858fa0..26df203091e 100644
--- a/apps/files_external/js/settings.js
+++ b/apps/files_external/js/settings.js
@@ -23,6 +23,10 @@ var MOUNT_OPTIONS_DROPDOWN_TEMPLATE =
' <label for="mountOptionsPreviews">{{t "files_external" "Enable previews"}}</label>' +
' </div>' +
' <div class="optionRow">' +
+ ' <input id="mountOptionsSharing" name="enable_sharing" type="checkbox" value="true" checked="checked"/>' +
+ ' <label for="mountOptionsSharing">{{t "files_external" "Enable sharing"}}</label>' +
+ ' </div>' +
+ ' <div class="optionRow">' +
' <label for="mountOptionsFilesystemCheck">{{t "files_external" "Check for changes"}}</label>' +
' <select id="mountOptionsFilesystemCheck" name="filesystem_check_changes" data-type="int">' +
' <option value="0">{{t "files_external" "Never"}}</option>' +
@@ -35,6 +39,7 @@ var MOUNT_OPTIONS_DROPDOWN_TEMPLATE =
templates therefore they are duplicated here
t("files_external", "Enable encryption")
t("files_external", "Enable previews")
+ t("files_external", "Enable sharing")
t("files_external", "Check for changes")
t("files_external", "Never")
t("files_external", "Once every direct access")
@@ -582,6 +587,19 @@ MountOptionsDropdown.prototype = {
var MountConfigListView = function($el, options) {
this.initialize($el, options);
};
+
+MountConfigListView.ParameterFlags = {
+ OPTIONAL: 1,
+ USER_PROVIDED: 2
+};
+
+MountConfigListView.ParameterTypes = {
+ TEXT: 0,
+ BOOLEAN: 1,
+ PASSWORD: 2,
+ HIDDEN: 3
+};
+
/**
* @memberOf OCA.External.Settings
*/
@@ -870,6 +888,7 @@ MountConfigListView.prototype = _.extend({
$tr.find('input.mountOptions').val(JSON.stringify({
'encrypt': true,
'previews': true,
+ 'enable_sharing': true,
'filesystem_check_changes': 1
}));
}
@@ -955,16 +974,15 @@ MountConfigListView.prototype = _.extend({
*/
writeParameterInput: function($td, parameter, placeholder, classes) {
var hasFlag = function(flag) {
- return placeholder.indexOf(flag) !== -1;
+ return (placeholder.flags & flag) === flag;
};
classes = $.isArray(classes) ? classes : [];
classes.push('added');
- if (placeholder.indexOf('&') === 0) {
+ if (hasFlag(MountConfigListView.ParameterFlags.OPTIONAL)) {
classes.push('optional');
- placeholder = placeholder.substring(1);
}
- if (hasFlag('@')) {
+ if (hasFlag(MountConfigListView.ParameterFlags.USER_PROVIDED)) {
if (this._isPersonal) {
classes.push('user_provided');
} else {
@@ -974,17 +992,13 @@ MountConfigListView.prototype = _.extend({
var newElement;
- var trimmedPlaceholder = placeholder;
- var flags = ['@', '*', '!', '#', '&']; // used to determine what kind of parameter
- while(flags.indexOf(trimmedPlaceholder[0]) !== -1) {
- trimmedPlaceholder = trimmedPlaceholder.substr(1);
- }
- if (hasFlag('*')) {
+ var trimmedPlaceholder = placeholder.value;
+ if (placeholder.type === MountConfigListView.ParameterTypes.PASSWORD) {
newElement = $('<input type="password" class="'+classes.join(' ')+'" data-parameter="'+parameter+'" placeholder="'+ trimmedPlaceholder+'" />');
- } else if (hasFlag('!')) {
+ } else if (placeholder.type === MountConfigListView.ParameterTypes.BOOLEAN) {
var checkboxId = _.uniqueId('checkbox_');
newElement = $('<input type="checkbox" id="'+checkboxId+'" class="'+classes.join(' ')+'" data-parameter="'+parameter+'" /><label for="'+checkboxId+'">'+ trimmedPlaceholder+'</label>');
- } else if (hasFlag('#')) {
+ } else if (placeholder.type === MountConfigListView.ParameterTypes.HIDDEN) {
newElement = $('<input type="hidden" class="'+classes.join(' ')+'" data-parameter="'+parameter+'" />');
} else {
newElement = $('<input type="text" class="'+classes.join(' ')+'" data-parameter="'+parameter+'" placeholder="'+ trimmedPlaceholder+'" />');
@@ -1245,7 +1259,7 @@ MountConfigListView.prototype = _.extend({
var storage = this.getStorageConfig($tr);
var $toggle = $tr.find('.mountOptionsToggle');
var dropDown = new MountOptionsDropdown();
- var enabledOptions = ['previews', 'filesystem_check_changes'];
+ var enabledOptions = ['previews', 'filesystem_check_changes', 'enable_sharing'];
if (this._encryptionEnabled) {
enabledOptions.push('encrypt');
}
diff --git a/apps/files_external/l10n/cs_CZ.js b/apps/files_external/l10n/cs_CZ.js
index e9a24ba73f8..56a8147fd99 100644
--- a/apps/files_external/l10n/cs_CZ.js
+++ b/apps/files_external/l10n/cs_CZ.js
@@ -18,6 +18,7 @@ OC.L10N.register(
"Unsatisfied authentication mechanism parameters" : "Neuspokojivé parametry ověřovacího mechanismu",
"Insufficient data: %s" : "Nedostatečná data: %s",
"%s" : "%s",
+ "Storage with id \"%i\" is not user editable" : "Úložiště s id \"%i\" uživatelé nemohou upravovat",
"Personal" : "Osobní",
"System" : "Systém",
"Grant access" : "Povolit přístup",
@@ -60,9 +61,7 @@ OC.L10N.register(
"Identity endpoint URL" : "Identifikační koncový bod URL",
"Rackspace" : "Rackspace",
"API key" : "Klíč API",
- "Login credentials" : "Přihlašovací údaje",
"Username and password" : "Uživatelské jméno a heslo",
- "Session credentials" : "Přihlašovací údaje sezení",
"RSA public key" : "RSA veřejný klíč",
"Public key" : "Veřejný klíč",
"Amazon S3" : "Amazon S3",
diff --git a/apps/files_external/l10n/cs_CZ.json b/apps/files_external/l10n/cs_CZ.json
index aa991682b7d..f22bbc25e35 100644
--- a/apps/files_external/l10n/cs_CZ.json
+++ b/apps/files_external/l10n/cs_CZ.json
@@ -16,6 +16,7 @@
"Unsatisfied authentication mechanism parameters" : "Neuspokojivé parametry ověřovacího mechanismu",
"Insufficient data: %s" : "Nedostatečná data: %s",
"%s" : "%s",
+ "Storage with id \"%i\" is not user editable" : "Úložiště s id \"%i\" uživatelé nemohou upravovat",
"Personal" : "Osobní",
"System" : "Systém",
"Grant access" : "Povolit přístup",
@@ -58,9 +59,7 @@
"Identity endpoint URL" : "Identifikační koncový bod URL",
"Rackspace" : "Rackspace",
"API key" : "Klíč API",
- "Login credentials" : "Přihlašovací údaje",
"Username and password" : "Uživatelské jméno a heslo",
- "Session credentials" : "Přihlašovací údaje sezení",
"RSA public key" : "RSA veřejný klíč",
"Public key" : "Veřejný klíč",
"Amazon S3" : "Amazon S3",
diff --git a/apps/files_external/l10n/da.js b/apps/files_external/l10n/da.js
index 6b6db06d817..70f43819496 100644
--- a/apps/files_external/l10n/da.js
+++ b/apps/files_external/l10n/da.js
@@ -50,7 +50,6 @@ OC.L10N.register(
"Rackspace" : "Hyldeplads",
"API key" : "API nøgle",
"Username and password" : "Brugernavn og kodeord",
- "Session credentials" : "Brugeroplysninger for session",
"RSA public key" : "RSA offentlig nøgle",
"Public key" : "Offentlig nøgle",
"Amazon S3" : "Amazon S3",
diff --git a/apps/files_external/l10n/da.json b/apps/files_external/l10n/da.json
index 61b1bab2d55..aaeaaa58ae0 100644
--- a/apps/files_external/l10n/da.json
+++ b/apps/files_external/l10n/da.json
@@ -48,7 +48,6 @@
"Rackspace" : "Hyldeplads",
"API key" : "API nøgle",
"Username and password" : "Brugernavn og kodeord",
- "Session credentials" : "Brugeroplysninger for session",
"RSA public key" : "RSA offentlig nøgle",
"Public key" : "Offentlig nøgle",
"Amazon S3" : "Amazon S3",
diff --git a/apps/files_external/l10n/de_DE.js b/apps/files_external/l10n/de_DE.js
index f3cbcd2be6e..ef860918474 100644
--- a/apps/files_external/l10n/de_DE.js
+++ b/apps/files_external/l10n/de_DE.js
@@ -73,6 +73,7 @@ OC.L10N.register(
"Scope" : "Anwendungsbereich",
"External Storage" : "Externer Speicher",
"Folder name" : "Ordnername",
+ "Authentication" : "Authentifizierung",
"Configuration" : "Konfiguration",
"Available for" : "Verfügbar für",
"Add storage" : "Speicher hinzufügen",
diff --git a/apps/files_external/l10n/de_DE.json b/apps/files_external/l10n/de_DE.json
index d366b1f0d9f..75a84334764 100644
--- a/apps/files_external/l10n/de_DE.json
+++ b/apps/files_external/l10n/de_DE.json
@@ -71,6 +71,7 @@
"Scope" : "Anwendungsbereich",
"External Storage" : "Externer Speicher",
"Folder name" : "Ordnername",
+ "Authentication" : "Authentifizierung",
"Configuration" : "Konfiguration",
"Available for" : "Verfügbar für",
"Add storage" : "Speicher hinzufügen",
diff --git a/apps/files_external/l10n/el.js b/apps/files_external/l10n/el.js
index bae61dee741..c04d3d015d3 100644
--- a/apps/files_external/l10n/el.js
+++ b/apps/files_external/l10n/el.js
@@ -52,7 +52,6 @@ OC.L10N.register(
"Rackspace" : "Rackspace",
"API key" : "Κλειδί Google API",
"Username and password" : "Όνομα χρήστη και κωδικός πρόσβασης",
- "Session credentials" : "Διαπιστευτήρια συνεδρίας",
"RSA public key" : "Δημόσιο κλειδί RSA",
"Public key" : "Δημόσιο κλειδί",
"Amazon S3" : "Amazon S3",
diff --git a/apps/files_external/l10n/el.json b/apps/files_external/l10n/el.json
index e85059d88ba..98a6d18bfc3 100644
--- a/apps/files_external/l10n/el.json
+++ b/apps/files_external/l10n/el.json
@@ -50,7 +50,6 @@
"Rackspace" : "Rackspace",
"API key" : "Κλειδί Google API",
"Username and password" : "Όνομα χρήστη και κωδικός πρόσβασης",
- "Session credentials" : "Διαπιστευτήρια συνεδρίας",
"RSA public key" : "Δημόσιο κλειδί RSA",
"Public key" : "Δημόσιο κλειδί",
"Amazon S3" : "Amazon S3",
diff --git a/apps/files_external/l10n/es.js b/apps/files_external/l10n/es.js
index 6db3afd6441..8a49dd4ec05 100644
--- a/apps/files_external/l10n/es.js
+++ b/apps/files_external/l10n/es.js
@@ -61,7 +61,6 @@ OC.L10N.register(
"Rackspace" : "Espacio de Rack",
"API key" : "Clave API",
"Username and password" : "Nombre de usuario y contraseña",
- "Session credentials" : "Credenciales de la sesión",
"RSA public key" : "Clave pública RSA",
"Public key" : "Clave pública",
"Amazon S3" : "Amazon S3",
diff --git a/apps/files_external/l10n/es.json b/apps/files_external/l10n/es.json
index e9a3e317679..c3b81338776 100644
--- a/apps/files_external/l10n/es.json
+++ b/apps/files_external/l10n/es.json
@@ -59,7 +59,6 @@
"Rackspace" : "Espacio de Rack",
"API key" : "Clave API",
"Username and password" : "Nombre de usuario y contraseña",
- "Session credentials" : "Credenciales de la sesión",
"RSA public key" : "Clave pública RSA",
"Public key" : "Clave pública",
"Amazon S3" : "Amazon S3",
diff --git a/apps/files_external/l10n/fi_FI.js b/apps/files_external/l10n/fi_FI.js
index f7ec6fea3b3..ec969586d6d 100644
--- a/apps/files_external/l10n/fi_FI.js
+++ b/apps/files_external/l10n/fi_FI.js
@@ -45,8 +45,10 @@ OC.L10N.register(
"Password" : "Salasana",
"Rackspace" : "Rackspace",
"API key" : "API-avain",
+ "Log-in credentials, save in database" : "Kirjautumistiedot, tallenna tietokantaan",
"Username and password" : "Käyttäjätunnus ja salasana",
- "Session credentials" : "Istunnon tunnistetiedot",
+ "Log-in credentials, save in session" : "Kirjautumistiedot, tallenna istuntoon",
+ "User entered, store in database" : "Käyttäjän määrittämä, tallenna tietokantaan",
"RSA public key" : "Julkinen RSA-avain",
"Public key" : "Julkinen avain",
"Amazon S3" : "Amazon S3",
diff --git a/apps/files_external/l10n/fi_FI.json b/apps/files_external/l10n/fi_FI.json
index 368121feae8..8c39acff034 100644
--- a/apps/files_external/l10n/fi_FI.json
+++ b/apps/files_external/l10n/fi_FI.json
@@ -43,8 +43,10 @@
"Password" : "Salasana",
"Rackspace" : "Rackspace",
"API key" : "API-avain",
+ "Log-in credentials, save in database" : "Kirjautumistiedot, tallenna tietokantaan",
"Username and password" : "Käyttäjätunnus ja salasana",
- "Session credentials" : "Istunnon tunnistetiedot",
+ "Log-in credentials, save in session" : "Kirjautumistiedot, tallenna istuntoon",
+ "User entered, store in database" : "Käyttäjän määrittämä, tallenna tietokantaan",
"RSA public key" : "Julkinen RSA-avain",
"Public key" : "Julkinen avain",
"Amazon S3" : "Amazon S3",
diff --git a/apps/files_external/l10n/fr.js b/apps/files_external/l10n/fr.js
index 0c2bb063b29..cc1c1bc693c 100644
--- a/apps/files_external/l10n/fr.js
+++ b/apps/files_external/l10n/fr.js
@@ -18,6 +18,7 @@ OC.L10N.register(
"Unsatisfied authentication mechanism parameters" : "Paramètres manquants pour la méthode d'authentification",
"Insufficient data: %s" : "Données insuffisantes : %s",
"%s" : "%s",
+ "Storage with id \"%i\" is not user editable" : "Le support de stockage d'id \"%i\" n'est pas modifiable par les utilisateurs",
"Personal" : "Personnel",
"System" : "Système",
"Grant access" : "Autoriser l'accès",
@@ -60,9 +61,7 @@ OC.L10N.register(
"Identity endpoint URL" : "Identity endpoint URL",
"Rackspace" : "Rackspace",
"API key" : "Clé API",
- "Login credentials" : "Informations d'identification",
"Username and password" : "Nom d'utilisateur et mot de passe",
- "Session credentials" : "Informations d'identification de session",
"RSA public key" : "Clé publique RSA",
"Public key" : "Clef publique",
"Amazon S3" : "Amazon S3",
diff --git a/apps/files_external/l10n/fr.json b/apps/files_external/l10n/fr.json
index 6f2fb0615bd..47a70fcd12a 100644
--- a/apps/files_external/l10n/fr.json
+++ b/apps/files_external/l10n/fr.json
@@ -16,6 +16,7 @@
"Unsatisfied authentication mechanism parameters" : "Paramètres manquants pour la méthode d'authentification",
"Insufficient data: %s" : "Données insuffisantes : %s",
"%s" : "%s",
+ "Storage with id \"%i\" is not user editable" : "Le support de stockage d'id \"%i\" n'est pas modifiable par les utilisateurs",
"Personal" : "Personnel",
"System" : "Système",
"Grant access" : "Autoriser l'accès",
@@ -58,9 +59,7 @@
"Identity endpoint URL" : "Identity endpoint URL",
"Rackspace" : "Rackspace",
"API key" : "Clé API",
- "Login credentials" : "Informations d'identification",
"Username and password" : "Nom d'utilisateur et mot de passe",
- "Session credentials" : "Informations d'identification de session",
"RSA public key" : "Clé publique RSA",
"Public key" : "Clef publique",
"Amazon S3" : "Amazon S3",
diff --git a/apps/files_external/l10n/he.js b/apps/files_external/l10n/he.js
index 3da4dd67776..d963d796428 100644
--- a/apps/files_external/l10n/he.js
+++ b/apps/files_external/l10n/he.js
@@ -18,6 +18,7 @@ OC.L10N.register(
"Unsatisfied authentication mechanism parameters" : "פרמטרים של מכניזם אימות אינם מספקים",
"Insufficient data: %s" : "מידע לא מספק: %s",
"%s" : "%s",
+ "Storage with id \"%i\" is not user editable" : "האחסון עם זהות \"%i\" לא ניתן לעריכה",
"Personal" : "אישי",
"System" : "מערכת",
"Grant access" : "הענקת גישה",
@@ -60,9 +61,10 @@ OC.L10N.register(
"Identity endpoint URL" : "זהות נתיב נקודת קצה",
"Rackspace" : "חץ אחורה",
"API key" : "מפתח API",
- "Login credentials" : "פרטי הכניסה",
+ "Log-in credentials, save in database" : "אישורי התחברות, נשמרים במסד הנתונים",
"Username and password" : "שם משתמש וסיסמא",
- "Session credentials" : "אישורי סשן",
+ "Log-in credentials, save in session" : "אישורי התחברות, נשמרים במידע שיחה - סשן",
+ "User entered, store in database" : "משתמש התחבר, נשמר במסד הנתונים",
"RSA public key" : "מפתח ציבורי RSA",
"Public key" : "מפתח ציבורי",
"Amazon S3" : "אמזון S3",
diff --git a/apps/files_external/l10n/he.json b/apps/files_external/l10n/he.json
index 9666346681c..fbdcc287495 100644
--- a/apps/files_external/l10n/he.json
+++ b/apps/files_external/l10n/he.json
@@ -16,6 +16,7 @@
"Unsatisfied authentication mechanism parameters" : "פרמטרים של מכניזם אימות אינם מספקים",
"Insufficient data: %s" : "מידע לא מספק: %s",
"%s" : "%s",
+ "Storage with id \"%i\" is not user editable" : "האחסון עם זהות \"%i\" לא ניתן לעריכה",
"Personal" : "אישי",
"System" : "מערכת",
"Grant access" : "הענקת גישה",
@@ -58,9 +59,10 @@
"Identity endpoint URL" : "זהות נתיב נקודת קצה",
"Rackspace" : "חץ אחורה",
"API key" : "מפתח API",
- "Login credentials" : "פרטי הכניסה",
+ "Log-in credentials, save in database" : "אישורי התחברות, נשמרים במסד הנתונים",
"Username and password" : "שם משתמש וסיסמא",
- "Session credentials" : "אישורי סשן",
+ "Log-in credentials, save in session" : "אישורי התחברות, נשמרים במידע שיחה - סשן",
+ "User entered, store in database" : "משתמש התחבר, נשמר במסד הנתונים",
"RSA public key" : "מפתח ציבורי RSA",
"Public key" : "מפתח ציבורי",
"Amazon S3" : "אמזון S3",
diff --git a/apps/files_external/l10n/id.js b/apps/files_external/l10n/id.js
index d782e285cc8..15f33571ee6 100644
--- a/apps/files_external/l10n/id.js
+++ b/apps/files_external/l10n/id.js
@@ -50,7 +50,6 @@ OC.L10N.register(
"Rackspace" : "Rackspace",
"API key" : "Kunci API",
"Username and password" : "Nama pengguna dan sandi",
- "Session credentials" : "Kredensial sesi",
"RSA public key" : "Kunci publik RSA",
"Public key" : "Kunci Public",
"Amazon S3" : "Amazon S3",
diff --git a/apps/files_external/l10n/id.json b/apps/files_external/l10n/id.json
index ae39765674b..00f78599006 100644
--- a/apps/files_external/l10n/id.json
+++ b/apps/files_external/l10n/id.json
@@ -48,7 +48,6 @@
"Rackspace" : "Rackspace",
"API key" : "Kunci API",
"Username and password" : "Nama pengguna dan sandi",
- "Session credentials" : "Kredensial sesi",
"RSA public key" : "Kunci publik RSA",
"Public key" : "Kunci Public",
"Amazon S3" : "Amazon S3",
diff --git a/apps/files_external/l10n/it.js b/apps/files_external/l10n/it.js
index 54192813b2a..d9adaad833b 100644
--- a/apps/files_external/l10n/it.js
+++ b/apps/files_external/l10n/it.js
@@ -18,6 +18,7 @@ OC.L10N.register(
"Unsatisfied authentication mechanism parameters" : "Parametri del meccanismo di autenticazione non soddisfatti",
"Insufficient data: %s" : "Dati insufficienti: %s",
"%s" : "%s",
+ "Storage with id \"%i\" is not user editable" : "L'archiviazione con ID \"%i\" non è modificabile dall'utente",
"Personal" : "Personale",
"System" : "Sistema",
"Grant access" : "Concedi l'accesso",
@@ -60,9 +61,10 @@ OC.L10N.register(
"Identity endpoint URL" : "URL endpoint delle identità",
"Rackspace" : "Rackspace",
"API key" : "Chiave API",
- "Login credentials" : "Credenziali di accesso",
+ "Log-in credentials, save in database" : "Credenziali di accesso, salva nel database",
"Username and password" : "Nome utente e password",
- "Session credentials" : "Credenziali di sessione",
+ "Log-in credentials, save in session" : "Credenziali di accesso, salva nella sessione",
+ "User entered, store in database" : "Digitate dall'utente, memorizza nel database",
"RSA public key" : "Chiave pubblica RSA",
"Public key" : "Chiave pubblica",
"Amazon S3" : "Amazon S3",
diff --git a/apps/files_external/l10n/it.json b/apps/files_external/l10n/it.json
index 3bc5ee133fc..b228cd76604 100644
--- a/apps/files_external/l10n/it.json
+++ b/apps/files_external/l10n/it.json
@@ -16,6 +16,7 @@
"Unsatisfied authentication mechanism parameters" : "Parametri del meccanismo di autenticazione non soddisfatti",
"Insufficient data: %s" : "Dati insufficienti: %s",
"%s" : "%s",
+ "Storage with id \"%i\" is not user editable" : "L'archiviazione con ID \"%i\" non è modificabile dall'utente",
"Personal" : "Personale",
"System" : "Sistema",
"Grant access" : "Concedi l'accesso",
@@ -58,9 +59,10 @@
"Identity endpoint URL" : "URL endpoint delle identità",
"Rackspace" : "Rackspace",
"API key" : "Chiave API",
- "Login credentials" : "Credenziali di accesso",
+ "Log-in credentials, save in database" : "Credenziali di accesso, salva nel database",
"Username and password" : "Nome utente e password",
- "Session credentials" : "Credenziali di sessione",
+ "Log-in credentials, save in session" : "Credenziali di accesso, salva nella sessione",
+ "User entered, store in database" : "Digitate dall'utente, memorizza nel database",
"RSA public key" : "Chiave pubblica RSA",
"Public key" : "Chiave pubblica",
"Amazon S3" : "Amazon S3",
diff --git a/apps/files_external/l10n/ja.js b/apps/files_external/l10n/ja.js
index 1deff5971a9..f14c07808ca 100644
--- a/apps/files_external/l10n/ja.js
+++ b/apps/files_external/l10n/ja.js
@@ -18,6 +18,7 @@ OC.L10N.register(
"Unsatisfied authentication mechanism parameters" : "認証のためのパラメータが不十分です",
"Insufficient data: %s" : "データが不足しています: %s",
"%s" : "%s",
+ "Storage with id \"%i\" is not user editable" : "ストレージID \"%i\" はユーザーが編集できません",
"Personal" : "個人",
"System" : "システム",
"Grant access" : "アクセスを許可",
@@ -61,7 +62,6 @@ OC.L10N.register(
"Rackspace" : "Rackspace",
"API key" : "APIキー",
"Username and password" : "ユーザー名とパスワード",
- "Session credentials" : "セッション資格情報",
"RSA public key" : "RSA公開鍵",
"Public key" : "公開鍵",
"Amazon S3" : "Amazon S3",
diff --git a/apps/files_external/l10n/ja.json b/apps/files_external/l10n/ja.json
index 744a63c929b..df6f7f44618 100644
--- a/apps/files_external/l10n/ja.json
+++ b/apps/files_external/l10n/ja.json
@@ -16,6 +16,7 @@
"Unsatisfied authentication mechanism parameters" : "認証のためのパラメータが不十分です",
"Insufficient data: %s" : "データが不足しています: %s",
"%s" : "%s",
+ "Storage with id \"%i\" is not user editable" : "ストレージID \"%i\" はユーザーが編集できません",
"Personal" : "個人",
"System" : "システム",
"Grant access" : "アクセスを許可",
@@ -59,7 +60,6 @@
"Rackspace" : "Rackspace",
"API key" : "APIキー",
"Username and password" : "ユーザー名とパスワード",
- "Session credentials" : "セッション資格情報",
"RSA public key" : "RSA公開鍵",
"Public key" : "公開鍵",
"Amazon S3" : "Amazon S3",
diff --git a/apps/files_external/l10n/ko.js b/apps/files_external/l10n/ko.js
index 6c569639cab..df30d86ca13 100644
--- a/apps/files_external/l10n/ko.js
+++ b/apps/files_external/l10n/ko.js
@@ -51,7 +51,6 @@ OC.L10N.register(
"Rackspace" : "Rackspace",
"API key" : "API 키",
"Username and password" : "사용자 이름과 암호",
- "Session credentials" : "세션 접근 정보",
"RSA public key" : "RSA 공개 키",
"Public key" : "공개 키",
"Amazon S3" : "Amazon S3",
diff --git a/apps/files_external/l10n/ko.json b/apps/files_external/l10n/ko.json
index 4b64b7433ac..a5f4b4946ca 100644
--- a/apps/files_external/l10n/ko.json
+++ b/apps/files_external/l10n/ko.json
@@ -49,7 +49,6 @@
"Rackspace" : "Rackspace",
"API key" : "API 키",
"Username and password" : "사용자 이름과 암호",
- "Session credentials" : "세션 접근 정보",
"RSA public key" : "RSA 공개 키",
"Public key" : "공개 키",
"Amazon S3" : "Amazon S3",
diff --git a/apps/files_external/l10n/nb_NO.js b/apps/files_external/l10n/nb_NO.js
index cf8e7813d20..fc791fe60ab 100644
--- a/apps/files_external/l10n/nb_NO.js
+++ b/apps/files_external/l10n/nb_NO.js
@@ -61,7 +61,6 @@ OC.L10N.register(
"Rackspace" : "Rackspace",
"API key" : "API-nøkkel",
"Username and password" : "Brukernavn og passord",
- "Session credentials" : "Påloggingsdetaljer for økt",
"RSA public key" : "RSA offentlig nøkkel",
"Public key" : "Offentlig nøkkel",
"Amazon S3" : "Amazon S3",
diff --git a/apps/files_external/l10n/nb_NO.json b/apps/files_external/l10n/nb_NO.json
index eaad9c7461d..0e956952948 100644
--- a/apps/files_external/l10n/nb_NO.json
+++ b/apps/files_external/l10n/nb_NO.json
@@ -59,7 +59,6 @@
"Rackspace" : "Rackspace",
"API key" : "API-nøkkel",
"Username and password" : "Brukernavn og passord",
- "Session credentials" : "Påloggingsdetaljer for økt",
"RSA public key" : "RSA offentlig nøkkel",
"Public key" : "Offentlig nøkkel",
"Amazon S3" : "Amazon S3",
diff --git a/apps/files_external/l10n/nds.js b/apps/files_external/l10n/nds.js
index 73df3c8483d..b4649fe681a 100644
--- a/apps/files_external/l10n/nds.js
+++ b/apps/files_external/l10n/nds.js
@@ -47,7 +47,6 @@ OC.L10N.register(
"Password" : "Passwort",
"API key" : "API Schlüssel",
"Username and password" : "Benutzername und Passwort",
- "Session credentials" : "Anmeldedaten der Sitzung",
"RSA public key" : "Öffentlicher RSA Schlüssel",
"Public key" : "Öffentlicher Schlüssel",
"Amazon S3" : "Amazon S3",
diff --git a/apps/files_external/l10n/nds.json b/apps/files_external/l10n/nds.json
index 57cbefff91b..75e36016384 100644
--- a/apps/files_external/l10n/nds.json
+++ b/apps/files_external/l10n/nds.json
@@ -45,7 +45,6 @@
"Password" : "Passwort",
"API key" : "API Schlüssel",
"Username and password" : "Benutzername und Passwort",
- "Session credentials" : "Anmeldedaten der Sitzung",
"RSA public key" : "Öffentlicher RSA Schlüssel",
"Public key" : "Öffentlicher Schlüssel",
"Amazon S3" : "Amazon S3",
diff --git a/apps/files_external/l10n/nl.js b/apps/files_external/l10n/nl.js
index adc2302d9c6..a3f3ca9a566 100644
--- a/apps/files_external/l10n/nl.js
+++ b/apps/files_external/l10n/nl.js
@@ -18,6 +18,7 @@ OC.L10N.register(
"Unsatisfied authentication mechanism parameters" : "Onvoldoende authenticatiemechanisme parameters",
"Insufficient data: %s" : "Onvoldoende gegevens: %s",
"%s" : "%s",
+ "Storage with id \"%i\" is not user editable" : "Opslag met id \"%i\" is niet te bewerken door gebruiker",
"Personal" : "Persoonlijk",
"System" : "Systeem",
"Grant access" : "Sta toegang toe",
@@ -60,9 +61,7 @@ OC.L10N.register(
"Identity endpoint URL" : "Identiteiten endpoint URL",
"Rackspace" : "Rackspace",
"API key" : "API sleutel",
- "Login credentials" : "Inloggegevens",
"Username and password" : "Gebruikersnaam en wachtwoord",
- "Session credentials" : "Sessie inloggegevens",
"RSA public key" : "RSA publieke sleutel",
"Public key" : "Publieke sleutel",
"Amazon S3" : "Amazon S3",
diff --git a/apps/files_external/l10n/nl.json b/apps/files_external/l10n/nl.json
index da7353a4bde..8654cdc662d 100644
--- a/apps/files_external/l10n/nl.json
+++ b/apps/files_external/l10n/nl.json
@@ -16,6 +16,7 @@
"Unsatisfied authentication mechanism parameters" : "Onvoldoende authenticatiemechanisme parameters",
"Insufficient data: %s" : "Onvoldoende gegevens: %s",
"%s" : "%s",
+ "Storage with id \"%i\" is not user editable" : "Opslag met id \"%i\" is niet te bewerken door gebruiker",
"Personal" : "Persoonlijk",
"System" : "Systeem",
"Grant access" : "Sta toegang toe",
@@ -58,9 +59,7 @@
"Identity endpoint URL" : "Identiteiten endpoint URL",
"Rackspace" : "Rackspace",
"API key" : "API sleutel",
- "Login credentials" : "Inloggegevens",
"Username and password" : "Gebruikersnaam en wachtwoord",
- "Session credentials" : "Sessie inloggegevens",
"RSA public key" : "RSA publieke sleutel",
"Public key" : "Publieke sleutel",
"Amazon S3" : "Amazon S3",
diff --git a/apps/files_external/l10n/oc.js b/apps/files_external/l10n/oc.js
index 71b174f2e61..716326f1cae 100644
--- a/apps/files_external/l10n/oc.js
+++ b/apps/files_external/l10n/oc.js
@@ -51,7 +51,6 @@ OC.L10N.register(
"Rackspace" : "Rackspace",
"API key" : "Clau API",
"Username and password" : "Nom d'utilizaire e senhal",
- "Session credentials" : "Informacions d'identificacion de session",
"RSA public key" : "Clau publica RSA",
"Public key" : "Clau publica",
"Amazon S3" : "Amazon S3",
diff --git a/apps/files_external/l10n/oc.json b/apps/files_external/l10n/oc.json
index edfd9d0a21b..bc882de1456 100644
--- a/apps/files_external/l10n/oc.json
+++ b/apps/files_external/l10n/oc.json
@@ -49,7 +49,6 @@
"Rackspace" : "Rackspace",
"API key" : "Clau API",
"Username and password" : "Nom d'utilizaire e senhal",
- "Session credentials" : "Informacions d'identificacion de session",
"RSA public key" : "Clau publica RSA",
"Public key" : "Clau publica",
"Amazon S3" : "Amazon S3",
diff --git a/apps/files_external/l10n/pt_BR.js b/apps/files_external/l10n/pt_BR.js
index 46b10051238..283b9478577 100644
--- a/apps/files_external/l10n/pt_BR.js
+++ b/apps/files_external/l10n/pt_BR.js
@@ -18,6 +18,7 @@ OC.L10N.register(
"Unsatisfied authentication mechanism parameters" : "Parâmetros de mecanismos de autenticação não satisfeitos",
"Insufficient data: %s" : "Dados insuficientes: %s",
"%s" : "%s",
+ "Storage with id \"%i\" is not user editable" : "Armazenamento com ID \"%i\" não é editável pelo usuário",
"Personal" : "Pessoal",
"System" : "Sistema",
"Grant access" : "Permitir acesso",
@@ -60,9 +61,7 @@ OC.L10N.register(
"Identity endpoint URL" : "Identidade pontofinal URL",
"Rackspace" : "Espaço em rack",
"API key" : "Chave API",
- "Login credentials" : "Credenciais de login",
"Username and password" : "Nome de Usuário e senha",
- "Session credentials" : "Credenciais de Sessão",
"RSA public key" : "Chave pública RSA",
"Public key" : "Chave pública",
"Amazon S3" : "Amazon S3",
diff --git a/apps/files_external/l10n/pt_BR.json b/apps/files_external/l10n/pt_BR.json
index af71fdb1b47..f1d97e6cf4a 100644
--- a/apps/files_external/l10n/pt_BR.json
+++ b/apps/files_external/l10n/pt_BR.json
@@ -16,6 +16,7 @@
"Unsatisfied authentication mechanism parameters" : "Parâmetros de mecanismos de autenticação não satisfeitos",
"Insufficient data: %s" : "Dados insuficientes: %s",
"%s" : "%s",
+ "Storage with id \"%i\" is not user editable" : "Armazenamento com ID \"%i\" não é editável pelo usuário",
"Personal" : "Pessoal",
"System" : "Sistema",
"Grant access" : "Permitir acesso",
@@ -58,9 +59,7 @@
"Identity endpoint URL" : "Identidade pontofinal URL",
"Rackspace" : "Espaço em rack",
"API key" : "Chave API",
- "Login credentials" : "Credenciais de login",
"Username and password" : "Nome de Usuário e senha",
- "Session credentials" : "Credenciais de Sessão",
"RSA public key" : "Chave pública RSA",
"Public key" : "Chave pública",
"Amazon S3" : "Amazon S3",
diff --git a/apps/files_external/l10n/pt_PT.js b/apps/files_external/l10n/pt_PT.js
index e80971ccff5..b84c399e34d 100644
--- a/apps/files_external/l10n/pt_PT.js
+++ b/apps/files_external/l10n/pt_PT.js
@@ -18,6 +18,7 @@ OC.L10N.register(
"Unsatisfied authentication mechanism parameters" : "Parâmetros do mecanismo de autenticação inválidos",
"Insufficient data: %s" : "Dados insuficientes: %s",
"%s" : "%s",
+ "Storage with id \"%i\" is not user editable" : "Armazenamento com id \"%i\" não é editável pelo utilizador",
"Personal" : "Pessoal",
"System" : "Sistema",
"Grant access" : "Conceder acesso",
@@ -60,9 +61,10 @@ OC.L10N.register(
"Identity endpoint URL" : "Identidade URL endpoint",
"Rackspace" : "Rackspace",
"API key" : "Chave API",
- "Login credentials" : "Credenciais de login",
+ "Log-in credentials, save in database" : "Credenciais de login, guardar na base de dados",
"Username and password" : "Nome de utilizador e palavra-passe",
- "Session credentials" : "Credenciais da sessão",
+ "Log-in credentials, save in session" : "Credenciais de login, guardar na sessão",
+ "User entered, store in database" : "Utilizador introduzido, guardar na base de dados",
"RSA public key" : "Chave pública RSA",
"Public key" : "Chave pública",
"Amazon S3" : "Amazon S3",
diff --git a/apps/files_external/l10n/pt_PT.json b/apps/files_external/l10n/pt_PT.json
index c2a4dd9848d..ac65ca7744f 100644
--- a/apps/files_external/l10n/pt_PT.json
+++ b/apps/files_external/l10n/pt_PT.json
@@ -16,6 +16,7 @@
"Unsatisfied authentication mechanism parameters" : "Parâmetros do mecanismo de autenticação inválidos",
"Insufficient data: %s" : "Dados insuficientes: %s",
"%s" : "%s",
+ "Storage with id \"%i\" is not user editable" : "Armazenamento com id \"%i\" não é editável pelo utilizador",
"Personal" : "Pessoal",
"System" : "Sistema",
"Grant access" : "Conceder acesso",
@@ -58,9 +59,10 @@
"Identity endpoint URL" : "Identidade URL endpoint",
"Rackspace" : "Rackspace",
"API key" : "Chave API",
- "Login credentials" : "Credenciais de login",
+ "Log-in credentials, save in database" : "Credenciais de login, guardar na base de dados",
"Username and password" : "Nome de utilizador e palavra-passe",
- "Session credentials" : "Credenciais da sessão",
+ "Log-in credentials, save in session" : "Credenciais de login, guardar na sessão",
+ "User entered, store in database" : "Utilizador introduzido, guardar na base de dados",
"RSA public key" : "Chave pública RSA",
"Public key" : "Chave pública",
"Amazon S3" : "Amazon S3",
diff --git a/apps/files_external/l10n/ru.js b/apps/files_external/l10n/ru.js
index 113cebcc9ef..ceae42c7942 100644
--- a/apps/files_external/l10n/ru.js
+++ b/apps/files_external/l10n/ru.js
@@ -18,6 +18,7 @@ OC.L10N.register(
"Unsatisfied authentication mechanism parameters" : "Недопустимые настройки механизма авторизации",
"Insufficient data: %s" : "Недостаточно данных: %s",
"%s" : "%s",
+ "Storage with id \"%i\" is not user editable" : "Пользователь не может редактировать хранилище \"%i\"",
"Personal" : "Личное",
"System" : "Система",
"Grant access" : "Предоставить доступ",
@@ -60,8 +61,10 @@ OC.L10N.register(
"Identity endpoint URL" : "Удостоверение конечной точки URL",
"Rackspace" : "Rackspace",
"API key" : "Ключ API",
+ "Log-in credentials, save in database" : "Учетные данные, хранить в базе данных",
"Username and password" : "Имя пользователя и пароль",
- "Session credentials" : "Учетные данные сессии",
+ "Log-in credentials, save in session" : "Учетные данные, хранить в сессии",
+ "User entered, store in database" : "Введенные пользователем, хранить в базе данных",
"RSA public key" : "Открытый ключ RSA",
"Public key" : "Открытый ключ",
"Amazon S3" : "Amazon S3",
diff --git a/apps/files_external/l10n/ru.json b/apps/files_external/l10n/ru.json
index 81df3078c59..d0dab706ea9 100644
--- a/apps/files_external/l10n/ru.json
+++ b/apps/files_external/l10n/ru.json
@@ -16,6 +16,7 @@
"Unsatisfied authentication mechanism parameters" : "Недопустимые настройки механизма авторизации",
"Insufficient data: %s" : "Недостаточно данных: %s",
"%s" : "%s",
+ "Storage with id \"%i\" is not user editable" : "Пользователь не может редактировать хранилище \"%i\"",
"Personal" : "Личное",
"System" : "Система",
"Grant access" : "Предоставить доступ",
@@ -58,8 +59,10 @@
"Identity endpoint URL" : "Удостоверение конечной точки URL",
"Rackspace" : "Rackspace",
"API key" : "Ключ API",
+ "Log-in credentials, save in database" : "Учетные данные, хранить в базе данных",
"Username and password" : "Имя пользователя и пароль",
- "Session credentials" : "Учетные данные сессии",
+ "Log-in credentials, save in session" : "Учетные данные, хранить в сессии",
+ "User entered, store in database" : "Введенные пользователем, хранить в базе данных",
"RSA public key" : "Открытый ключ RSA",
"Public key" : "Открытый ключ",
"Amazon S3" : "Amazon S3",
diff --git a/apps/files_external/l10n/sk_SK.js b/apps/files_external/l10n/sk_SK.js
index 2c81272ff1f..72fb57a1e0b 100644
--- a/apps/files_external/l10n/sk_SK.js
+++ b/apps/files_external/l10n/sk_SK.js
@@ -50,7 +50,6 @@ OC.L10N.register(
"Rackspace" : "Rackspace",
"API key" : "API kľúč",
"Username and password" : "Meno a heslo",
- "Session credentials" : "Pihlasovacie údaje sezóny",
"RSA public key" : "RSA verejný kľúč",
"Public key" : "Verejný kľúč",
"Amazon S3" : "Amazon S3",
diff --git a/apps/files_external/l10n/sk_SK.json b/apps/files_external/l10n/sk_SK.json
index 85532f676bc..267eb5b48b9 100644
--- a/apps/files_external/l10n/sk_SK.json
+++ b/apps/files_external/l10n/sk_SK.json
@@ -48,7 +48,6 @@
"Rackspace" : "Rackspace",
"API key" : "API kľúč",
"Username and password" : "Meno a heslo",
- "Session credentials" : "Pihlasovacie údaje sezóny",
"RSA public key" : "RSA verejný kľúč",
"Public key" : "Verejný kľúč",
"Amazon S3" : "Amazon S3",
diff --git a/apps/files_external/l10n/sl.js b/apps/files_external/l10n/sl.js
index 362e2cf84e1..62b35ffab5e 100644
--- a/apps/files_external/l10n/sl.js
+++ b/apps/files_external/l10n/sl.js
@@ -41,9 +41,7 @@ OC.L10N.register(
"Password" : "Geslo",
"Tenant name" : "Ime uporabnika",
"API key" : "Ključ API",
- "Login credentials" : "Poverila prijave",
"Username and password" : "Uporabniško ime in geslo",
- "Session credentials" : "Poverila seje",
"RSA public key" : "Javni ključ RSA",
"Public key" : "Javni ključ",
"Amazon S3" : "Amazon S3",
diff --git a/apps/files_external/l10n/sl.json b/apps/files_external/l10n/sl.json
index 2496a4d7199..3a806fbd25b 100644
--- a/apps/files_external/l10n/sl.json
+++ b/apps/files_external/l10n/sl.json
@@ -39,9 +39,7 @@
"Password" : "Geslo",
"Tenant name" : "Ime uporabnika",
"API key" : "Ključ API",
- "Login credentials" : "Poverila prijave",
"Username and password" : "Uporabniško ime in geslo",
- "Session credentials" : "Poverila seje",
"RSA public key" : "Javni ključ RSA",
"Public key" : "Javni ključ",
"Amazon S3" : "Amazon S3",
diff --git a/apps/files_external/l10n/sq.js b/apps/files_external/l10n/sq.js
index da6ccf917f5..5549657c448 100644
--- a/apps/files_external/l10n/sq.js
+++ b/apps/files_external/l10n/sq.js
@@ -18,6 +18,7 @@ OC.L10N.register(
"Unsatisfied authentication mechanism parameters" : "Parametra mekanizmi mirëfilltësimi të papërmbushur",
"Insufficient data: %s" : "Të dhëna të pamjaftueshme: %s",
"%s" : "%s",
+ "Storage with id \"%i\" is not user editable" : "Depozita me id \"%i\" s’është e përpunueshme nga përdoruesi",
"Personal" : "Personale",
"System" : "Sistem",
"Grant access" : "Akordoji hyrje",
@@ -59,9 +60,7 @@ OC.L10N.register(
"Tenant name" : "Emër qiraxhiu",
"Rackspace" : "Rackspace",
"API key" : "Kyç API",
- "Login credentials" : "Kredenciale hyrjesh",
"Username and password" : "Emër përdoruesi dhe fjalëkalim",
- "Session credentials" : "Kredenciale sesioni",
"RSA public key" : "Kyç publik RSA ",
"Public key" : "Kyç publik",
"Amazon S3" : "Amazon S3",
diff --git a/apps/files_external/l10n/sq.json b/apps/files_external/l10n/sq.json
index 068dfd351b5..5176c5ded27 100644
--- a/apps/files_external/l10n/sq.json
+++ b/apps/files_external/l10n/sq.json
@@ -16,6 +16,7 @@
"Unsatisfied authentication mechanism parameters" : "Parametra mekanizmi mirëfilltësimi të papërmbushur",
"Insufficient data: %s" : "Të dhëna të pamjaftueshme: %s",
"%s" : "%s",
+ "Storage with id \"%i\" is not user editable" : "Depozita me id \"%i\" s’është e përpunueshme nga përdoruesi",
"Personal" : "Personale",
"System" : "Sistem",
"Grant access" : "Akordoji hyrje",
@@ -57,9 +58,7 @@
"Tenant name" : "Emër qiraxhiu",
"Rackspace" : "Rackspace",
"API key" : "Kyç API",
- "Login credentials" : "Kredenciale hyrjesh",
"Username and password" : "Emër përdoruesi dhe fjalëkalim",
- "Session credentials" : "Kredenciale sesioni",
"RSA public key" : "Kyç publik RSA ",
"Public key" : "Kyç publik",
"Amazon S3" : "Amazon S3",
diff --git a/apps/files_external/l10n/th_TH.js b/apps/files_external/l10n/th_TH.js
index 90db47a31d7..31993619cae 100644
--- a/apps/files_external/l10n/th_TH.js
+++ b/apps/files_external/l10n/th_TH.js
@@ -60,9 +60,7 @@ OC.L10N.register(
"Identity endpoint URL" : "ตัวตนของ URL ปลายทาง",
"Rackspace" : "Rackspace",
"API key" : "รหัส API",
- "Login credentials" : "เข้าสู่ระบบด้วยข้อมูลประจำตัว",
"Username and password" : "ชื่อผู้ใช้และรหัสผ่าน",
- "Session credentials" : "ข้อมูลของเซสชั่น",
"RSA public key" : "RSA คีย์สาธารณะ",
"Public key" : "คีย์สาธารณะ",
"Amazon S3" : "Amazon S3",
diff --git a/apps/files_external/l10n/th_TH.json b/apps/files_external/l10n/th_TH.json
index 4f327592ab8..07424cc1d23 100644
--- a/apps/files_external/l10n/th_TH.json
+++ b/apps/files_external/l10n/th_TH.json
@@ -58,9 +58,7 @@
"Identity endpoint URL" : "ตัวตนของ URL ปลายทาง",
"Rackspace" : "Rackspace",
"API key" : "รหัส API",
- "Login credentials" : "เข้าสู่ระบบด้วยข้อมูลประจำตัว",
"Username and password" : "ชื่อผู้ใช้และรหัสผ่าน",
- "Session credentials" : "ข้อมูลของเซสชั่น",
"RSA public key" : "RSA คีย์สาธารณะ",
"Public key" : "คีย์สาธารณะ",
"Amazon S3" : "Amazon S3",
diff --git a/apps/files_external/l10n/tr.js b/apps/files_external/l10n/tr.js
index 8f75ae2a274..cb506b24c1f 100644
--- a/apps/files_external/l10n/tr.js
+++ b/apps/files_external/l10n/tr.js
@@ -51,7 +51,6 @@ OC.L10N.register(
"Rackspace" : "Rackspace",
"API key" : "API anahtarı",
"Username and password" : "Kullanıcı adı ve parola",
- "Session credentials" : "Oturum bilgileri",
"RSA public key" : "RSA ortak anahtarı",
"Public key" : "Ortak anahtar",
"Amazon S3" : "Amazon S3",
diff --git a/apps/files_external/l10n/tr.json b/apps/files_external/l10n/tr.json
index fdae4642acc..1eeaafa433c 100644
--- a/apps/files_external/l10n/tr.json
+++ b/apps/files_external/l10n/tr.json
@@ -49,7 +49,6 @@
"Rackspace" : "Rackspace",
"API key" : "API anahtarı",
"Username and password" : "Kullanıcı adı ve parola",
- "Session credentials" : "Oturum bilgileri",
"RSA public key" : "RSA ortak anahtarı",
"Public key" : "Ortak anahtar",
"Amazon S3" : "Amazon S3",
diff --git a/apps/files_external/lib/auth/password/logincredentials.php b/apps/files_external/lib/auth/password/logincredentials.php
index 99cac3f4202..25bd66fb41a 100644
--- a/apps/files_external/lib/auth/password/logincredentials.php
+++ b/apps/files_external/lib/auth/password/logincredentials.php
@@ -52,7 +52,7 @@ class LoginCredentials extends AuthMechanism {
$this
->setIdentifier('password::logincredentials')
->setScheme(self::SCHEME_PASSWORD)
- ->setText($l->t('Login credentials'))
+ ->setText($l->t('Log-in credentials, save in database'))
->addParameters([
])
;
diff --git a/apps/files_external/lib/auth/password/sessioncredentials.php b/apps/files_external/lib/auth/password/sessioncredentials.php
index 3fb8b8526cc..d8e8443418a 100644
--- a/apps/files_external/lib/auth/password/sessioncredentials.php
+++ b/apps/files_external/lib/auth/password/sessioncredentials.php
@@ -50,7 +50,7 @@ class SessionCredentials extends AuthMechanism {
$this
->setIdentifier('password::sessioncredentials')
->setScheme(self::SCHEME_PASSWORD)
- ->setText($l->t('Session credentials'))
+ ->setText($l->t('Log-in credentials, save in session'))
->addParameters([
])
;
diff --git a/apps/files_external/lib/auth/password/userprovided.php b/apps/files_external/lib/auth/password/userprovided.php
index e1c1352022f..2f277163184 100644
--- a/apps/files_external/lib/auth/password/userprovided.php
+++ b/apps/files_external/lib/auth/password/userprovided.php
@@ -49,7 +49,7 @@ class UserProvided extends AuthMechanism implements IUserProvided {
->setIdentifier('password::userprovided')
->setVisibility(BackendService::VISIBILITY_ADMIN)
->setScheme(self::SCHEME_PASSWORD)
- ->setText($l->t('User provided'))
+ ->setText($l->t('User entered, store in database'))
->addParameters([
(new DefinitionParameter('user', $l->t('Username')))
->setFlag(DefinitionParameter::FLAG_USER_PROVIDED),
diff --git a/apps/files_external/lib/definitionparameter.php b/apps/files_external/lib/definitionparameter.php
index dc7985837f5..27c6af0fcda 100644
--- a/apps/files_external/lib/definitionparameter.php
+++ b/apps/files_external/lib/definitionparameter.php
@@ -131,27 +131,11 @@ class DefinitionParameter implements \JsonSerializable {
* @return string
*/
public function jsonSerialize() {
- $prefix = '';
- switch ($this->getType()) {
- case self::VALUE_BOOLEAN:
- $prefix = '!';
- break;
- case self::VALUE_PASSWORD:
- $prefix = '*';
- break;
- case self::VALUE_HIDDEN:
- $prefix = '#';
- break;
- }
-
- if ($this->isFlagSet(self::FLAG_OPTIONAL)) {
- $prefix = '&' . $prefix;
- }
- if ($this->isFlagSet(self::FLAG_USER_PROVIDED)) {
- $prefix = '@' . $prefix;
- }
-
- return $prefix . $this->getText();
+ return [
+ 'value' => $this->getText(),
+ 'flags' => $this->getFlags(),
+ 'type' => $this->getType()
+ ];
}
public function isOptional() {
diff --git a/apps/files_external/tests/definitionparameterttest.php b/apps/files_external/tests/definitionparameterttest.php
index dc7c150ec96..e89058d5c78 100644
--- a/apps/files_external/tests/definitionparameterttest.php
+++ b/apps/files_external/tests/definitionparameterttest.php
@@ -27,18 +27,34 @@ class DefinitionParameterTest extends \Test\TestCase {
public function testJsonSerialization() {
$param = new Param('foo', 'bar');
- $this->assertEquals('bar', $param->jsonSerialize());
+ $this->assertEquals([
+ 'value' => 'bar',
+ 'flags' => 0,
+ 'type' => 0
+ ], $param->jsonSerialize());
$param->setType(Param::VALUE_BOOLEAN);
- $this->assertEquals('!bar', $param->jsonSerialize());
+ $this->assertEquals([
+ 'value' => 'bar',
+ 'flags' => 0,
+ 'type' => Param::VALUE_BOOLEAN
+ ], $param->jsonSerialize());
$param->setType(Param::VALUE_PASSWORD);
$param->setFlag(Param::FLAG_OPTIONAL);
- $this->assertEquals('&*bar', $param->jsonSerialize());
+ $this->assertEquals([
+ 'value' => 'bar',
+ 'flags' => Param::FLAG_OPTIONAL,
+ 'type' => Param::VALUE_PASSWORD
+ ], $param->jsonSerialize());
$param->setType(Param::VALUE_HIDDEN);
$param->setFlags(Param::FLAG_NONE);
- $this->assertEquals('#bar', $param->jsonSerialize());
+ $this->assertEquals([
+ 'value' => 'bar',
+ 'flags' => Param::FLAG_NONE,
+ 'type' => Param::VALUE_HIDDEN
+ ], $param->jsonSerialize());
}
public function validateValueProvider() {
diff --git a/apps/files_external/tests/js/settingsSpec.js b/apps/files_external/tests/js/settingsSpec.js
index b2b5e1f57ec..6f5bb2a8e3e 100644
--- a/apps/files_external/tests/js/settingsSpec.js
+++ b/apps/files_external/tests/js/settingsSpec.js
@@ -58,8 +58,13 @@ describe('OCA.External.Settings tests', function() {
'identifier': '\\OC\\TestBackend',
'name': 'Test Backend',
'configuration': {
- 'field1': 'Display Name 1',
- 'field2': '&Display Name 2'
+ 'field1': {
+ 'value': 'Display Name 1'
+ },
+ 'field2': {
+ 'value': 'Display Name 2',
+ 'flags': 1
+ }
},
'authSchemes': {
'builtin': true,
@@ -70,8 +75,13 @@ describe('OCA.External.Settings tests', function() {
'identifier': '\\OC\\AnotherTestBackend',
'name': 'Another Test Backend',
'configuration': {
- 'field1': 'Display Name 1',
- 'field2': '&Display Name 2'
+ 'field1': {
+ 'value': 'Display Name 1'
+ },
+ 'field2': {
+ 'value': 'Display Name 2',
+ 'flags': 1
+ }
},
'authSchemes': {
'builtin': true,
@@ -82,12 +92,30 @@ describe('OCA.External.Settings tests', function() {
'identifier': '\\OC\\InputsTestBackend',
'name': 'Inputs test backend',
'configuration': {
- 'field_text': 'Text field',
- 'field_password': '*Password field',
- 'field_bool': '!Boolean field',
- 'field_hidden': '#Hidden field',
- 'field_text_optional': '&Text field optional',
- 'field_password_optional': '&*Password field optional'
+ 'field_text': {
+ 'value': 'Text field'
+ },
+ 'field_password': {
+ 'value': ',Password field',
+ 'type': 2
+ },
+ 'field_bool': {
+ 'value': 'Boolean field',
+ 'type': 1
+ },
+ 'field_hidden': {
+ 'value': 'Hidden field',
+ 'type': 3
+ },
+ 'field_text_optional': {
+ 'value': 'Text field optional',
+ 'flags': 1
+ },
+ 'field_password_optional': {
+ 'value': 'Password field optional',
+ 'flags': 1,
+ 'type': 2
+ }
},
'authSchemes': {
'builtin': true,
@@ -335,6 +363,7 @@ describe('OCA.External.Settings tests', function() {
expect(JSON.parse($tr.find('input.mountOptions').val())).toEqual({
encrypt: true,
previews: true,
+ enable_sharing: true,
filesystem_check_changes: 0
});
});
diff --git a/apps/files_sharing/api/share20ocs.php b/apps/files_sharing/api/share20ocs.php
index 2dadc0888ec..8fe8991f9c9 100644
--- a/apps/files_sharing/api/share20ocs.php
+++ b/apps/files_sharing/api/share20ocs.php
@@ -329,9 +329,13 @@ class Share20OCS {
return new \OC_OCS_Result($share);
}
- private function getSharedWithMe() {
- $userShares = $this->shareManager->getSharedWith($this->currentUser, \OCP\Share::SHARE_TYPE_USER, -1, 0);
- $groupShares = $this->shareManager->getSharedWith($this->currentUser, \OCP\Share::SHARE_TYPE_GROUP, -1, 0);
+ /**
+ * @param \OCP\Files\File|\OCP\Files\Folder $node
+ * @return \OC_OCS_Result
+ */
+ private function getSharedWithMe($node = null) {
+ $userShares = $this->shareManager->getSharedWith($this->currentUser, \OCP\Share::SHARE_TYPE_USER, $node, -1, 0);
+ $groupShares = $this->shareManager->getSharedWith($this->currentUser, \OCP\Share::SHARE_TYPE_GROUP, $node, -1, 0);
$shares = array_merge($userShares, $groupShares);
@@ -390,10 +394,6 @@ class Share20OCS {
$subfiles = $this->request->getParam('subfiles');
$path = $this->request->getParam('path', null);
- if ($sharedWithMe === 'true') {
- return $this->getSharedWithMe();
- }
-
if ($path !== null) {
$userFolder = $this->rootFolder->getUserFolder($this->currentUser->getUID());
try {
@@ -403,6 +403,10 @@ class Share20OCS {
}
}
+ if ($sharedWithMe === 'true') {
+ return $this->getSharedWithMe($path);
+ }
+
if ($subfiles === 'true') {
return $this->getSharesInDir($path);
}
diff --git a/apps/files_sharing/api/sharees.php b/apps/files_sharing/api/sharees.php
index d23a6f56501..85cea2e4238 100644
--- a/apps/files_sharing/api/sharees.php
+++ b/apps/files_sharing/api/sharees.php
@@ -270,8 +270,15 @@ class Sharees {
$addressBookContacts = $this->contactsManager->search($search, ['CLOUD', 'FN']);
$foundRemoteById = false;
foreach ($addressBookContacts as $contact) {
+ if (isset($contact['isLocalSystemBook'])) {
+ continue;
+ }
if (isset($contact['CLOUD'])) {
- foreach ($contact['CLOUD'] as $cloudId) {
+ $cloudIds = $contact['CLOUD'];
+ if (!is_array($cloudIds)) {
+ $cloudIds = [$cloudIds];
+ }
+ foreach ($cloudIds as $cloudId) {
if (strtolower($contact['FN']) === $search || strtolower($cloudId) === $search) {
if (strtolower($cloudId) === $search) {
$foundRemoteById = true;
diff --git a/apps/files_sharing/lib/sharedstorage.php b/apps/files_sharing/lib/sharedstorage.php
index 3ae5749ea87..101503a03fb 100644
--- a/apps/files_sharing/lib/sharedstorage.php
+++ b/apps/files_sharing/lib/sharedstorage.php
@@ -32,6 +32,7 @@ namespace OC\Files\Storage;
use OC\Files\Filesystem;
use OCA\Files_Sharing\ISharedStorage;
+use OCP\Constants;
use OCP\Files\Cache\ICacheEntry;
use OCP\Files\Storage\IStorage;
use OCP\Lock\ILockingProvider;
@@ -83,6 +84,10 @@ class Shared extends \OC\Files\Storage\Common implements ISharedStorage {
$this->sourceRootInfo = $this->sourceStorage->getCache()->get($sourceInternalPath);
}
+ private function isValid() {
+ return ($this->sourceRootInfo->getPermissions() & Constants::PERMISSION_SHARE) === Constants::PERMISSION_SHARE;
+ }
+
/**
* get id of the mount point
*
@@ -133,6 +138,9 @@ class Shared extends \OC\Files\Storage\Common implements ISharedStorage {
* @return string|false source file path or false if not found
*/
public function getSourcePath($target) {
+ if (!$this->isValid()){
+ return false;
+ }
$source = $this->getFile($target);
if ($source) {
if (!isset($source['fullPath'])) {
@@ -157,6 +165,9 @@ class Shared extends \OC\Files\Storage\Common implements ISharedStorage {
* @return int CRUDS permissions granted
*/
public function getPermissions($target = '') {
+ if (!$this->isValid()) {
+ return 0;
+ }
$permissions = $this->share['permissions'];
// part files and the mount point always have delete permissions
if ($target === '' || pathinfo($target, PATHINFO_EXTENSION) === 'part') {
@@ -253,13 +264,14 @@ class Shared extends \OC\Files\Storage\Common implements ISharedStorage {
}
public function isReadable($path) {
- $isReadable = false;
- if ($source = $this->getSourcePath($path)) {
- list($storage, $internalPath) = \OC\Files\Filesystem::resolvePath($source);
- $isReadable = $storage->isReadable($internalPath);
+ if (!$this->isValid()) {
+ return false;
}
-
- return $isReadable && $this->file_exists($path);
+ if (!$this->file_exists($path)) {
+ return false;
+ }
+ list($storage, $internalPath) = $this->resolvePath($path);
+ return $storage->isReadable($internalPath);
}
public function isUpdatable($path) {
diff --git a/apps/systemtags/activity/extension.php b/apps/systemtags/activity/extension.php
new file mode 100644
index 00000000000..4a974611509
--- /dev/null
+++ b/apps/systemtags/activity/extension.php
@@ -0,0 +1,300 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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 OCA\SystemTags\Activity;
+
+use OCP\Activity\IExtension;
+use OCP\Activity\IManager;
+use OCP\IL10N;
+use OCP\L10N\IFactory;
+
+/**
+ * Class Extension
+ *
+ * @package OCA\SystemTags\Activity
+ */
+class Extension implements IExtension {
+ const APP_NAME = 'systemtags';
+
+ const CREATE_TAG = 'create_tag';
+ const UPDATE_TAG = 'update_tag';
+ const DELETE_TAG = 'delete_tag';
+
+ const ASSIGN_TAG = 'assign_tag';
+ const UNASSIGN_TAG = 'unassign_tag';
+
+ /** @var IFactory */
+ protected $languageFactory;
+
+ /** @var IManager */
+ protected $activityManager;
+
+ /**
+ * @param IFactory $languageFactory
+ * @param IManager $activityManager
+ */
+ public function __construct(IFactory $languageFactory, IManager $activityManager) {
+ $this->languageFactory = $languageFactory;
+ $this->activityManager = $activityManager;
+ }
+
+ protected function getL10N($languageCode = null) {
+ return $this->languageFactory->get(self::APP_NAME, $languageCode);
+ }
+
+ /**
+ * The extension can return an array of additional notification types.
+ * If no additional types are to be added false is to be returned
+ *
+ * @param string $languageCode
+ * @return array|false
+ */
+ public function getNotificationTypes($languageCode) {
+ $l = $this->getL10N($languageCode);
+
+ return array(
+ self::APP_NAME => (string) $l->t('<strong>System tags</strong> for a file have been modified'),
+ );
+ }
+
+ /**
+ * For a given method additional types to be displayed in the settings can be returned.
+ * In case no additional types are to be added false is to be returned.
+ *
+ * @param string $method
+ * @return array|false
+ */
+ public function getDefaultTypes($method) {
+ return $method === self::METHOD_STREAM ? [self::APP_NAME] : false;
+ }
+
+ /**
+ * A string naming the css class for the icon to be used can be returned.
+ * If no icon is known for the given type false is to be returned.
+ *
+ * @param string $type
+ * @return string|false
+ */
+ public function getTypeIcon($type) {
+ switch ($type) {
+ case self::APP_NAME:
+ return false;
+ }
+
+ return false;
+ }
+
+ /**
+ * The extension can translate a given message to the requested languages.
+ * If no translation is available false is to be returned.
+ *
+ * @param string $app
+ * @param string $text
+ * @param array $params
+ * @param boolean $stripPath
+ * @param boolean $highlightParams
+ * @param string $languageCode
+ * @return string|false
+ */
+ public function translate($app, $text, $params, $stripPath, $highlightParams, $languageCode) {
+ if ($app !== self::APP_NAME) {
+ return false;
+ }
+
+ $l = $this->getL10N($languageCode);
+
+ if ($this->activityManager->isFormattingFilteredObject()) {
+ $translation = $this->translateShort($text, $l, $params);
+ if ($translation !== false) {
+ return $translation;
+ }
+ }
+
+ return $this->translateLong($text, $l, $params);
+ }
+
+ /**
+ * @param string $text
+ * @param IL10N $l
+ * @param array $params
+ * @return bool|string
+ */
+ protected function translateShort($text, IL10N $l, array $params) {
+
+ switch ($text) {
+ case self::ASSIGN_TAG:
+ $params[2] = $this->convertParameterToTag($params[2], $l);
+ return (string) $l->t('%1$s assigned system tag %3$s', $params);
+ case self::UNASSIGN_TAG:
+ $params[2] = $this->convertParameterToTag($params[2], $l);
+ return (string) $l->t('%1$s unassigned system tag %3$s', $params);
+ }
+
+ return false;
+ }
+
+ /**
+ * @param string $text
+ * @param IL10N $l
+ * @param array $params
+ * @return bool|string
+ */
+ protected function translateLong($text, IL10N $l, array $params) {
+
+ switch ($text) {
+ case self::CREATE_TAG:
+ $params[1] = $this->convertParameterToTag($params[1], $l);
+ return (string) $l->t('%1$s created system tag %2$s', $params);
+ case self::DELETE_TAG:
+ $params[1] = $this->convertParameterToTag($params[1], $l);
+ return (string) $l->t('%1$s deleted system tag %2$s', $params);
+ case self::UPDATE_TAG:
+ $params[1] = $this->convertParameterToTag($params[1], $l);
+ $params[2] = $this->convertParameterToTag($params[2], $l);
+ return (string) $l->t('%1$s updated system tag %3$s to %2$s', $params);
+ case self::ASSIGN_TAG:
+ $params[2] = $this->convertParameterToTag($params[2], $l);
+ return (string) $l->t('%1$s assigned system tag %3$s to %2$s', $params);
+ case self::UNASSIGN_TAG:
+ $params[2] = $this->convertParameterToTag($params[2], $l);
+ return (string) $l->t('%1$s unassigned system tag %3$s from %2$s', $params);
+ }
+
+ return false;
+ }
+
+ /**
+ * The extension can define the type of parameters for translation
+ *
+ * Currently known types are:
+ * * file => will strip away the path of the file and add a tooltip with it
+ * * username => will add the avatar of the user
+ *
+ * @param string $app
+ * @param string $text
+ * @return array|false
+ */
+ public function getSpecialParameterList($app, $text) {
+ if ($app === self::APP_NAME) {
+ switch ($text) {
+ case self::CREATE_TAG:
+ case self::DELETE_TAG:
+ return array(
+ 0 => 'username',
+ //1 => 'systemtag description',
+ );
+ case self::UPDATE_TAG:
+ return array(
+ 0 => 'username',
+ //1 => 'systemtag description',
+ //2 => 'systemtag description',
+ );
+
+ case self::ASSIGN_TAG:
+ case self::UNASSIGN_TAG:
+ return array(
+ 0 => 'username',
+ 1 => 'file',
+ //2 => 'systemtag description',
+ );
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * The extension can define the parameter grouping by returning the index as integer.
+ * In case no grouping is required false is to be returned.
+ *
+ * @param array $activity
+ * @return integer|false
+ */
+ public function getGroupParameter($activity) {
+ return false;
+ }
+
+ /**
+ * The extension can define additional navigation entries. The array returned has to contain two keys 'top'
+ * and 'apps' which hold arrays with the relevant entries.
+ * If no further entries are to be added false is no be returned.
+ *
+ * @return array|false
+ */
+ public function getNavigation() {
+ return false;
+ }
+
+ /**
+ * The extension can check if a custom filter (given by a query string like filter=abc) is valid or not.
+ *
+ * @param string $filterValue
+ * @return boolean
+ */
+ public function isFilterValid($filterValue) {
+ return false;
+ }
+
+ /**
+ * The extension can filter the types based on the filter if required.
+ * In case no filter is to be applied false is to be returned unchanged.
+ *
+ * @param array $types
+ * @param string $filter
+ * @return array|false
+ */
+ public function filterNotificationTypes($types, $filter) {
+ return false;
+ }
+
+ /**
+ * For a given filter the extension can specify the sql query conditions including parameters for that query.
+ * In case the extension does not know the filter false is to be returned.
+ * The query condition and the parameters are to be returned as array with two elements.
+ * E.g. return array('`app` = ? and `message` like ?', array('mail', 'ownCloud%'));
+ *
+ * @param string $filter
+ * @return array|false
+ */
+ public function getQueryForFilter($filter) {
+ return false;
+ }
+
+ /**
+ * @param string $parameter
+ * @param IL10N $l
+ * @return string
+ */
+ protected function convertParameterToTag($parameter, IL10N $l) {
+ if (preg_match('/^\<parameter\>\{\{\{(.*)\|\|\|(.*)\}\}\}\<\/parameter\>$/', $parameter, $matches)) {
+ switch ($matches[2]) {
+ case 'assignable':
+ return '<parameter>' . $matches[1] . '</parameter>';
+ case 'not-assignable':
+ return '<parameter>' . $l->t('%s (not-assignable)', $matches[1]) . '</parameter>';
+ case 'invisible':
+ return '<parameter>' . $l->t('%s (invisible)', $matches[1]) . '</parameter>';
+ }
+ }
+
+ return $parameter;
+ }
+}
diff --git a/apps/systemtags/activity/listener.php b/apps/systemtags/activity/listener.php
new file mode 100644
index 00000000000..9b6597119c6
--- /dev/null
+++ b/apps/systemtags/activity/listener.php
@@ -0,0 +1,223 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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 OCA\SystemTags\Activity;
+
+use OCP\Activity\IManager;
+use OCP\App\IAppManager;
+use OCP\Files\Config\IMountProviderCollection;
+use OCP\Files\IRootFolder;
+use OCP\Files\Node;
+use OCP\IGroup;
+use OCP\IGroupManager;
+use OCP\IUser;
+use OCP\IUserSession;
+use OCP\Share;
+use OCP\SystemTag\ISystemTag;
+use OCP\SystemTag\ISystemTagManager;
+use OCP\SystemTag\ManagerEvent;
+use OCP\SystemTag\MapperEvent;
+use OCP\SystemTag\TagNotFoundException;
+
+class Listener {
+ /** @var IGroupManager */
+ protected $groupManager;
+ /** @var IManager */
+ protected $activityManager;
+ /** @var IUserSession */
+ protected $session;
+ /** @var \OCP\SystemTag\ISystemTagManager */
+ protected $tagManager;
+ /** @var \OCP\App\IAppManager */
+ protected $appManager;
+ /** @var \OCP\Files\Config\IMountProviderCollection */
+ protected $mountCollection;
+ /** @var \OCP\Files\IRootFolder */
+ protected $rootFolder;
+
+ /**
+ * Listener constructor.
+ *
+ * @param IGroupManager $groupManager
+ * @param IManager $activityManager
+ * @param IUserSession $session
+ * @param ISystemTagManager $tagManager
+ * @param IAppManager $appManager
+ * @param IMountProviderCollection $mountCollection
+ * @param IRootFolder $rootFolder
+ */
+ public function __construct(IGroupManager $groupManager,
+ IManager $activityManager,
+ IUserSession $session,
+ ISystemTagManager $tagManager,
+ IAppManager $appManager,
+ IMountProviderCollection $mountCollection,
+ IRootFolder $rootFolder) {
+ $this->groupManager = $groupManager;
+ $this->activityManager = $activityManager;
+ $this->session = $session;
+ $this->tagManager = $tagManager;
+ $this->appManager = $appManager;
+ $this->mountCollection = $mountCollection;
+ $this->rootFolder = $rootFolder;
+ }
+
+ /**
+ * @param ManagerEvent $event
+ */
+ public function event(ManagerEvent $event) {
+ $actor = $this->session->getUser();
+ if ($actor instanceof IUser) {
+ $actor = $actor->getUID();
+ } else {
+ $actor = '';
+ }
+
+ $activity = $this->activityManager->generateEvent();
+ $activity->setApp(Extension::APP_NAME)
+ ->setType(Extension::APP_NAME)
+ ->setAuthor($actor);
+ if ($event->getEvent() === ManagerEvent::EVENT_CREATE) {
+ $activity->setSubject(Extension::CREATE_TAG, [
+ $actor,
+ $this->prepareTagAsParameter($event->getTag()),
+ ]);
+ } else if ($event->getEvent() === ManagerEvent::EVENT_UPDATE) {
+ $activity->setSubject(Extension::UPDATE_TAG, [
+ $actor,
+ $this->prepareTagAsParameter($event->getTag()),
+ $this->prepareTagAsParameter($event->getTagBefore()),
+ ]);
+ } else if ($event->getEvent() === ManagerEvent::EVENT_DELETE) {
+ $activity->setSubject(Extension::DELETE_TAG, [
+ $actor,
+ $this->prepareTagAsParameter($event->getTag()),
+ ]);
+ } else {
+ return;
+ }
+
+ $group = $this->groupManager->get('admin');
+ if ($group instanceof IGroup) {
+ foreach ($group->getUsers() as $user) {
+ $activity->setAffectedUser($user->getUID());
+ $this->activityManager->publish($activity);
+ }
+ }
+ }
+
+ /**
+ * @param MapperEvent $event
+ */
+ public function mapperEvent(MapperEvent $event) {
+ $tagIds = $event->getTags();
+ if ($event->getObjectType() !== 'files' ||empty($tagIds)
+ || !in_array($event->getEvent(), [MapperEvent::EVENT_ASSIGN, MapperEvent::EVENT_UNASSIGN])
+ || !$this->appManager->isInstalled('activity')) {
+ // System tags not for files, no tags, not (un-)assigning or no activity-app enabled (save the energy)
+ return;
+ }
+
+ try {
+ $tags = $this->tagManager->getTagsByIds($tagIds);
+ } catch (TagNotFoundException $e) {
+ // User assigned/unassigned a non-existing tag, ignore...
+ return;
+ }
+
+ if (empty($tags)) {
+ return;
+ }
+
+ // Get all mount point owners
+ $cache = $this->mountCollection->getMountCache();
+ $mounts = $cache->getMountsForFileId($event->getObjectId());
+ if (empty($mounts)) {
+ return;
+ }
+
+ $users = [];
+ foreach ($mounts as $mount) {
+ $owner = $mount->getUser()->getUID();
+ $ownerFolder = $this->rootFolder->getUserFolder($owner);
+ $nodes = $ownerFolder->getById($event->getObjectId());
+ if (!empty($nodes)) {
+ /** @var Node $node */
+ $node = array_shift($nodes);
+ $path = $node->getPath();
+ if (strpos($path, '/' . $owner . '/files/') === 0) {
+ $path = substr($path, strlen('/' . $owner . '/files'));
+ }
+ // Get all users that have access to the mount point
+ $users = array_merge($users, Share::getUsersSharingFile($path, $owner, true, true));
+ }
+ }
+
+ $actor = $this->session->getUser();
+ if ($actor instanceof IUser) {
+ $actor = $actor->getUID();
+ } else {
+ $actor = '';
+ }
+
+ $activity = $this->activityManager->generateEvent();
+ $activity->setApp(Extension::APP_NAME)
+ ->setType(Extension::APP_NAME)
+ ->setAuthor($actor)
+ ->setObject($event->getObjectType(), $event->getObjectId());
+
+ foreach ($users as $user => $path) {
+ $activity->setAffectedUser($user);
+
+ foreach ($tags as $tag) {
+ if ($event->getEvent() === MapperEvent::EVENT_ASSIGN) {
+ $activity->setSubject(Extension::ASSIGN_TAG, [
+ $actor,
+ $path,
+ $this->prepareTagAsParameter($tag),
+ ]);
+ } else if ($event->getEvent() === MapperEvent::EVENT_UNASSIGN) {
+ $activity->setSubject(Extension::UNASSIGN_TAG, [
+ $actor,
+ $path,
+ $this->prepareTagAsParameter($tag),
+ ]);
+ }
+
+ $this->activityManager->publish($activity);
+ }
+ }
+ }
+
+ /**
+ * @param ISystemTag $tag
+ * @return string
+ */
+ protected function prepareTagAsParameter(ISystemTag $tag) {
+ if (!$tag->isUserVisible()) {
+ return '{{{' . $tag->getName() . '|||invisible}}}';
+ } else if (!$tag->isUserAssignable()) {
+ return '{{{' . $tag->getName() . '|||not-assignable}}}';
+ } else {
+ return '{{{' . $tag->getName() . '|||assignable}}}';
+ }
+ }
+}
diff --git a/apps/systemtags/appinfo/app.php b/apps/systemtags/appinfo/app.php
index d3886993f8f..0bb57e1227b 100644
--- a/apps/systemtags/appinfo/app.php
+++ b/apps/systemtags/appinfo/app.php
@@ -19,6 +19,11 @@
*
*/
+use OCA\SystemTags\Activity\Extension;
+use OCA\SystemTags\Activity\Listener;
+use OCP\SystemTag\ManagerEvent;
+use OCP\SystemTag\MapperEvent;
+
$eventDispatcher = \OC::$server->getEventDispatcher();
$eventDispatcher->addListener(
'OCA\Files::loadAdditionalScripts',
@@ -39,3 +44,32 @@ $eventDispatcher->addListener(
\OCP\Util::addStyle('systemtags');
}
);
+
+$activityManager = \OC::$server->getActivityManager();
+$activityManager->registerExtension(function() {
+ $application = new \OCP\AppFramework\App('systemtags');
+ /** @var \OCA\SystemTags\Activity\Extension $extension */
+ $extension = $application->getContainer()->query('OCA\SystemTags\Activity\Extension');
+ return $extension;
+});
+
+$managerListener = function(ManagerEvent $event) use ($activityManager) {
+ $application = new \OCP\AppFramework\App('systemtags');
+ /** @var \OCA\SystemTags\Activity\Listener $listener */
+ $listener = $application->getContainer()->query('OCA\SystemTags\Activity\Listener');
+ $listener->event($event);
+};
+
+$eventDispatcher->addListener(ManagerEvent::EVENT_CREATE, $managerListener);
+$eventDispatcher->addListener(ManagerEvent::EVENT_DELETE, $managerListener);
+$eventDispatcher->addListener(ManagerEvent::EVENT_UPDATE, $managerListener);
+
+$mapperListener = function(MapperEvent $event) use ($activityManager) {
+ $application = new \OCP\AppFramework\App('systemtags');
+ /** @var \OCA\SystemTags\Activity\Listener $listener */
+ $listener = $application->getContainer()->query('OCA\SystemTags\Activity\Listener');
+ $listener->mapperEvent($event);
+};
+
+$eventDispatcher->addListener(MapperEvent::EVENT_ASSIGN, $mapperListener);
+$eventDispatcher->addListener(MapperEvent::EVENT_UNASSIGN, $mapperListener);
diff --git a/apps/systemtags/appinfo/info.xml b/apps/systemtags/appinfo/info.xml
index d2f30e2c040..5da945db703 100644
--- a/apps/systemtags/appinfo/info.xml
+++ b/apps/systemtags/appinfo/info.xml
@@ -6,11 +6,14 @@
<licence>AGPL</licence>
<author>Vincent Petry</author>
<default_enable/>
- <version>0.1</version>
+ <version>0.2</version>
<dependencies>
<owncloud min-version="9.0" max-version="9.0" />
</dependencies>
<documentation>
<user>user-systemtags</user>
</documentation>
+ <types>
+ <logging/>
+ </types>
</info>
diff --git a/apps/user_ldap/l10n/he.js b/apps/user_ldap/l10n/he.js
index 8b10da2ea1e..56be7447e72 100644
--- a/apps/user_ldap/l10n/he.js
+++ b/apps/user_ldap/l10n/he.js
@@ -17,6 +17,7 @@ OC.L10N.register(
"Configuration incomplete" : "הגדרה לא מלאה",
"Configuration OK" : "הגדרה בסדר",
"Select groups" : "בחירת קבוצות",
+ "Select object classes" : "בחירת מחלקות עצמים",
"Please check the credentials, they seem to be wrong." : "יש לבדוק את פרטי הכניסה, נראה שהם שגויים",
"Please specify the port, it could not be auto-detected." : "יש לספק את שער הכניסה - פורט, לא ניתן היה לאתרו בצורה אוטומטית",
"Base DN could not be auto-detected, please revise credentials, host and port." : "לא ניתן היה לאתר באופן אוטומטי את בסיס DN, יש להחליף את פרטי הכניסה, פרטי שרת ושער גישה - פורט.",
@@ -57,6 +58,7 @@ OC.L10N.register(
"Test Configuration" : "בדיקת הגדרות",
"Help" : "עזרה",
"Groups meeting these criteria are available in %s:" : "קבוצות העומדות בקריטריון זה זמינות ב- %s:",
+ "Only these object classes:" : "מחלקות עצמים אלו בלבד:",
"Only from these groups:" : "רק מקבוצות אלו:",
"Search groups" : "חיפוש בקבוצות",
"Available groups" : "קבוצות זמינות",
@@ -66,23 +68,52 @@ OC.L10N.register(
"The filter specifies which LDAP groups shall have access to the %s instance." : "המסנן הקובע לאיזו קבוצת LDAP תהיה יכולת כניסה למקרה %s.",
"Verify settings and count groups" : "מאמת הגדרות וסופר קבוצות",
"When logging in, %s will find the user based on the following attributes:" : "כאשר מתחברים, %s יחפש את המשתמש על פי המאפיינים הבאים:",
+ "LDAP / AD Username:" : "שם משתמש LDAP / AD:",
+ "Allows login against the LDAP / AD username, which is either uid or samaccountname and will be detected." : "מאפשר התחברות אל מול שם משתמש LDAP / AD, שהוא רק uid או samaccountname ויזוהה.",
+ "LDAP / AD Email Address:" : "כתובת דואר אלקטרוני LDAP / AD:",
+ "Allows login against an email attribute. Mail and mailPrimaryAddress will be allowed." : "מאפשר התחברות אל מול מאפיין דואר אלקטרוני. Mail וכן mailPrimaryAddress יהיו מותרים לשימוש.",
+ "Other Attributes:" : "מאפיינים נוספים:",
+ "Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action. Example: \"uid=%%uid\"" : "מגדיר את הסינון הפעיל, כשיש ניסיון התחברות. %%uid מחליף את שם המשתמש בפעולת ההתחברות. לדוגמא: \"uid=%%uid\"",
+ "Test Loginname" : "בדיקת שם התחברות",
+ "Verify settings" : "מאמת הגדרות",
"1. Server" : "1. שרת",
"%s. Server:" : "%s. שרת:",
"Add a new and blank configuration" : "הוספת תצורה חדשה וריקה",
+ "Copy current configuration into new directory binding" : "מעתיק תצורה נוכחית אל תוך תיקייה חדשה",
"Delete the current configuration" : "מחיקת תצורה נוכחית",
"Host" : "מארח",
+ "You can omit the protocol, except you require SSL. Then start with ldaps://" : "ניתן להשמיט את הפרוטוקול, אך SSL מחייב. לפיכך יש להתחיל עם ldaps://",
"Port" : "פורט",
"Detect Port" : "מחיקת שער - פורט",
"User DN" : "DN משתמש",
+ "The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." : "ה- DN של משתמש הלקוח שבו החיבור יעשה, למשל uid=agent,dc=example,dc=com. לחיבור אנונימי, יש להשאיר את ה- DN והסיסמא ריקים.",
"Password" : "סיסמא",
"For anonymous access, leave DN and Password empty." : "לגישה אנונימית, השאר את הDM והסיסמא ריקים.",
+ "One Base DN per line" : "DN בסיסי אחד לשורה",
+ "You can specify Base DN for users and groups in the Advanced tab" : "ניתן לציין DN בסיסי למשתמשים ולקבוצות בלשונית מתקדם",
+ "Detect Base DN" : "גילוי DN בסיסי",
+ "Test Base DN" : "בדיקת DN בסיסי",
+ "Avoids automatic LDAP requests. Better for bigger setups, but requires some LDAP knowledge." : "נמנע מבקשות אוטומטיות של LDAP. מועדף עבור התקנות גדולות, אבל מחייב ידע מסויים של LDAP.",
+ "Manually enter LDAP filters (recommended for large directories)" : "הכנסת מסנני LDAP ידנית (מומלץ עבוק תיקיות גדולות)",
+ "Limit %s access to users meeting these criteria:" : "מגביל כניסות %s למשתמשים אשר עומדים בתנאים אלו:",
+ "Verify settings and count users" : "מאמת הגדרות וסופר משתמשים",
"Saving" : "שמירה",
"Back" : "אחורה",
"Continue" : "המשך",
"LDAP" : "LDAP",
"Expert" : "מומחה",
"Advanced" : "מתקדם",
+ "Connection Settings" : "הגדרות התחברות",
+ "Configuration Active" : "תצורה פעילה",
+ "When unchecked, this configuration will be skipped." : "כאשר לא מסומן, נדלג על תצורה זו.",
+ "Backup (Replica) Host" : "גיבוי (העתק) שרת",
+ "Backup (Replica) Port" : "גיבוי (העתק) שער - פורט",
+ "Disable Main Server" : "ניטרול שרת עיקרי",
+ "Only connect to the replica server." : "חיבור רק להעתק שרת.",
+ "Turn off SSL certificate validation." : "כיבוי אימות אישורי אבטחה SSL.",
"in seconds. A change empties the cache." : "בשניות. שינוי מרוקן את המטמון.",
+ "Directory Settings" : "הגדרות תיקייה",
+ "Base User Tree" : "עץ משתמש בסיסי",
"in bytes" : "בבתים"
},
"nplurals=2; plural=(n != 1);");
diff --git a/apps/user_ldap/l10n/he.json b/apps/user_ldap/l10n/he.json
index 889a55a7172..ca2ba2f91a6 100644
--- a/apps/user_ldap/l10n/he.json
+++ b/apps/user_ldap/l10n/he.json
@@ -15,6 +15,7 @@
"Configuration incomplete" : "הגדרה לא מלאה",
"Configuration OK" : "הגדרה בסדר",
"Select groups" : "בחירת קבוצות",
+ "Select object classes" : "בחירת מחלקות עצמים",
"Please check the credentials, they seem to be wrong." : "יש לבדוק את פרטי הכניסה, נראה שהם שגויים",
"Please specify the port, it could not be auto-detected." : "יש לספק את שער הכניסה - פורט, לא ניתן היה לאתרו בצורה אוטומטית",
"Base DN could not be auto-detected, please revise credentials, host and port." : "לא ניתן היה לאתר באופן אוטומטי את בסיס DN, יש להחליף את פרטי הכניסה, פרטי שרת ושער גישה - פורט.",
@@ -55,6 +56,7 @@
"Test Configuration" : "בדיקת הגדרות",
"Help" : "עזרה",
"Groups meeting these criteria are available in %s:" : "קבוצות העומדות בקריטריון זה זמינות ב- %s:",
+ "Only these object classes:" : "מחלקות עצמים אלו בלבד:",
"Only from these groups:" : "רק מקבוצות אלו:",
"Search groups" : "חיפוש בקבוצות",
"Available groups" : "קבוצות זמינות",
@@ -64,23 +66,52 @@
"The filter specifies which LDAP groups shall have access to the %s instance." : "המסנן הקובע לאיזו קבוצת LDAP תהיה יכולת כניסה למקרה %s.",
"Verify settings and count groups" : "מאמת הגדרות וסופר קבוצות",
"When logging in, %s will find the user based on the following attributes:" : "כאשר מתחברים, %s יחפש את המשתמש על פי המאפיינים הבאים:",
+ "LDAP / AD Username:" : "שם משתמש LDAP / AD:",
+ "Allows login against the LDAP / AD username, which is either uid or samaccountname and will be detected." : "מאפשר התחברות אל מול שם משתמש LDAP / AD, שהוא רק uid או samaccountname ויזוהה.",
+ "LDAP / AD Email Address:" : "כתובת דואר אלקטרוני LDAP / AD:",
+ "Allows login against an email attribute. Mail and mailPrimaryAddress will be allowed." : "מאפשר התחברות אל מול מאפיין דואר אלקטרוני. Mail וכן mailPrimaryAddress יהיו מותרים לשימוש.",
+ "Other Attributes:" : "מאפיינים נוספים:",
+ "Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action. Example: \"uid=%%uid\"" : "מגדיר את הסינון הפעיל, כשיש ניסיון התחברות. %%uid מחליף את שם המשתמש בפעולת ההתחברות. לדוגמא: \"uid=%%uid\"",
+ "Test Loginname" : "בדיקת שם התחברות",
+ "Verify settings" : "מאמת הגדרות",
"1. Server" : "1. שרת",
"%s. Server:" : "%s. שרת:",
"Add a new and blank configuration" : "הוספת תצורה חדשה וריקה",
+ "Copy current configuration into new directory binding" : "מעתיק תצורה נוכחית אל תוך תיקייה חדשה",
"Delete the current configuration" : "מחיקת תצורה נוכחית",
"Host" : "מארח",
+ "You can omit the protocol, except you require SSL. Then start with ldaps://" : "ניתן להשמיט את הפרוטוקול, אך SSL מחייב. לפיכך יש להתחיל עם ldaps://",
"Port" : "פורט",
"Detect Port" : "מחיקת שער - פורט",
"User DN" : "DN משתמש",
+ "The DN of the client user with which the bind shall be done, e.g. uid=agent,dc=example,dc=com. For anonymous access, leave DN and Password empty." : "ה- DN של משתמש הלקוח שבו החיבור יעשה, למשל uid=agent,dc=example,dc=com. לחיבור אנונימי, יש להשאיר את ה- DN והסיסמא ריקים.",
"Password" : "סיסמא",
"For anonymous access, leave DN and Password empty." : "לגישה אנונימית, השאר את הDM והסיסמא ריקים.",
+ "One Base DN per line" : "DN בסיסי אחד לשורה",
+ "You can specify Base DN for users and groups in the Advanced tab" : "ניתן לציין DN בסיסי למשתמשים ולקבוצות בלשונית מתקדם",
+ "Detect Base DN" : "גילוי DN בסיסי",
+ "Test Base DN" : "בדיקת DN בסיסי",
+ "Avoids automatic LDAP requests. Better for bigger setups, but requires some LDAP knowledge." : "נמנע מבקשות אוטומטיות של LDAP. מועדף עבור התקנות גדולות, אבל מחייב ידע מסויים של LDAP.",
+ "Manually enter LDAP filters (recommended for large directories)" : "הכנסת מסנני LDAP ידנית (מומלץ עבוק תיקיות גדולות)",
+ "Limit %s access to users meeting these criteria:" : "מגביל כניסות %s למשתמשים אשר עומדים בתנאים אלו:",
+ "Verify settings and count users" : "מאמת הגדרות וסופר משתמשים",
"Saving" : "שמירה",
"Back" : "אחורה",
"Continue" : "המשך",
"LDAP" : "LDAP",
"Expert" : "מומחה",
"Advanced" : "מתקדם",
+ "Connection Settings" : "הגדרות התחברות",
+ "Configuration Active" : "תצורה פעילה",
+ "When unchecked, this configuration will be skipped." : "כאשר לא מסומן, נדלג על תצורה זו.",
+ "Backup (Replica) Host" : "גיבוי (העתק) שרת",
+ "Backup (Replica) Port" : "גיבוי (העתק) שער - פורט",
+ "Disable Main Server" : "ניטרול שרת עיקרי",
+ "Only connect to the replica server." : "חיבור רק להעתק שרת.",
+ "Turn off SSL certificate validation." : "כיבוי אימות אישורי אבטחה SSL.",
"in seconds. A change empties the cache." : "בשניות. שינוי מרוקן את המטמון.",
+ "Directory Settings" : "הגדרות תיקייה",
+ "Base User Tree" : "עץ משתמש בסיסי",
"in bytes" : "בבתים"
},"pluralForm" :"nplurals=2; plural=(n != 1);"
} \ No newline at end of file
diff --git a/apps/user_ldap/lib/connection.php b/apps/user_ldap/lib/connection.php
index 97bb13c5f79..addd7d0b51d 100644
--- a/apps/user_ldap/lib/connection.php
+++ b/apps/user_ldap/lib/connection.php
@@ -206,7 +206,7 @@ class Connection extends LDAPUtility {
}
$key = $this->getCacheKey($key);
- return json_decode(base64_decode($this->cache->get($key)));
+ return json_decode(base64_decode($this->cache->get($key)), true);
}
/**
diff --git a/build/integration/features/provisioning-v1.feature b/build/integration/features/provisioning-v1.feature
index 4d4d5361647..3b8633b872a 100644
--- a/build/integration/features/provisioning-v1.feature
+++ b/build/integration/features/provisioning-v1.feature
@@ -282,8 +282,9 @@ Feature: provisioning
Then the OCS status code should be "100"
And the HTTP status code should be "200"
And apps returned are
- | files |
+ | comments |
| dav |
+ | files |
| files_sharing |
| files_trashbin |
| files_versions |
diff --git a/build/integration/features/sharing-v1.feature b/build/integration/features/sharing-v1.feature
index dedf2c388fc..bdc1a4224d8 100644
--- a/build/integration/features/sharing-v1.feature
+++ b/build/integration/features/sharing-v1.feature
@@ -313,6 +313,28 @@ Feature: sharing
And the HTTP status code should be "200"
And last share_id is included in the answer
+ Scenario: Sharee can see the filtered share
+ Given user "user0" exists
+ And user "user1" exists
+ And file "textfile0.txt" of user "user0" is shared with user "user1"
+ And file "textfile1.txt" of user "user0" is shared with user "user1"
+ And As an "user1"
+ When sending "GET" to "/apps/files_sharing/api/v1/shares?shared_with_me=true&path=textfile1 (2).txt"
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ And last share_id is included in the answer
+
+ Scenario: Sharee can't see the share that is filtered out
+ Given user "user0" exists
+ And user "user1" exists
+ And file "textfile0.txt" of user "user0" is shared with user "user1"
+ And file "textfile1.txt" of user "user0" is shared with user "user1"
+ And As an "user1"
+ When sending "GET" to "/apps/files_sharing/api/v1/shares?shared_with_me=true&path=textfile0 (2).txt"
+ Then the OCS status code should be "100"
+ And the HTTP status code should be "200"
+ And last share_id is not included in the answer
+
Scenario: Sharee can see the group share
Given As an "admin"
And user "user0" exists
diff --git a/console.php b/console.php
index 23f40a15437..2073654fa8d 100644
--- a/console.php
+++ b/console.php
@@ -76,6 +76,10 @@ try {
exit(1);
}
+ if (!function_exists('pcntl_signal')) {
+ echo "The process control (PCNTL) extensions are required in case you want to interrupt long running commands - see http://php.net/manual/en/book.pcntl.php" . PHP_EOL;
+ }
+
$application = new Application(\OC::$server->getConfig());
$application->loadCommands(new ConsoleOutput());
$application->run();
diff --git a/core/command/base.php b/core/command/base.php
index bc5ae2e429b..a34d7ec1c9a 100644
--- a/core/command/base.php
+++ b/core/command/base.php
@@ -33,6 +33,12 @@ class Base extends Command {
protected $defaultOutputFormat = self::OUTPUT_FORMAT_PLAIN;
+ /** @var boolean */
+ private $php_pcntl_signal = false;
+
+ /** @var boolean */
+ private $interrupted = false;
+
protected function configure() {
$this
->addOption(
@@ -43,6 +49,15 @@ class Base extends Command {
$this->defaultOutputFormat
)
;
+
+ // check if the php pcntl_signal functions are accessible
+ $this->php_pcntl_signal = function_exists('pcntl_signal');
+ if ($this->php_pcntl_signal) {
+ // Collect interrupts and notify the running command
+ pcntl_signal(SIGTERM, [$this, 'cancelOperation']);
+ pcntl_signal(SIGINT, [$this, 'cancelOperation']);
+ }
+
}
/**
@@ -116,4 +131,27 @@ class Base extends Command {
return $value;
}
}
+
+ /**
+ * @return bool
+ */
+ protected function hasBeenInterrupted() {
+ // return always false if pcntl_signal functions are not accessible
+ if ($this->php_pcntl_signal) {
+ pcntl_signal_dispatch();
+ return $this->interrupted;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Changes the status of the command to "interrupted" if ctrl-c has been pressed
+ *
+ * Gives a chance to the command to properly terminate what it's doing
+ */
+ private function cancelOperation() {
+ $this->interrupted = true;
+ }
+
}
diff --git a/core/js/config.php b/core/js/config.php
index 708da777ee4..aac7630a1d8 100644
--- a/core/js/config.php
+++ b/core/js/config.php
@@ -62,9 +62,17 @@ if ($defaultExpireDateEnabled) {
}
$outgoingServer2serverShareEnabled = $config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'yes';
+$countOfDataLocation = 0;
+
+$dataLocation = str_replace(OC::$SERVERROOT .'/', '', $config->getSystemValue('datadirectory', ''), $countOfDataLocation);
+if($countOfDataLocation !== 1 || !OC_User::isAdminUser(OC_User::getUser())){
+ $dataLocation = false;
+}
+
$array = array(
"oc_debug" => $config->getSystemValue('debug', false) ? 'true' : 'false',
"oc_isadmin" => OC_User::isAdminUser(OC_User::getUser()) ? 'true' : 'false',
+ "oc_dataURL" => is_string($dataLocation) ? "\"".$dataLocation."\"" : 'false',
"oc_webroot" => "\"".OC::$WEBROOT."\"",
"oc_appswebroots" => str_replace('\\/', '/', json_encode($apps_paths)), // Ugly unescape slashes waiting for better solution
"datepickerFormatDate" => json_encode($l->l('jsdate', null)),
@@ -140,7 +148,7 @@ $array = array(
'session_keepalive' => \OCP\Config::getSystemValue('session_keepalive', true),
'version' => implode('.', \OCP\Util::getVersion()),
'versionstring' => OC_Util::getVersionString(),
- 'enable_avatars' => \OC::$server->getConfig()->getSystemValue('enable_avatars', true),
+ 'enable_avatars' => \OC::$server->getConfig()->getSystemValue('enable_avatars', true) === true,
'lost_password_link'=> \OC::$server->getConfig()->getSystemValue('lost_password_link', null),
'modRewriteWorking' => (getenv('front_controller_active') === 'true'),
)
diff --git a/core/js/js.js b/core/js/js.js
index 74121fa3d80..bc8c51e40d3 100644
--- a/core/js/js.js
+++ b/core/js/js.js
@@ -1,8 +1,8 @@
/**
* Disable console output unless DEBUG mode is enabled.
* Add
- * define('DEBUG', true);
- * To the end of config/config.php to enable debug mode.
+ * 'debug' => true,
+ * To the definition of $CONFIG in config/config.php to enable debug mode.
* The undefined checks fix the broken ie8 console
*/
@@ -82,6 +82,12 @@ var OC={
webroot:oc_webroot,
appswebroots:(typeof oc_appswebroots !== 'undefined') ? oc_appswebroots:false,
+ /**
+ * Currently logged in user or null if none
+ *
+ * @type String
+ * @deprecated use {@link OC.getCurrentUser} instead
+ */
currentUser:(typeof oc_current_user!=='undefined')?oc_current_user:false,
config: window.oc_config,
appConfig: window.oc_appconfig || {},
@@ -272,6 +278,23 @@ var OC={
},
/**
+ * Returns the currently logged in user or null if there is no logged in
+ * user (public page mode)
+ *
+ * @return {OC.CurrentUser} user spec
+ * @since 9.0.0
+ */
+ getCurrentUser: function() {
+ if (_.isUndefined(this._currentUserDisplayName)) {
+ this._currentUserDisplayName = document.getElementsByTagName('head')[0].getAttribute('data-user-displayname');
+ }
+ return {
+ uid: this.currentUser,
+ displayName: this._currentUserDisplayName
+ };
+ },
+
+ /**
* get the absolute path to an image file
* if no extension is given for the image, it will automatically decide
* between .png and .svg based on what the browser supports
@@ -690,6 +713,15 @@ var OC={
};
/**
+ * Current user attributes
+ *
+ * @typedef {Object} OC.CurrentUser
+ *
+ * @property {String} uid user id
+ * @property {String} displayName display name
+ */
+
+/**
* @namespace OC.Plugins
*/
OC.Plugins = {
diff --git a/core/js/oc-backbone-webdav.js b/core/js/oc-backbone-webdav.js
index 7c32116f011..ba678a32fcf 100644
--- a/core/js/oc-backbone-webdav.js
+++ b/core/js/oc-backbone-webdav.js
@@ -76,6 +76,11 @@
* @param {Object} davProperties properties mapping
*/
function parsePropFindResult(result, davProperties) {
+ if (_.isArray(result)) {
+ return _.map(result, function(subResult) {
+ return parsePropFindResult(subResult, davProperties);
+ });
+ }
var props = {
href: result.href
};
@@ -87,7 +92,7 @@
for (var key in propStat.properties) {
var propKey = key;
- if (davProperties[key]) {
+ if (key in davProperties) {
propKey = davProperties[key];
}
props[propKey] = propStat.properties[key];
@@ -151,15 +156,10 @@
if (isSuccessStatus(response.status)) {
if (_.isFunction(options.success)) {
var propsMapping = _.invert(options.davProperties);
- var results;
+ var results = parsePropFindResult(response.body, propsMapping);
if (options.depth > 0) {
- results = _.map(response.body, function(data) {
- return parsePropFindResult(data, propsMapping);
- });
// discard root entry
results.shift();
- } else {
- results = parsePropFindResult(response.body, propsMapping);
}
options.success(results);
@@ -217,7 +217,13 @@
options.success(responseJson);
return;
}
- options.success(result.body);
+ // if multi-status, parse
+ if (result.status === 207) {
+ var propsMapping = _.invert(options.davProperties);
+ options.success(parsePropFindResult(result.body, propsMapping));
+ } else {
+ options.success(result.body);
+ }
}
});
}
@@ -249,7 +255,7 @@
* DAV transport
*/
function davSync(method, model, options) {
- var params = {type: methodMap[method]};
+ var params = {type: methodMap[method] || method};
var isCollection = (model instanceof Backbone.Collection);
if (method === 'update' && (model.usePUT || (model.collection && model.collection.usePUT))) {
diff --git a/core/js/setupchecks.js b/core/js/setupchecks.js
index 2fa119334db..bc49c61a5aa 100644
--- a/core/js/setupchecks.js
+++ b/core/js/setupchecks.js
@@ -15,7 +15,6 @@
MESSAGE_TYPE_INFO:0,
MESSAGE_TYPE_WARNING:1,
MESSAGE_TYPE_ERROR:2,
-
/**
* Check whether the WebDAV connection works.
*
@@ -97,12 +96,6 @@
type: OC.SetupChecks.MESSAGE_TYPE_WARNING
});
}
- if(!data.dataDirectoryProtected) {
- messages.push({
- msg: t('core', 'Your data directory and your files are probably accessible from the Internet. The .htaccess file is not working. We strongly suggest that you configure your web server in a way that the data directory is no longer accessible or you move the data directory outside the web server document root.'),
- type: OC.SetupChecks.MESSAGE_TYPE_ERROR
- });
- }
if(!data.isMemcacheConfigured) {
messages.push({
msg: t('core', 'No memory cache has been configured. To enhance your performance please configure a memcache if available. Further information can be found in our <a target="_blank" href="{docLink}">documentation</a>.', {docLink: data.memcacheDocs}),
@@ -194,6 +187,30 @@
return deferred.promise();
},
+ checkDataProtected: function() {
+ var deferred = $.Deferred();
+ if(oc_dataURL === false){
+ return deferred.resolve([]);
+ }
+ var afterCall = function(xhr) {
+ var messages = [];
+ if (xhr.status !== 403 && xhr.status !== 307 && xhr.status !== 301) {
+ messages.push({
+ msg: t('core', 'Your data directory and your files are probably accessible from the Internet. The .htaccess file is not working. We strongly suggest that you configure your web server in a way that the data directory is no longer accessible or you move the data directory outside the web server document root.'),
+ type: OC.SetupChecks.MESSAGE_TYPE_ERROR
+ });
+ }
+ deferred.resolve(messages);
+ };
+
+ $.ajax({
+ type: 'GET',
+ url: OC.linkTo('', oc_dataURL+'/.ocdata'),
+ complete: afterCall
+ });
+ return deferred.promise();
+ },
+
/**
* Runs check for some generic security headers on the server side
*
diff --git a/core/js/tests/specs/setupchecksSpec.js b/core/js/tests/specs/setupchecksSpec.js
index fff169ec098..59df3a58746 100644
--- a/core/js/tests/specs/setupchecksSpec.js
+++ b/core/js/tests/specs/setupchecksSpec.js
@@ -96,6 +96,49 @@ describe('OC.SetupChecks tests', function() {
});
});
+ describe('checkDataProtected', function() {
+
+ oc_dataURL = "data";
+
+ it('should return an error if data directory is not protected', function(done) {
+ var async = OC.SetupChecks.checkDataProtected();
+
+ suite.server.requests[0].respond(200);
+
+ async.done(function( data, s, x ){
+ expect(data).toEqual([
+ {
+ msg: 'Your data directory and your files are probably accessible from the Internet. The .htaccess file is not working. We strongly suggest that you configure your web server in a way that the data directory is no longer accessible or you move the data directory outside the web server document root.',
+ type: OC.SetupChecks.MESSAGE_TYPE_ERROR
+ }]);
+ done();
+ });
+ });
+
+ it('should not return an error if data directory is protected', function(done) {
+ var async = OC.SetupChecks.checkDataProtected();
+
+ suite.server.requests[0].respond(403);
+
+ async.done(function( data, s, x ){
+ expect(data).toEqual([]);
+ done();
+ });
+ });
+
+ it('should return an error if data directory is a boolean', function(done) {
+
+ oc_dataURL = false;
+
+ var async = OC.SetupChecks.checkDataProtected();
+
+ async.done(function( data, s, x ){
+ expect(data).toEqual([]);
+ done();
+ });
+ });
+ });
+
describe('checkSetup', function() {
it('should return an error if server has no internet connection', function(done) {
var async = OC.SetupChecks.checkSetup();
@@ -121,9 +164,6 @@ describe('OC.SetupChecks tests', function() {
msg: 'This server has no working Internet connection. This means that some of the features like mounting external storage, notifications about updates or installation of third-party apps will not work. Accessing files remotely and sending of notification emails might not work, either. We suggest to enable Internet connection for this server if you want to have all features.',
type: OC.SetupChecks.MESSAGE_TYPE_WARNING
}, {
- msg: 'Your data directory and your files are probably accessible from the Internet. The .htaccess file is not working. We strongly suggest that you configure your web server in a way that the data directory is no longer accessible or you move the data directory outside the web server document root.',
- type: OC.SetupChecks.MESSAGE_TYPE_ERROR
- }, {
msg: 'No memory cache has been configured. To enhance your performance please configure a memcache if available. Further information can be found in our <a target="_blank" href="https://doc.owncloud.org/server/go.php?to=admin-performance">documentation</a>.',
type: OC.SetupChecks.MESSAGE_TYPE_INFO
}]);
@@ -142,7 +182,6 @@ describe('OC.SetupChecks tests', function() {
JSON.stringify({
isUrandomAvailable: true,
serverHasInternetConnection: false,
- dataDirectoryProtected: false,
memcacheDocs: 'https://doc.owncloud.org/server/go.php?to=admin-performance',
forwardedForHeadersWorking: true,
isCorrectMemcachedPHPModuleInstalled: true,
@@ -157,10 +196,6 @@ describe('OC.SetupChecks tests', function() {
type: OC.SetupChecks.MESSAGE_TYPE_WARNING
},
{
- msg: 'Your data directory and your files are probably accessible from the Internet. The .htaccess file is not working. We strongly suggest that you configure your web server in a way that the data directory is no longer accessible or you move the data directory outside the web server document root.',
- type: OC.SetupChecks.MESSAGE_TYPE_ERROR
- },
- {
msg: 'No memory cache has been configured. To enhance your performance please configure a memcache if available. Further information can be found in our <a target="_blank" href="https://doc.owncloud.org/server/go.php?to=admin-performance">documentation</a>.',
type: OC.SetupChecks.MESSAGE_TYPE_INFO
}]);
@@ -179,7 +214,6 @@ describe('OC.SetupChecks tests', function() {
JSON.stringify({
isUrandomAvailable: true,
serverHasInternetConnection: false,
- dataDirectoryProtected: false,
isMemcacheConfigured: true,
forwardedForHeadersWorking: true,
isCorrectMemcachedPHPModuleInstalled: true,
@@ -192,11 +226,8 @@ describe('OC.SetupChecks tests', function() {
{
msg: 'This server has no working Internet connection. This means that some of the features like mounting external storage, notifications about updates or installation of third-party apps will not work. Accessing files remotely and sending of notification emails might not work, either. We suggest to enable Internet connection for this server if you want to have all features.',
type: OC.SetupChecks.MESSAGE_TYPE_WARNING
- },
- {
- msg: 'Your data directory and your files are probably accessible from the Internet. The .htaccess file is not working. We strongly suggest that you configure your web server in a way that the data directory is no longer accessible or you move the data directory outside the web server document root.',
- type: OC.SetupChecks.MESSAGE_TYPE_ERROR
- }]);
+ }
+ ]);
done();
});
});
@@ -213,7 +244,6 @@ describe('OC.SetupChecks tests', function() {
isUrandomAvailable: false,
securityDocs: 'https://docs.owncloud.org/myDocs.html',
serverHasInternetConnection: true,
- dataDirectoryProtected: true,
isMemcacheConfigured: true,
forwardedForHeadersWorking: true,
isCorrectMemcachedPHPModuleInstalled: true,
@@ -242,7 +272,6 @@ describe('OC.SetupChecks tests', function() {
isUrandomAvailable: true,
securityDocs: 'https://docs.owncloud.org/myDocs.html',
serverHasInternetConnection: true,
- dataDirectoryProtected: true,
isMemcacheConfigured: true,
forwardedForHeadersWorking: true,
isCorrectMemcachedPHPModuleInstalled: false,
@@ -270,7 +299,6 @@ describe('OC.SetupChecks tests', function() {
JSON.stringify({
isUrandomAvailable: true,
serverHasInternetConnection: true,
- dataDirectoryProtected: true,
isMemcacheConfigured: true,
forwardedForHeadersWorking: false,
reverseProxyDocs: 'https://docs.owncloud.org/foo/bar.html',
@@ -296,7 +324,7 @@ describe('OC.SetupChecks tests', function() {
{
'Content-Type': 'application/json'
},
- JSON.stringify({data: {serverHasInternetConnection: false, dataDirectoryProtected: false}})
+ JSON.stringify({data: {serverHasInternetConnection: false}})
);
async.done(function( data, s, x ){
@@ -320,7 +348,6 @@ describe('OC.SetupChecks tests', function() {
isUrandomAvailable: true,
securityDocs: 'https://docs.owncloud.org/myDocs.html',
serverHasInternetConnection: true,
- dataDirectoryProtected: true,
isMemcacheConfigured: true,
forwardedForHeadersWorking: true,
phpSupported: {eol: true, version: '5.4.0'},
@@ -484,7 +511,7 @@ describe('OC.SetupChecks tests', function() {
{
'Content-Type': 'application/json'
},
- JSON.stringify({data: {serverHasInternetConnection: false, dataDirectoryProtected: false}})
+ JSON.stringify({data: {serverHasInternetConnection: false}})
);
async.done(function( data, s, x ){
expect(data).toEqual([{
diff --git a/core/l10n/cs_CZ.js b/core/l10n/cs_CZ.js
index 33c18b3a293..8fcfef6f2b4 100644
--- a/core/l10n/cs_CZ.js
+++ b/core/l10n/cs_CZ.js
@@ -164,17 +164,20 @@ OC.L10N.register(
"change" : "změnit",
"delete" : "smazat",
"access control" : "řízení přístupu",
+ "Could not unshare" : "Nelze zrušit sdílení",
"Share details could not be loaded for this item." : "Detaily sdílení pro tuto položku nelze načíst.",
"An error occured. Please try again" : "Nastala chyba. Prosím zkuste to znovu",
"Share" : "Sdílet",
"Share with people on other ownClouds using the syntax username@example.com/owncloud" : "Sdílejte s lidmi na ownClouds použitím syntaxe username@example.com/owncloud",
"Share with users or groups …" : "Sdílet s uživateli nebo skupinami",
"Share with users, groups or remote users …" : "Sdílet s uživateli, skupinami nebo vzdálenými uživateli",
+ "Error removing share" : "Chyba při odstraňování sdílení",
"Warning" : "Varování",
"Error while sending notification" : "Chyba při odesílání upozornění",
"Non-existing tag #{tag}" : "Neexistující tag #{tag}",
"not assignable" : "nepřiřaditelný",
"invisible" : "neviditelný",
+ "({scope})" : "({scope})",
"Delete" : "Smazat",
"Rename" : "Přejmenovat",
"Global tags" : "Globální tagy",
diff --git a/core/l10n/cs_CZ.json b/core/l10n/cs_CZ.json
index f72bdd32fdc..19c22abd616 100644
--- a/core/l10n/cs_CZ.json
+++ b/core/l10n/cs_CZ.json
@@ -162,17 +162,20 @@
"change" : "změnit",
"delete" : "smazat",
"access control" : "řízení přístupu",
+ "Could not unshare" : "Nelze zrušit sdílení",
"Share details could not be loaded for this item." : "Detaily sdílení pro tuto položku nelze načíst.",
"An error occured. Please try again" : "Nastala chyba. Prosím zkuste to znovu",
"Share" : "Sdílet",
"Share with people on other ownClouds using the syntax username@example.com/owncloud" : "Sdílejte s lidmi na ownClouds použitím syntaxe username@example.com/owncloud",
"Share with users or groups …" : "Sdílet s uživateli nebo skupinami",
"Share with users, groups or remote users …" : "Sdílet s uživateli, skupinami nebo vzdálenými uživateli",
+ "Error removing share" : "Chyba při odstraňování sdílení",
"Warning" : "Varování",
"Error while sending notification" : "Chyba při odesílání upozornění",
"Non-existing tag #{tag}" : "Neexistující tag #{tag}",
"not assignable" : "nepřiřaditelný",
"invisible" : "neviditelný",
+ "({scope})" : "({scope})",
"Delete" : "Smazat",
"Rename" : "Přejmenovat",
"Global tags" : "Globální tagy",
diff --git a/core/l10n/de_DE.js b/core/l10n/de_DE.js
index 0da881a3b11..eb3cff6bef5 100644
--- a/core/l10n/de_DE.js
+++ b/core/l10n/de_DE.js
@@ -160,6 +160,7 @@ OC.L10N.register(
"Share with users, groups or remote users …" : "Mit Benutzern, Gruppen oder entfernten Benutzern teilen…",
"Warning" : "Warnung",
"Error while sending notification" : "Fehler beim Senden der Benachrichtigung",
+ "invisible" : "unsichtbar",
"Delete" : "Löschen",
"Rename" : "Umbenennen",
"The object type is not specified." : "Der Objekttyp ist nicht angegeben.",
@@ -175,6 +176,7 @@ OC.L10N.register(
"Hello {name}" : "Hallo {name}",
"_download %n file_::_download %n files_" : ["Lade %n Datei herunter","Lade %n Dateien herunter"],
"{version} is available. Get more information on how to update." : "{version} ist verfügbar. Holen Sie weitere Informationen zu Aktualisierungen ein.",
+ "The upgrade is in progress, leaving this page might interrupt the process in some environments." : "Das Upgrade läuft noch , diese Seite zu verlassen könnte das Verfahren in einigen Umgebungen unterbrechen.",
"Updating {productName} to version {version}, this may take a while." : "{productName} wird auf Version {version} aktualisiert. Das könnte eine Weile dauern.",
"An error occurred." : "Ein Fehler ist aufgetreten.",
"Please reload the page." : "Bitte laden Sie die Seite neu.",
@@ -250,6 +252,7 @@ OC.L10N.register(
"Please try again or contact your administrator." : "Bitte versuchen Sie es noch einmal oder kontaktieren Sie Ihren Administrator.",
"Log in" : "Einloggen",
"Wrong password. Reset it?" : "Falsches Passwort. Soll es zurückgesetzt werden?",
+ "Wrong password." : "Falsches Passwort.",
"Stay logged in" : "Angemeldet bleiben",
"Alternative Logins" : "Alternative Logins",
"Use the following link to reset your password: {link}" : "Benutzen Sie den folgenden Link, um Ihr Passwort zurückzusetzen: {link}",
diff --git a/core/l10n/de_DE.json b/core/l10n/de_DE.json
index 47bcc27c72b..4eec4c90246 100644
--- a/core/l10n/de_DE.json
+++ b/core/l10n/de_DE.json
@@ -158,6 +158,7 @@
"Share with users, groups or remote users …" : "Mit Benutzern, Gruppen oder entfernten Benutzern teilen…",
"Warning" : "Warnung",
"Error while sending notification" : "Fehler beim Senden der Benachrichtigung",
+ "invisible" : "unsichtbar",
"Delete" : "Löschen",
"Rename" : "Umbenennen",
"The object type is not specified." : "Der Objekttyp ist nicht angegeben.",
@@ -173,6 +174,7 @@
"Hello {name}" : "Hallo {name}",
"_download %n file_::_download %n files_" : ["Lade %n Datei herunter","Lade %n Dateien herunter"],
"{version} is available. Get more information on how to update." : "{version} ist verfügbar. Holen Sie weitere Informationen zu Aktualisierungen ein.",
+ "The upgrade is in progress, leaving this page might interrupt the process in some environments." : "Das Upgrade läuft noch , diese Seite zu verlassen könnte das Verfahren in einigen Umgebungen unterbrechen.",
"Updating {productName} to version {version}, this may take a while." : "{productName} wird auf Version {version} aktualisiert. Das könnte eine Weile dauern.",
"An error occurred." : "Ein Fehler ist aufgetreten.",
"Please reload the page." : "Bitte laden Sie die Seite neu.",
@@ -248,6 +250,7 @@
"Please try again or contact your administrator." : "Bitte versuchen Sie es noch einmal oder kontaktieren Sie Ihren Administrator.",
"Log in" : "Einloggen",
"Wrong password. Reset it?" : "Falsches Passwort. Soll es zurückgesetzt werden?",
+ "Wrong password." : "Falsches Passwort.",
"Stay logged in" : "Angemeldet bleiben",
"Alternative Logins" : "Alternative Logins",
"Use the following link to reset your password: {link}" : "Benutzen Sie den folgenden Link, um Ihr Passwort zurückzusetzen: {link}",
diff --git a/core/l10n/fi_FI.js b/core/l10n/fi_FI.js
index 43422498388..12c131f8410 100644
--- a/core/l10n/fi_FI.js
+++ b/core/l10n/fi_FI.js
@@ -164,17 +164,20 @@ OC.L10N.register(
"change" : "muuta",
"delete" : "poista",
"access control" : "Pääsyn hallinta",
+ "Could not unshare" : "Jakamisen lopettaminen epäonnistui",
"Share details could not be loaded for this item." : "Tämän kohteen jakamistietoja ei voitu ladata.",
"An error occured. Please try again" : "Tapahtui virhe. Yritä myöhemmin uudestaan",
"Share" : "Jaa",
"Share with people on other ownClouds using the syntax username@example.com/owncloud" : "Jaa toisia ownCloud-järjestelmiä käyttävien kesken käyttäen syntaksia käyttäjätunnus@esimerkki.fi/owncloud",
"Share with users or groups …" : "Jaa käyttäjien tai ryhmien kanssa…",
"Share with users, groups or remote users …" : "Jaa käyttäjien, ryhmien tai etäkäyttäjien kanssa…",
+ "Error removing share" : "Virhe jakoa poistaessa",
"Warning" : "Varoitus",
"Error while sending notification" : "Virhe ilmoitusta lähettäessä",
"Non-existing tag #{tag}" : "Ei olemassa oleva tunniste #{tag}",
"not assignable" : "ei määritettävissä",
"invisible" : "näkymätön",
+ "({scope})" : "({scope})",
"Delete" : "Poista",
"Rename" : "Nimeä uudelleen",
"Global tags" : "Yleiset tunnisteet",
diff --git a/core/l10n/fi_FI.json b/core/l10n/fi_FI.json
index 40afb85d496..89944f4e153 100644
--- a/core/l10n/fi_FI.json
+++ b/core/l10n/fi_FI.json
@@ -162,17 +162,20 @@
"change" : "muuta",
"delete" : "poista",
"access control" : "Pääsyn hallinta",
+ "Could not unshare" : "Jakamisen lopettaminen epäonnistui",
"Share details could not be loaded for this item." : "Tämän kohteen jakamistietoja ei voitu ladata.",
"An error occured. Please try again" : "Tapahtui virhe. Yritä myöhemmin uudestaan",
"Share" : "Jaa",
"Share with people on other ownClouds using the syntax username@example.com/owncloud" : "Jaa toisia ownCloud-järjestelmiä käyttävien kesken käyttäen syntaksia käyttäjätunnus@esimerkki.fi/owncloud",
"Share with users or groups …" : "Jaa käyttäjien tai ryhmien kanssa…",
"Share with users, groups or remote users …" : "Jaa käyttäjien, ryhmien tai etäkäyttäjien kanssa…",
+ "Error removing share" : "Virhe jakoa poistaessa",
"Warning" : "Varoitus",
"Error while sending notification" : "Virhe ilmoitusta lähettäessä",
"Non-existing tag #{tag}" : "Ei olemassa oleva tunniste #{tag}",
"not assignable" : "ei määritettävissä",
"invisible" : "näkymätön",
+ "({scope})" : "({scope})",
"Delete" : "Poista",
"Rename" : "Nimeä uudelleen",
"Global tags" : "Yleiset tunnisteet",
diff --git a/core/l10n/fr.js b/core/l10n/fr.js
index 5e312662719..2f1ebc3a9cb 100644
--- a/core/l10n/fr.js
+++ b/core/l10n/fr.js
@@ -86,7 +86,7 @@ OC.L10N.register(
"Oct." : "Oct.",
"Nov." : "Nov.",
"Dec." : "Déc.",
- "<a href=\"{docUrl}\">There were problems with the code integrity check. More information…</a>" : "<a href=\"{docUrl}\">Il y a eu des problèmes avec la vérification d’intégrité du code. Plus d'infos...</a>",
+ "<a href=\"{docUrl}\">There were problems with the code integrity check. More information…</a>" : "<a href=\"{docUrl}\">Il y a eu des problèmes à la vérification d’intégrité du code. Plus d'infos...</a>",
"Settings" : "Paramètres",
"Saving..." : "Enregistrement…",
"seconds ago" : "à l'instant",
@@ -120,7 +120,6 @@ OC.L10N.register(
"Your web server is not yet set up properly to allow file synchronization because the WebDAV interface seems to be broken." : "Votre serveur web n'est pas correctement configuré pour la synchronisation de fichiers : l'interface WebDAV semble ne pas fonctionner.",
"Your web server is not set up properly to resolve \"{url}\". Further information can be found in our <a target=\"_blank\" href=\"{docLink}\">documentation</a>." : "La configuration du serveur web ne permet pas d'atteindre \"{url}\". Consultez la <a target=\"_blank\" href=\"{docLink}\">documentation</a> pour avoir plus d'informations à ce sujet.",
"This server has no working Internet connection. This means that some of the features like mounting external storage, notifications about updates or installation of third-party apps will not work. Accessing files remotely and sending of notification emails might not work, either. We suggest to enable Internet connection for this server if you want to have all features." : "Ce serveur ne peut se connecter à internet. Cela signifie que certaines fonctionnalités, telles que le montage de supports de stockage distants, les notifications de mises à jour ou l'installation d'applications tierces ne fonctionneront pas. L'accès aux fichiers à distance, ainsi que les notifications par mail peuvent aussi être indisponibles. Il est recommandé d'activer la connexion internet pour ce serveur si vous souhaitez disposer de l'ensemble des fonctionnalités offertes.",
- "Your data directory and your files are probably accessible from the Internet. The .htaccess file is not working. We strongly suggest that you configure your web server in a way that the data directory is no longer accessible or you move the data directory outside the web server document root." : "Votre dossier de données et vos fichiers sont probablement accessibles depuis internet. Le fichier .htaccess ne fonctionne pas. Nous vous recommandons vivement de configurer votre serveur web de façon à ce que ce dossier de données ne soit plus accessible, ou de le déplacer hors de la racine du serveur web.",
"No memory cache has been configured. To enhance your performance please configure a memcache if available. Further information can be found in our <a target=\"_blank\" href=\"{docLink}\">documentation</a>." : "Aucun cache mémoire n'est configuré. Si possible, configurez un cache pour augmenter les performances. Consultez la <a target=\"_blank\" href=\"{docLink}\">documentation</a> pour avoir plus d'informations à ce sujet.",
"/dev/urandom is not readable by PHP which is highly discouraged for security reasons. Further information can be found in our <a target=\"_blank\" href=\"{docLink}\">documentation</a>." : "/dev/urandom n'est pas lisible par PHP, ce qui est fortement déconseillé pour des raisons de sécurité. Consultez la <a target=\"_blank\" href=\"{docLink}\">documentation</a> pour avoir plus d'informations à ce sujet.",
"Your PHP version ({version}) is no longer <a target=\"_blank\" href=\"{phpLink}\">supported by PHP</a>. We encourage you to upgrade your PHP version to take advantage of performance and security updates provided by PHP." : "Votre version de PHP ({version}) <a target=\"_blank\" href=\"{phpLink}\">n'est plus prise en charge par les créateurs de PHP</a>. Nous vous recommandons de mettre à niveau votre installation de PHP pour bénéficier de meilleures performances et des mises à jour de sécurité fournies par PHP.",
@@ -128,6 +127,7 @@ OC.L10N.register(
"Memcached is configured as distributed cache, but the wrong PHP module \"memcache\" is installed. \\OC\\Memcache\\Memcached only supports \"memcached\" and not \"memcache\". See the <a target=\"_blank\" href=\"{wikiLink}\">memcached wiki about both modules</a>." : "\"memcached\" est configuré comme cache distribué, mais le module installé est \"memcache\". \\OC\\Memcache\\Memcached ne prend en charge que \"memcached\" et non \"memcache\". <a target=\"_blank\" href=\"{wikiLink}\">Consulter le wiki de memcached à propos de ces deux modules.</a>",
"Some files have not passed the integrity check. Further information on how to resolve this issue can be found in our <a target=\"_blank\" href=\"{docLink}\">documentation</a>. (<a href=\"{codeIntegrityDownloadEndpoint}\">List of invalid files…</a> / <a href=\"{rescanEndpoint}\">Rescan…</a>)" : "Des fichiers n'ont pas passé la vérification d’intégrité. \nConsultez la <a target=\"_blank\" href=\"{docLink}\">documentation</a> pour avoir plus d'informations sur comment résoudre ce problème.\n(<a target=\"_blank\" href=\"{codeIntegrityDownloadEndpoint}\">Liste des fichiers non valides…</a> / <a href=\"{rescanEndpoint}\">Relancer…</a>)",
"Error occurred while checking server setup" : "Une erreur s'est produite lors de la vérification de la configuration du serveur",
+ "Your data directory and your files are probably accessible from the Internet. The .htaccess file is not working. We strongly suggest that you configure your web server in a way that the data directory is no longer accessible or you move the data directory outside the web server document root." : "Votre dossier de données et vos fichiers sont probablement accessibles depuis internet. Le fichier .htaccess ne fonctionne pas. Nous vous recommandons vivement de configurer votre serveur web de façon à ce que ce dossier de données ne soit plus accessible, ou de le déplacer hors de la racine du serveur web.",
"The \"{header}\" HTTP header is not configured to equal to \"{expected}\". This is a potential security or privacy risk and we recommend adjusting this setting." : "L'en-tête HTTP \"{header}\" n'est pas configurée pour être égale à \"{expected}\" créant potentiellement un risque relié à la sécurité et à la vie privée. Il est donc recommandé d'ajuster ce paramètre.",
"The \"Strict-Transport-Security\" HTTP header is not configured to least \"{seconds}\" seconds. For enhanced security we recommend enabling HSTS as described in our <a href=\"{docUrl}\">security tips</a>." : "L'en-tête HTTP \"Strict-Transport-Security\" n'est pas configurée à \"{seconds}\" secondes. Pour renforcer la sécurité nous recommandons d'activer HSTS comme décrit dans notre <a href=\"{docUrl}\">Guide pour le renforcement et la sécurité</a>.",
"You are accessing this site via HTTP. We strongly suggest you configure your server to require using HTTPS instead as described in our <a href=\"{docUrl}\">security tips</a>." : "Vous accédez à ce site via HTTP. Nous vous recommandons fortement de configurer votre serveur pour forcer l'utilisation de HTTPS, comme expliqué dans notre <a href=\"{docUrl}\">Guide pour le renforcement et la sécurité</a>.",
@@ -164,17 +164,20 @@ OC.L10N.register(
"change" : "modification",
"delete" : "suppression",
"access control" : "contrôle d'accès",
+ "Could not unshare" : "Impossible d'arrêter de partager",
"Share details could not be loaded for this item." : "Les informations de partage n'ont pu être chargées pour cet élément.",
"An error occured. Please try again" : "Une erreur est survenue. Merci de réessayer",
"Share" : "Partager",
"Share with people on other ownClouds using the syntax username@example.com/owncloud" : "Partagez avec des personnes sur d'autres ownClouds en utilisant la syntaxe utilisateur@exemple.com/owncloud",
"Share with users or groups …" : "Partager avec des utilisateurs ou groupes...",
"Share with users, groups or remote users …" : "Partager avec des utilisateurs, groupes, ou utilisateurs distants",
+ "Error removing share" : "Erreur lors de l'arrêt du partage",
"Warning" : "Attention",
"Error while sending notification" : "Erreur lors de l'envoi de la notification",
"Non-existing tag #{tag}" : "Étiquette #{tag} inexistante",
"not assignable" : "inassignable",
"invisible" : "invisible",
+ "({scope})" : "({scope})",
"Delete" : "Supprimer",
"Rename" : "Renommer",
"Global tags" : "Étiquettes globales",
diff --git a/core/l10n/fr.json b/core/l10n/fr.json
index 4b0196e5ee4..df7cd0cf3c5 100644
--- a/core/l10n/fr.json
+++ b/core/l10n/fr.json
@@ -84,7 +84,7 @@
"Oct." : "Oct.",
"Nov." : "Nov.",
"Dec." : "Déc.",
- "<a href=\"{docUrl}\">There were problems with the code integrity check. More information…</a>" : "<a href=\"{docUrl}\">Il y a eu des problèmes avec la vérification d’intégrité du code. Plus d'infos...</a>",
+ "<a href=\"{docUrl}\">There were problems with the code integrity check. More information…</a>" : "<a href=\"{docUrl}\">Il y a eu des problèmes à la vérification d’intégrité du code. Plus d'infos...</a>",
"Settings" : "Paramètres",
"Saving..." : "Enregistrement…",
"seconds ago" : "à l'instant",
@@ -118,7 +118,6 @@
"Your web server is not yet set up properly to allow file synchronization because the WebDAV interface seems to be broken." : "Votre serveur web n'est pas correctement configuré pour la synchronisation de fichiers : l'interface WebDAV semble ne pas fonctionner.",
"Your web server is not set up properly to resolve \"{url}\". Further information can be found in our <a target=\"_blank\" href=\"{docLink}\">documentation</a>." : "La configuration du serveur web ne permet pas d'atteindre \"{url}\". Consultez la <a target=\"_blank\" href=\"{docLink}\">documentation</a> pour avoir plus d'informations à ce sujet.",
"This server has no working Internet connection. This means that some of the features like mounting external storage, notifications about updates or installation of third-party apps will not work. Accessing files remotely and sending of notification emails might not work, either. We suggest to enable Internet connection for this server if you want to have all features." : "Ce serveur ne peut se connecter à internet. Cela signifie que certaines fonctionnalités, telles que le montage de supports de stockage distants, les notifications de mises à jour ou l'installation d'applications tierces ne fonctionneront pas. L'accès aux fichiers à distance, ainsi que les notifications par mail peuvent aussi être indisponibles. Il est recommandé d'activer la connexion internet pour ce serveur si vous souhaitez disposer de l'ensemble des fonctionnalités offertes.",
- "Your data directory and your files are probably accessible from the Internet. The .htaccess file is not working. We strongly suggest that you configure your web server in a way that the data directory is no longer accessible or you move the data directory outside the web server document root." : "Votre dossier de données et vos fichiers sont probablement accessibles depuis internet. Le fichier .htaccess ne fonctionne pas. Nous vous recommandons vivement de configurer votre serveur web de façon à ce que ce dossier de données ne soit plus accessible, ou de le déplacer hors de la racine du serveur web.",
"No memory cache has been configured. To enhance your performance please configure a memcache if available. Further information can be found in our <a target=\"_blank\" href=\"{docLink}\">documentation</a>." : "Aucun cache mémoire n'est configuré. Si possible, configurez un cache pour augmenter les performances. Consultez la <a target=\"_blank\" href=\"{docLink}\">documentation</a> pour avoir plus d'informations à ce sujet.",
"/dev/urandom is not readable by PHP which is highly discouraged for security reasons. Further information can be found in our <a target=\"_blank\" href=\"{docLink}\">documentation</a>." : "/dev/urandom n'est pas lisible par PHP, ce qui est fortement déconseillé pour des raisons de sécurité. Consultez la <a target=\"_blank\" href=\"{docLink}\">documentation</a> pour avoir plus d'informations à ce sujet.",
"Your PHP version ({version}) is no longer <a target=\"_blank\" href=\"{phpLink}\">supported by PHP</a>. We encourage you to upgrade your PHP version to take advantage of performance and security updates provided by PHP." : "Votre version de PHP ({version}) <a target=\"_blank\" href=\"{phpLink}\">n'est plus prise en charge par les créateurs de PHP</a>. Nous vous recommandons de mettre à niveau votre installation de PHP pour bénéficier de meilleures performances et des mises à jour de sécurité fournies par PHP.",
@@ -126,6 +125,7 @@
"Memcached is configured as distributed cache, but the wrong PHP module \"memcache\" is installed. \\OC\\Memcache\\Memcached only supports \"memcached\" and not \"memcache\". See the <a target=\"_blank\" href=\"{wikiLink}\">memcached wiki about both modules</a>." : "\"memcached\" est configuré comme cache distribué, mais le module installé est \"memcache\". \\OC\\Memcache\\Memcached ne prend en charge que \"memcached\" et non \"memcache\". <a target=\"_blank\" href=\"{wikiLink}\">Consulter le wiki de memcached à propos de ces deux modules.</a>",
"Some files have not passed the integrity check. Further information on how to resolve this issue can be found in our <a target=\"_blank\" href=\"{docLink}\">documentation</a>. (<a href=\"{codeIntegrityDownloadEndpoint}\">List of invalid files…</a> / <a href=\"{rescanEndpoint}\">Rescan…</a>)" : "Des fichiers n'ont pas passé la vérification d’intégrité. \nConsultez la <a target=\"_blank\" href=\"{docLink}\">documentation</a> pour avoir plus d'informations sur comment résoudre ce problème.\n(<a target=\"_blank\" href=\"{codeIntegrityDownloadEndpoint}\">Liste des fichiers non valides…</a> / <a href=\"{rescanEndpoint}\">Relancer…</a>)",
"Error occurred while checking server setup" : "Une erreur s'est produite lors de la vérification de la configuration du serveur",
+ "Your data directory and your files are probably accessible from the Internet. The .htaccess file is not working. We strongly suggest that you configure your web server in a way that the data directory is no longer accessible or you move the data directory outside the web server document root." : "Votre dossier de données et vos fichiers sont probablement accessibles depuis internet. Le fichier .htaccess ne fonctionne pas. Nous vous recommandons vivement de configurer votre serveur web de façon à ce que ce dossier de données ne soit plus accessible, ou de le déplacer hors de la racine du serveur web.",
"The \"{header}\" HTTP header is not configured to equal to \"{expected}\". This is a potential security or privacy risk and we recommend adjusting this setting." : "L'en-tête HTTP \"{header}\" n'est pas configurée pour être égale à \"{expected}\" créant potentiellement un risque relié à la sécurité et à la vie privée. Il est donc recommandé d'ajuster ce paramètre.",
"The \"Strict-Transport-Security\" HTTP header is not configured to least \"{seconds}\" seconds. For enhanced security we recommend enabling HSTS as described in our <a href=\"{docUrl}\">security tips</a>." : "L'en-tête HTTP \"Strict-Transport-Security\" n'est pas configurée à \"{seconds}\" secondes. Pour renforcer la sécurité nous recommandons d'activer HSTS comme décrit dans notre <a href=\"{docUrl}\">Guide pour le renforcement et la sécurité</a>.",
"You are accessing this site via HTTP. We strongly suggest you configure your server to require using HTTPS instead as described in our <a href=\"{docUrl}\">security tips</a>." : "Vous accédez à ce site via HTTP. Nous vous recommandons fortement de configurer votre serveur pour forcer l'utilisation de HTTPS, comme expliqué dans notre <a href=\"{docUrl}\">Guide pour le renforcement et la sécurité</a>.",
@@ -162,17 +162,20 @@
"change" : "modification",
"delete" : "suppression",
"access control" : "contrôle d'accès",
+ "Could not unshare" : "Impossible d'arrêter de partager",
"Share details could not be loaded for this item." : "Les informations de partage n'ont pu être chargées pour cet élément.",
"An error occured. Please try again" : "Une erreur est survenue. Merci de réessayer",
"Share" : "Partager",
"Share with people on other ownClouds using the syntax username@example.com/owncloud" : "Partagez avec des personnes sur d'autres ownClouds en utilisant la syntaxe utilisateur@exemple.com/owncloud",
"Share with users or groups …" : "Partager avec des utilisateurs ou groupes...",
"Share with users, groups or remote users …" : "Partager avec des utilisateurs, groupes, ou utilisateurs distants",
+ "Error removing share" : "Erreur lors de l'arrêt du partage",
"Warning" : "Attention",
"Error while sending notification" : "Erreur lors de l'envoi de la notification",
"Non-existing tag #{tag}" : "Étiquette #{tag} inexistante",
"not assignable" : "inassignable",
"invisible" : "invisible",
+ "({scope})" : "({scope})",
"Delete" : "Supprimer",
"Rename" : "Renommer",
"Global tags" : "Étiquettes globales",
diff --git a/core/l10n/he.js b/core/l10n/he.js
index 7d061d236b5..9a7bfeede44 100644
--- a/core/l10n/he.js
+++ b/core/l10n/he.js
@@ -119,7 +119,18 @@ OC.L10N.register(
"Strong password" : "סיסמא חזקה",
"Your web server is not yet set up properly to allow file synchronization because the WebDAV interface seems to be broken." : "שרת האינטרנט שלך אינו מוגדר כהלכה לאפשר סנכרון כיוון שממשק ה־WebDAV כנראה שבור.",
"Your web server is not set up properly to resolve \"{url}\". Further information can be found in our <a target=\"_blank\" href=\"{docLink}\">documentation</a>." : "שרת האינטרנט שלך לא מוגדר כהלכה לפתור \"{url}\". מידע נוסף קיים <a target=\"_blank\" href=\"{docLink}\">במסמכים</a> שלנו.",
+ "This server has no working Internet connection. This means that some of the features like mounting external storage, notifications about updates or installation of third-party apps will not work. Accessing files remotely and sending of notification emails might not work, either. We suggest to enable Internet connection for this server if you want to have all features." : "לשרת זה אין חיבור אינטרנט פעיל. לפיכך חלק מהתכונות כגון עגינת אחסון חיצוני, הודעות על עדכונים או התקנת יישומי צד שלישי לא יעבדו. ייתכן ולא יעבדו גם כניסה לקבצים מבחוץ ושליחת הודעות דואר אלקטרוני. אנו ממליצים לאפשר חיבור אינטרנט לשרת זה אם ברצונך שיהיו לך את כל התכונות.",
+ "Your data directory and your files are probably accessible from the Internet. The .htaccess file is not working. We strongly suggest that you configure your web server in a way that the data directory is no longer accessible or you move the data directory outside the web server document root." : "כנראה וניתן לגשת אל תיקיית data והקבצים שלך מהאינטרנט. קובץ .htaccess אינו עובד. אנו ממליצים בכל תוקף שתגדיר את השרת בצורה כזאת שלא ניתן יהיה לגשת לתיקיית ה- data או להעביר את תיקיית ה- dta מחוץ לנתיב המסמכים של שרת האינטרנט.",
+ "No memory cache has been configured. To enhance your performance please configure a memcache if available. Further information can be found in our <a target=\"_blank\" href=\"{docLink}\">documentation</a>." : "לא הוגדר memory cache. על מנת לשפר את הביצועים יש להגדיר memcache אם קיים. מידע נוסף ניתן לצפות ב- <a target=\"_blank\" href=\"{docLink}\">מסמכי התיעוד</a>.",
+ "/dev/urandom is not readable by PHP which is highly discouraged for security reasons. Further information can be found in our <a target=\"_blank\" href=\"{docLink}\">documentation</a>." : "/dev/urandom אינו ניתן לקריאה על ידי PHP אשר אינו מומלץ בשל סיבות אבטחה. מידע נוסף ניתן לראות ב- <a target=\"_blank\" href=\"{docLink}\">מסמכי התיעוד</a>.",
+ "Your PHP version ({version}) is no longer <a target=\"_blank\" href=\"{phpLink}\">supported by PHP</a>. We encourage you to upgrade your PHP version to take advantage of performance and security updates provided by PHP." : "גרסת ה- PHP שלך ({version}) אינה <a target=\"_blank\" href=\"{phpLink}\">נתמכת כבר על ידי PHP</a>. אנו ממליצים לשדרג את גרסת ה- PHP ולהנות מהיתרון של עדכוני האבטחה והביצועים שמספק ה- PHP.",
+ "The reverse proxy headers configuration is incorrect, or you are accessing ownCloud from a trusted proxy. If you are not accessing ownCloud from a trusted proxy, this is a security issue and can allow an attacker to spoof their IP address as visible to ownCloud. Further information can be found in our <a target=\"_blank\" href=\"{docLink}\">documentation</a>." : "תצורת כותרות פרוקסי ההפוכה אינה נכונה, או שהגישה ל- ownCloud מתבצעת מ- proxy אמין. אם הגישה ל- ownCloud אינה מ- proxy אמין, מדובר בבעיית אבטחה שמאפשרת לתוקף לזיין את כתובת ה- IP כגלויה ל- ownCloud. מידע נוסף ניתן למצוא ב- <a target=\"_blank\" href=\"{docLink}\">מסמכי התיעוד</a> שלנו.",
+ "Memcached is configured as distributed cache, but the wrong PHP module \"memcache\" is installed. \\OC\\Memcache\\Memcached only supports \"memcached\" and not \"memcache\". See the <a target=\"_blank\" href=\"{wikiLink}\">memcached wiki about both modules</a>." : "Memcached מוגדר כמטמון מופץ, אבל מותקן מודול PHP לא נכון \"memcache\". רק \\OC\\Memcache\\Memcached תומך ב- \"memcached\" אבל לא ב- \"memcache\". ניתן לצפות ב- <a target=\"_blank\" href=\"{wikiLink}\">memcached wiki בנושא שני המודולים</a>.",
+ "Some files have not passed the integrity check. Further information on how to resolve this issue can be found in our <a target=\"_blank\" href=\"{docLink}\">documentation</a>. (<a href=\"{codeIntegrityDownloadEndpoint}\">List of invalid files…</a> / <a href=\"{rescanEndpoint}\">Rescan…</a>)" : "חלק מהקבצים לא עברו את בדיקת התקינות. מידע נוסף איך לפתור את הבעיה ניתן למצוא ב- to resolve this issue can be found in our <a target=\"_blank\" href=\"{docLink}\">מסמכי התיעוד</a> שלנו. (<a href=\"{codeIntegrityDownloadEndpoint}\">רשימה של קבצים לא תקינים…</a> / <a href=\"{rescanEndpoint}\">סריקה מחדש…</a>)",
"Error occurred while checking server setup" : "שגיאה אירעה בזמן בדיקת התקנת השרת",
+ "The \"{header}\" HTTP header is not configured to equal to \"{expected}\". This is a potential security or privacy risk and we recommend adjusting this setting." : "כותרת ה- HTTP \"{header}\" אינה מוגדרת להיות שווה ל- \"{expected}\". הדבר מהווה פוטנציאל סיכון אבטחה או פגיעה בפרטיות ואנו ממליצים לתקן את הגדרה זו.",
+ "The \"Strict-Transport-Security\" HTTP header is not configured to least \"{seconds}\" seconds. For enhanced security we recommend enabling HSTS as described in our <a href=\"{docUrl}\">security tips</a>." : "כותרת HTTP \"Strict-Transport-Security\" אינה מוגדרת לפחות \"{seconds}\" שניות. להגברת האבטחה אנו ממליצים לאפשר HSTS כפי שמוסבר ב- <a href=\"{docUrl}\">טיפים לאבטחה</a> שלנו.",
+ "You are accessing this site via HTTP. We strongly suggest you configure your server to require using HTTPS instead as described in our <a href=\"{docUrl}\">security tips</a>." : "הנך נכנס לאתר באמצעות פרוטוקול HTTP. אנו ממליצים מאוד להגדיר את השרת לעבוד עם פרוטוקול HTTPS במקום כפי שמוסבר ב- <a href=\"{docUrl}\">טיפים לאבטחה</a> שלנו.",
"Shared" : "שותף",
"Shared with {recipients}" : "שיתוף עם {recipients}",
"Error" : "שגיאה",
@@ -153,16 +164,20 @@ OC.L10N.register(
"change" : "שינוי",
"delete" : "מחיקה",
"access control" : "בקרת גישה",
+ "Could not unshare" : "לא ניתן לבטל שיתוף",
"Share details could not be loaded for this item." : "לא ניתן היה לטעון מידע שיתוף לפריט זה",
"An error occured. Please try again" : "אירעה שגיאה. יש לנסות בבקשה שוב",
"Share" : "שתף",
"Share with people on other ownClouds using the syntax username@example.com/owncloud" : "ניתן לשתף עם אנשים אחרים המשתמשים ב- ownClouds בעזרת הפורמט הבא username@example.com/owncloud",
"Share with users or groups …" : "שיתוף עם משתמשים או קבוצות...",
"Share with users, groups or remote users …" : "שיתוף עם משתמשים, קבוצות או משתמשים חצוניים...",
+ "Error removing share" : "שגיאה בזמן הסרת שיתוף",
"Warning" : "אזהרה",
"Error while sending notification" : "שגיאה בזמן שליחת הודעה",
"Non-existing tag #{tag}" : "תגית לא קיימת #{tag}",
+ "not assignable" : "לא ניתן להקצאה",
"invisible" : "בלתי גלוי",
+ "({scope})" : "({scope})",
"Delete" : "מחיקה",
"Rename" : "שינוי שם",
"Global tags" : "תגיות גלובליות",
@@ -179,6 +194,8 @@ OC.L10N.register(
"Hello {name}" : "שלום {name}",
"_download %n file_::_download %n files_" : ["הורד %n קובץ","הורדו %n קבצים"],
"{version} is available. Get more information on how to update." : "{version} זמינה. ניתן לקבל מידע נוסף על איך לעדכן.",
+ "The upgrade is in progress, leaving this page might interrupt the process in some environments." : "מתבצע עכשיו שדרוג, מעבר מדף זה עלול לפגוע בתהליך בסביבות הפעלה מסויימות.",
+ "Updating {productName} to version {version}, this may take a while." : "מעדכן {productName} לגרסה {version}, זה יקח זמן מה.",
"An error occurred." : "אירעה שגיאה.",
"Please reload the page." : "יש להעלות מחדש דף זה.",
"The update was unsuccessful. " : "העדכון בוצע בהצלחה.",
@@ -186,6 +203,7 @@ OC.L10N.register(
"The update was successful. Redirecting you to ownCloud now." : "תהליך העדכון הסתיים בהצלחה. עכשיו מנתב אותך אל ownCloud.",
"Searching other places" : "מחפש במקומות אחרים",
"No search results in other folders" : "אין תוצאות חיפוש בתיקיות אחרות",
+ "_{count} search result in another folder_::_{count} search results in other folders_" : ["{count} תוצאת חיפוש בתיקייה אחרות","{count} תוצאות חיפוש בתיקיות אחרות"],
"Personal" : "אישי",
"Users" : "משתמשים",
"Apps" : "יישומים",
@@ -206,6 +224,9 @@ OC.L10N.register(
"The share will expire on %s." : "השיתוף יפוג תוקף ב- %s.",
"Cheers!" : "לחיים!",
"Internal Server Error" : "שגיאה פנימית בשרת",
+ "The server encountered an internal error and was unable to complete your request." : "השרת נתקל בשגיאה פנימית ולא הצליח לסיים את הבקשה שלך.",
+ "Please contact the server administrator if this error reappears multiple times, please include the technical details below in your report." : "יש ליצור קשר עם מנהל השרת אם שגיאה זו חוזרת מספר פעמים, יש לצרף לדיווח את הפרטים הטכניים למטה.",
+ "More details can be found in the server log." : "פרטים נוספים ניתן למצוא בלוג של הרשת.",
"Technical details" : "פרטים טכנים",
"Remote Address: %s" : "כתובת מרוחקת: %s",
"Request ID: %s" : "מספר זיהוי מבוקש: %s",
@@ -217,24 +238,38 @@ OC.L10N.register(
"Trace" : "עקבות",
"Security warning" : "אזהרת אבטחה",
"Your data directory and files are probably accessible from the internet because the .htaccess file does not work." : "תיקיית וקבצי המידע שלך כנראה נגישים מהאינטרנט מכיוון שקובץ ה.htaccess לא עובד.",
+ "For information how to properly configure your server, please see the <a href=\"%s\" target=\"_blank\">documentation</a>." : "למידע איך להגדיר את השרת שלך, ניתן לראות ב- <a href=\"%s\" target=\"_blank\">מסמכי התיעוד</a>.",
"Create an <strong>admin account</strong>" : "יצירת <strong>חשבון מנהל</strong>",
"Username" : "שם משתמש",
"Storage & database" : "אחסון ומסד נתונים",
"Data folder" : "תיקיית נתונים",
"Configure the database" : "הגדרת מסד הנתונים",
"Only %s is available." : "רק %s זמין.",
+ "Install and activate additional PHP modules to choose other database types." : "לבחירת סוגים אחרים של מסדי נתונים יש להתקין ולהפעיל מודולי PHP נוספים.",
+ "For more details check out the documentation." : "למידע נוסף יש לבדוק במסמכי התיעוד.",
"Database user" : "שם משתמש במסד הנתונים",
"Database password" : "ססמת מסד הנתונים",
"Database name" : "שם מסד הנתונים",
"Database tablespace" : "מרחב הכתובות של מסד הנתונים",
"Database host" : "שרת בסיס נתונים",
+ "Performance warning" : "אזהרת ביצועים",
+ "SQLite will be used as database." : "יעשה שימוש ב- SQLite כמסד נתונים.",
+ "For larger installations we recommend to choose a different database backend." : "להתקנות נרחבות אנו ממליצים לבחור מסד נתונים אחר לצד השרת.",
"Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "במיוחד כאשר משתמשים במחשב שולחני לסנכרון קבצים השימוש ב SQLite אינו מומלץ.",
"Finish setup" : "סיום התקנה",
"Finishing …" : "מסיים...",
"Need help?" : "עזרה נזקקת?",
+ "See the documentation" : "יש לצפות במסמכי התיעוד",
+ "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>" : "הי לך,<br><br>כדאי לך לדעת ש- %s משתף/פת <strong>%s</strong> אתך.<br><a href=\"%s\">לצפייה!</a><br><br>",
+ "This application requires JavaScript for correct operation. Please {linkstart}enable JavaScript{linkend} and reload the page." : "יישום זה דורש JavaScript לפעולה נכונה. יש {linkstart}לאפשר JavaScript{linkend} ולטעון את העמוד מחדש.",
"Log out" : "התנתקות",
"Search" : "חיפוש",
+ "Server side authentication failed!" : "אימות לצד שרת נכשל!",
+ "Please contact your administrator." : "יש ליצור קשר עם המנהל.",
+ "An internal error occured." : "שגיאה פנימית אירעה.",
+ "Please try again or contact your administrator." : "יש לנסות שוב ליצור קשר עם המנהל שלך.",
"Log in" : "כניסה",
+ "Wrong password. Reset it?" : "סיסמא שגוייה. האם לאפס אותה?",
"Wrong password." : "סיסמא שגוייה.",
"Stay logged in" : "השאר מחובר",
"Alternative Logins" : "כניסות אלטרנטיביות",
@@ -242,7 +277,23 @@ OC.L10N.register(
"New password" : "ססמה חדשה",
"New Password" : "סיסמא חדשה",
"Reset password" : "איפוס ססמה",
+ "This ownCloud instance is currently in single user mode." : "הפעלת ownCloud זו עובדת כרגע במצב של משתמש יחיד.",
+ "This means only administrators can use the instance." : "לפיכך רק מנהלים יכולים להשתמש בהפעלה זו.",
+ "Contact your system administrator if this message persists or appeared unexpectedly." : "יש ליצור קשר עם מנהל המערכת אם הודעה שו נמשכת או מופיעה באופן בלתי צפוי. ",
"Thank you for your patience." : "תודה על הסבלנות.",
- "Start update" : "התחלת עדכון"
+ "You are accessing the server from an untrusted domain." : "נכנסת לשרת משם מתחם / דומיין שאינו מהימן.",
+ "Please contact your administrator. If you are an administrator of this instance, configure the \"trusted_domains\" setting in config/config.php. An example configuration is provided in config/config.sample.php." : "יש ליצור קשר עם המנהל שלך. אם הנך המנהל של הפעלה זו, יש להגדיר את הגדרות ה- \"trusted_domains\" של config/config.php. דוגמת תצורה ניתן לראות ב- config/config.sample.php.",
+ "Depending on your configuration, as an administrator you might also be able to use the button below to trust this domain." : "בהתאם לתצורה שלך, כמנהל יתכן ותוכל להשתמש בכפתור מטה להפיכת שם המתחם / דומיין למהימן.",
+ "Add \"%s\" as trusted domain" : "הוספת \"%s\" כשם מתחם / דומיין מהימן",
+ "App update required" : "נדרש עדכון יישום",
+ "%s will be updated to version %s" : "%s יעודכן לגרסה %s",
+ "These apps will be updated:" : "יישומים אלו יעודכנו:",
+ "These incompatible apps will be disabled:" : "יישומים לא תואמים ינוטרלו:",
+ "The theme %s has been disabled." : "ערכת הנושא %s נוטרלה.",
+ "Please make sure that the database, the config folder and the data folder have been backed up before proceeding." : "יש לוודא שמסד הנתונים, תיקיית config ותיקיית data גובו לפני ההמשך.",
+ "Start update" : "התחלת עדכון",
+ "To avoid timeouts with larger installations, you can instead run the following command from your installation directory:" : "למניעת פסקי זמן בהתקנות גדולות, ניתן במקום להריץ את הפקודה הבאה בתיקיית ההתקנה שלך:",
+ "This %s instance is currently in maintenance mode, which may take a while." : "הפעלה %s זו כרגע במצב אחזקה, שתמשך זמן מה.",
+ "This page will refresh itself when the %s instance is available again." : "עמוד זה ירענן את עצמו כשהפעלת %s תהיה זמינה שוב."
},
"nplurals=2; plural=(n != 1);");
diff --git a/core/l10n/he.json b/core/l10n/he.json
index 64492bcbcac..9e25f9895d1 100644
--- a/core/l10n/he.json
+++ b/core/l10n/he.json
@@ -117,7 +117,18 @@
"Strong password" : "סיסמא חזקה",
"Your web server is not yet set up properly to allow file synchronization because the WebDAV interface seems to be broken." : "שרת האינטרנט שלך אינו מוגדר כהלכה לאפשר סנכרון כיוון שממשק ה־WebDAV כנראה שבור.",
"Your web server is not set up properly to resolve \"{url}\". Further information can be found in our <a target=\"_blank\" href=\"{docLink}\">documentation</a>." : "שרת האינטרנט שלך לא מוגדר כהלכה לפתור \"{url}\". מידע נוסף קיים <a target=\"_blank\" href=\"{docLink}\">במסמכים</a> שלנו.",
+ "This server has no working Internet connection. This means that some of the features like mounting external storage, notifications about updates or installation of third-party apps will not work. Accessing files remotely and sending of notification emails might not work, either. We suggest to enable Internet connection for this server if you want to have all features." : "לשרת זה אין חיבור אינטרנט פעיל. לפיכך חלק מהתכונות כגון עגינת אחסון חיצוני, הודעות על עדכונים או התקנת יישומי צד שלישי לא יעבדו. ייתכן ולא יעבדו גם כניסה לקבצים מבחוץ ושליחת הודעות דואר אלקטרוני. אנו ממליצים לאפשר חיבור אינטרנט לשרת זה אם ברצונך שיהיו לך את כל התכונות.",
+ "Your data directory and your files are probably accessible from the Internet. The .htaccess file is not working. We strongly suggest that you configure your web server in a way that the data directory is no longer accessible or you move the data directory outside the web server document root." : "כנראה וניתן לגשת אל תיקיית data והקבצים שלך מהאינטרנט. קובץ .htaccess אינו עובד. אנו ממליצים בכל תוקף שתגדיר את השרת בצורה כזאת שלא ניתן יהיה לגשת לתיקיית ה- data או להעביר את תיקיית ה- dta מחוץ לנתיב המסמכים של שרת האינטרנט.",
+ "No memory cache has been configured. To enhance your performance please configure a memcache if available. Further information can be found in our <a target=\"_blank\" href=\"{docLink}\">documentation</a>." : "לא הוגדר memory cache. על מנת לשפר את הביצועים יש להגדיר memcache אם קיים. מידע נוסף ניתן לצפות ב- <a target=\"_blank\" href=\"{docLink}\">מסמכי התיעוד</a>.",
+ "/dev/urandom is not readable by PHP which is highly discouraged for security reasons. Further information can be found in our <a target=\"_blank\" href=\"{docLink}\">documentation</a>." : "/dev/urandom אינו ניתן לקריאה על ידי PHP אשר אינו מומלץ בשל סיבות אבטחה. מידע נוסף ניתן לראות ב- <a target=\"_blank\" href=\"{docLink}\">מסמכי התיעוד</a>.",
+ "Your PHP version ({version}) is no longer <a target=\"_blank\" href=\"{phpLink}\">supported by PHP</a>. We encourage you to upgrade your PHP version to take advantage of performance and security updates provided by PHP." : "גרסת ה- PHP שלך ({version}) אינה <a target=\"_blank\" href=\"{phpLink}\">נתמכת כבר על ידי PHP</a>. אנו ממליצים לשדרג את גרסת ה- PHP ולהנות מהיתרון של עדכוני האבטחה והביצועים שמספק ה- PHP.",
+ "The reverse proxy headers configuration is incorrect, or you are accessing ownCloud from a trusted proxy. If you are not accessing ownCloud from a trusted proxy, this is a security issue and can allow an attacker to spoof their IP address as visible to ownCloud. Further information can be found in our <a target=\"_blank\" href=\"{docLink}\">documentation</a>." : "תצורת כותרות פרוקסי ההפוכה אינה נכונה, או שהגישה ל- ownCloud מתבצעת מ- proxy אמין. אם הגישה ל- ownCloud אינה מ- proxy אמין, מדובר בבעיית אבטחה שמאפשרת לתוקף לזיין את כתובת ה- IP כגלויה ל- ownCloud. מידע נוסף ניתן למצוא ב- <a target=\"_blank\" href=\"{docLink}\">מסמכי התיעוד</a> שלנו.",
+ "Memcached is configured as distributed cache, but the wrong PHP module \"memcache\" is installed. \\OC\\Memcache\\Memcached only supports \"memcached\" and not \"memcache\". See the <a target=\"_blank\" href=\"{wikiLink}\">memcached wiki about both modules</a>." : "Memcached מוגדר כמטמון מופץ, אבל מותקן מודול PHP לא נכון \"memcache\". רק \\OC\\Memcache\\Memcached תומך ב- \"memcached\" אבל לא ב- \"memcache\". ניתן לצפות ב- <a target=\"_blank\" href=\"{wikiLink}\">memcached wiki בנושא שני המודולים</a>.",
+ "Some files have not passed the integrity check. Further information on how to resolve this issue can be found in our <a target=\"_blank\" href=\"{docLink}\">documentation</a>. (<a href=\"{codeIntegrityDownloadEndpoint}\">List of invalid files…</a> / <a href=\"{rescanEndpoint}\">Rescan…</a>)" : "חלק מהקבצים לא עברו את בדיקת התקינות. מידע נוסף איך לפתור את הבעיה ניתן למצוא ב- to resolve this issue can be found in our <a target=\"_blank\" href=\"{docLink}\">מסמכי התיעוד</a> שלנו. (<a href=\"{codeIntegrityDownloadEndpoint}\">רשימה של קבצים לא תקינים…</a> / <a href=\"{rescanEndpoint}\">סריקה מחדש…</a>)",
"Error occurred while checking server setup" : "שגיאה אירעה בזמן בדיקת התקנת השרת",
+ "The \"{header}\" HTTP header is not configured to equal to \"{expected}\". This is a potential security or privacy risk and we recommend adjusting this setting." : "כותרת ה- HTTP \"{header}\" אינה מוגדרת להיות שווה ל- \"{expected}\". הדבר מהווה פוטנציאל סיכון אבטחה או פגיעה בפרטיות ואנו ממליצים לתקן את הגדרה זו.",
+ "The \"Strict-Transport-Security\" HTTP header is not configured to least \"{seconds}\" seconds. For enhanced security we recommend enabling HSTS as described in our <a href=\"{docUrl}\">security tips</a>." : "כותרת HTTP \"Strict-Transport-Security\" אינה מוגדרת לפחות \"{seconds}\" שניות. להגברת האבטחה אנו ממליצים לאפשר HSTS כפי שמוסבר ב- <a href=\"{docUrl}\">טיפים לאבטחה</a> שלנו.",
+ "You are accessing this site via HTTP. We strongly suggest you configure your server to require using HTTPS instead as described in our <a href=\"{docUrl}\">security tips</a>." : "הנך נכנס לאתר באמצעות פרוטוקול HTTP. אנו ממליצים מאוד להגדיר את השרת לעבוד עם פרוטוקול HTTPS במקום כפי שמוסבר ב- <a href=\"{docUrl}\">טיפים לאבטחה</a> שלנו.",
"Shared" : "שותף",
"Shared with {recipients}" : "שיתוף עם {recipients}",
"Error" : "שגיאה",
@@ -151,16 +162,20 @@
"change" : "שינוי",
"delete" : "מחיקה",
"access control" : "בקרת גישה",
+ "Could not unshare" : "לא ניתן לבטל שיתוף",
"Share details could not be loaded for this item." : "לא ניתן היה לטעון מידע שיתוף לפריט זה",
"An error occured. Please try again" : "אירעה שגיאה. יש לנסות בבקשה שוב",
"Share" : "שתף",
"Share with people on other ownClouds using the syntax username@example.com/owncloud" : "ניתן לשתף עם אנשים אחרים המשתמשים ב- ownClouds בעזרת הפורמט הבא username@example.com/owncloud",
"Share with users or groups …" : "שיתוף עם משתמשים או קבוצות...",
"Share with users, groups or remote users …" : "שיתוף עם משתמשים, קבוצות או משתמשים חצוניים...",
+ "Error removing share" : "שגיאה בזמן הסרת שיתוף",
"Warning" : "אזהרה",
"Error while sending notification" : "שגיאה בזמן שליחת הודעה",
"Non-existing tag #{tag}" : "תגית לא קיימת #{tag}",
+ "not assignable" : "לא ניתן להקצאה",
"invisible" : "בלתי גלוי",
+ "({scope})" : "({scope})",
"Delete" : "מחיקה",
"Rename" : "שינוי שם",
"Global tags" : "תגיות גלובליות",
@@ -177,6 +192,8 @@
"Hello {name}" : "שלום {name}",
"_download %n file_::_download %n files_" : ["הורד %n קובץ","הורדו %n קבצים"],
"{version} is available. Get more information on how to update." : "{version} זמינה. ניתן לקבל מידע נוסף על איך לעדכן.",
+ "The upgrade is in progress, leaving this page might interrupt the process in some environments." : "מתבצע עכשיו שדרוג, מעבר מדף זה עלול לפגוע בתהליך בסביבות הפעלה מסויימות.",
+ "Updating {productName} to version {version}, this may take a while." : "מעדכן {productName} לגרסה {version}, זה יקח זמן מה.",
"An error occurred." : "אירעה שגיאה.",
"Please reload the page." : "יש להעלות מחדש דף זה.",
"The update was unsuccessful. " : "העדכון בוצע בהצלחה.",
@@ -184,6 +201,7 @@
"The update was successful. Redirecting you to ownCloud now." : "תהליך העדכון הסתיים בהצלחה. עכשיו מנתב אותך אל ownCloud.",
"Searching other places" : "מחפש במקומות אחרים",
"No search results in other folders" : "אין תוצאות חיפוש בתיקיות אחרות",
+ "_{count} search result in another folder_::_{count} search results in other folders_" : ["{count} תוצאת חיפוש בתיקייה אחרות","{count} תוצאות חיפוש בתיקיות אחרות"],
"Personal" : "אישי",
"Users" : "משתמשים",
"Apps" : "יישומים",
@@ -204,6 +222,9 @@
"The share will expire on %s." : "השיתוף יפוג תוקף ב- %s.",
"Cheers!" : "לחיים!",
"Internal Server Error" : "שגיאה פנימית בשרת",
+ "The server encountered an internal error and was unable to complete your request." : "השרת נתקל בשגיאה פנימית ולא הצליח לסיים את הבקשה שלך.",
+ "Please contact the server administrator if this error reappears multiple times, please include the technical details below in your report." : "יש ליצור קשר עם מנהל השרת אם שגיאה זו חוזרת מספר פעמים, יש לצרף לדיווח את הפרטים הטכניים למטה.",
+ "More details can be found in the server log." : "פרטים נוספים ניתן למצוא בלוג של הרשת.",
"Technical details" : "פרטים טכנים",
"Remote Address: %s" : "כתובת מרוחקת: %s",
"Request ID: %s" : "מספר זיהוי מבוקש: %s",
@@ -215,24 +236,38 @@
"Trace" : "עקבות",
"Security warning" : "אזהרת אבטחה",
"Your data directory and files are probably accessible from the internet because the .htaccess file does not work." : "תיקיית וקבצי המידע שלך כנראה נגישים מהאינטרנט מכיוון שקובץ ה.htaccess לא עובד.",
+ "For information how to properly configure your server, please see the <a href=\"%s\" target=\"_blank\">documentation</a>." : "למידע איך להגדיר את השרת שלך, ניתן לראות ב- <a href=\"%s\" target=\"_blank\">מסמכי התיעוד</a>.",
"Create an <strong>admin account</strong>" : "יצירת <strong>חשבון מנהל</strong>",
"Username" : "שם משתמש",
"Storage & database" : "אחסון ומסד נתונים",
"Data folder" : "תיקיית נתונים",
"Configure the database" : "הגדרת מסד הנתונים",
"Only %s is available." : "רק %s זמין.",
+ "Install and activate additional PHP modules to choose other database types." : "לבחירת סוגים אחרים של מסדי נתונים יש להתקין ולהפעיל מודולי PHP נוספים.",
+ "For more details check out the documentation." : "למידע נוסף יש לבדוק במסמכי התיעוד.",
"Database user" : "שם משתמש במסד הנתונים",
"Database password" : "ססמת מסד הנתונים",
"Database name" : "שם מסד הנתונים",
"Database tablespace" : "מרחב הכתובות של מסד הנתונים",
"Database host" : "שרת בסיס נתונים",
+ "Performance warning" : "אזהרת ביצועים",
+ "SQLite will be used as database." : "יעשה שימוש ב- SQLite כמסד נתונים.",
+ "For larger installations we recommend to choose a different database backend." : "להתקנות נרחבות אנו ממליצים לבחור מסד נתונים אחר לצד השרת.",
"Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "במיוחד כאשר משתמשים במחשב שולחני לסנכרון קבצים השימוש ב SQLite אינו מומלץ.",
"Finish setup" : "סיום התקנה",
"Finishing …" : "מסיים...",
"Need help?" : "עזרה נזקקת?",
+ "See the documentation" : "יש לצפות במסמכי התיעוד",
+ "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>" : "הי לך,<br><br>כדאי לך לדעת ש- %s משתף/פת <strong>%s</strong> אתך.<br><a href=\"%s\">לצפייה!</a><br><br>",
+ "This application requires JavaScript for correct operation. Please {linkstart}enable JavaScript{linkend} and reload the page." : "יישום זה דורש JavaScript לפעולה נכונה. יש {linkstart}לאפשר JavaScript{linkend} ולטעון את העמוד מחדש.",
"Log out" : "התנתקות",
"Search" : "חיפוש",
+ "Server side authentication failed!" : "אימות לצד שרת נכשל!",
+ "Please contact your administrator." : "יש ליצור קשר עם המנהל.",
+ "An internal error occured." : "שגיאה פנימית אירעה.",
+ "Please try again or contact your administrator." : "יש לנסות שוב ליצור קשר עם המנהל שלך.",
"Log in" : "כניסה",
+ "Wrong password. Reset it?" : "סיסמא שגוייה. האם לאפס אותה?",
"Wrong password." : "סיסמא שגוייה.",
"Stay logged in" : "השאר מחובר",
"Alternative Logins" : "כניסות אלטרנטיביות",
@@ -240,7 +275,23 @@
"New password" : "ססמה חדשה",
"New Password" : "סיסמא חדשה",
"Reset password" : "איפוס ססמה",
+ "This ownCloud instance is currently in single user mode." : "הפעלת ownCloud זו עובדת כרגע במצב של משתמש יחיד.",
+ "This means only administrators can use the instance." : "לפיכך רק מנהלים יכולים להשתמש בהפעלה זו.",
+ "Contact your system administrator if this message persists or appeared unexpectedly." : "יש ליצור קשר עם מנהל המערכת אם הודעה שו נמשכת או מופיעה באופן בלתי צפוי. ",
"Thank you for your patience." : "תודה על הסבלנות.",
- "Start update" : "התחלת עדכון"
+ "You are accessing the server from an untrusted domain." : "נכנסת לשרת משם מתחם / דומיין שאינו מהימן.",
+ "Please contact your administrator. If you are an administrator of this instance, configure the \"trusted_domains\" setting in config/config.php. An example configuration is provided in config/config.sample.php." : "יש ליצור קשר עם המנהל שלך. אם הנך המנהל של הפעלה זו, יש להגדיר את הגדרות ה- \"trusted_domains\" של config/config.php. דוגמת תצורה ניתן לראות ב- config/config.sample.php.",
+ "Depending on your configuration, as an administrator you might also be able to use the button below to trust this domain." : "בהתאם לתצורה שלך, כמנהל יתכן ותוכל להשתמש בכפתור מטה להפיכת שם המתחם / דומיין למהימן.",
+ "Add \"%s\" as trusted domain" : "הוספת \"%s\" כשם מתחם / דומיין מהימן",
+ "App update required" : "נדרש עדכון יישום",
+ "%s will be updated to version %s" : "%s יעודכן לגרסה %s",
+ "These apps will be updated:" : "יישומים אלו יעודכנו:",
+ "These incompatible apps will be disabled:" : "יישומים לא תואמים ינוטרלו:",
+ "The theme %s has been disabled." : "ערכת הנושא %s נוטרלה.",
+ "Please make sure that the database, the config folder and the data folder have been backed up before proceeding." : "יש לוודא שמסד הנתונים, תיקיית config ותיקיית data גובו לפני ההמשך.",
+ "Start update" : "התחלת עדכון",
+ "To avoid timeouts with larger installations, you can instead run the following command from your installation directory:" : "למניעת פסקי זמן בהתקנות גדולות, ניתן במקום להריץ את הפקודה הבאה בתיקיית ההתקנה שלך:",
+ "This %s instance is currently in maintenance mode, which may take a while." : "הפעלה %s זו כרגע במצב אחזקה, שתמשך זמן מה.",
+ "This page will refresh itself when the %s instance is available again." : "עמוד זה ירענן את עצמו כשהפעלת %s תהיה זמינה שוב."
},"pluralForm" :"nplurals=2; plural=(n != 1);"
} \ No newline at end of file
diff --git a/core/l10n/it.js b/core/l10n/it.js
index 05bb1dd31c1..d23609d57ec 100644
--- a/core/l10n/it.js
+++ b/core/l10n/it.js
@@ -164,17 +164,20 @@ OC.L10N.register(
"change" : "cambia",
"delete" : "elimina",
"access control" : "controllo d'accesso",
+ "Could not unshare" : "Impossibile rimuovere la condivisione",
"Share details could not be loaded for this item." : "I dettagli della condivisione non possono essere caricati per questo elemento.",
"An error occured. Please try again" : "Si è verificato un errore. Prova ancora",
"Share" : "Condividi",
"Share with people on other ownClouds using the syntax username@example.com/owncloud" : "Condividi con persone su altri ownCloud utilizzando la sintassi nomeutente@esempio.com/owncloud",
"Share with users or groups …" : "Condividi con utenti o gruppi...",
"Share with users, groups or remote users …" : "Condividi con utenti, gruppi o utenti remoti...",
+ "Error removing share" : "Errore durante la rimozione della condivisione",
"Warning" : "Avviso",
"Error while sending notification" : "Errore durante l'invio della notifica",
"Non-existing tag #{tag}" : "Tag #{tag} inesistente",
"not assignable" : "non assegnabile",
"invisible" : "invisibile",
+ "({scope})" : "({scope})",
"Delete" : "Elimina",
"Rename" : "Rinomina",
"Global tags" : "Tag globali",
diff --git a/core/l10n/it.json b/core/l10n/it.json
index 3b301350cb1..966d0f58125 100644
--- a/core/l10n/it.json
+++ b/core/l10n/it.json
@@ -162,17 +162,20 @@
"change" : "cambia",
"delete" : "elimina",
"access control" : "controllo d'accesso",
+ "Could not unshare" : "Impossibile rimuovere la condivisione",
"Share details could not be loaded for this item." : "I dettagli della condivisione non possono essere caricati per questo elemento.",
"An error occured. Please try again" : "Si è verificato un errore. Prova ancora",
"Share" : "Condividi",
"Share with people on other ownClouds using the syntax username@example.com/owncloud" : "Condividi con persone su altri ownCloud utilizzando la sintassi nomeutente@esempio.com/owncloud",
"Share with users or groups …" : "Condividi con utenti o gruppi...",
"Share with users, groups or remote users …" : "Condividi con utenti, gruppi o utenti remoti...",
+ "Error removing share" : "Errore durante la rimozione della condivisione",
"Warning" : "Avviso",
"Error while sending notification" : "Errore durante l'invio della notifica",
"Non-existing tag #{tag}" : "Tag #{tag} inesistente",
"not assignable" : "non assegnabile",
"invisible" : "invisibile",
+ "({scope})" : "({scope})",
"Delete" : "Elimina",
"Rename" : "Rinomina",
"Global tags" : "Tag globali",
diff --git a/core/l10n/ja.js b/core/l10n/ja.js
index 044ca97e888..1b368469f43 100644
--- a/core/l10n/ja.js
+++ b/core/l10n/ja.js
@@ -163,17 +163,21 @@ OC.L10N.register(
"change" : "更新",
"delete" : "削除",
"access control" : "アクセス権限",
+ "Could not unshare" : "共有の解除ができませんでした",
"Share details could not be loaded for this item." : "共有の詳細はこのアイテムによりロードできませんでした。",
"An error occured. Please try again" : "エラーが発生しました。もう一度トライしてください。",
"Share" : "共有",
"Share with people on other ownClouds using the syntax username@example.com/owncloud" : "次の形式で指定して他のownCloudのユーザーと、共有",
"Share with users or groups …" : "ユーザーもしくはグループと共有 ...",
"Share with users, groups or remote users …" : "ユーザー、グループもしくはリモートユーザーと共有 ...",
+ "Error removing share" : "共有の削除エラー",
"Warning" : "警告",
"Error while sending notification" : "通知送信中にエラーが発生",
"Non-existing tag #{tag}" : "存在しないタグ#{tag}",
+ "not assignable" : "割り当てできない",
"Delete" : "削除",
"Rename" : "名前の変更",
+ "Global tags" : "グローバルタグ",
"The object type is not specified." : "オブジェクトタイプが指定されていません。",
"Enter new" : "新規に入力",
"Add" : "追加",
diff --git a/core/l10n/ja.json b/core/l10n/ja.json
index 337aa886031..4dbcaac6a08 100644
--- a/core/l10n/ja.json
+++ b/core/l10n/ja.json
@@ -161,17 +161,21 @@
"change" : "更新",
"delete" : "削除",
"access control" : "アクセス権限",
+ "Could not unshare" : "共有の解除ができませんでした",
"Share details could not be loaded for this item." : "共有の詳細はこのアイテムによりロードできませんでした。",
"An error occured. Please try again" : "エラーが発生しました。もう一度トライしてください。",
"Share" : "共有",
"Share with people on other ownClouds using the syntax username@example.com/owncloud" : "次の形式で指定して他のownCloudのユーザーと、共有",
"Share with users or groups …" : "ユーザーもしくはグループと共有 ...",
"Share with users, groups or remote users …" : "ユーザー、グループもしくはリモートユーザーと共有 ...",
+ "Error removing share" : "共有の削除エラー",
"Warning" : "警告",
"Error while sending notification" : "通知送信中にエラーが発生",
"Non-existing tag #{tag}" : "存在しないタグ#{tag}",
+ "not assignable" : "割り当てできない",
"Delete" : "削除",
"Rename" : "名前の変更",
+ "Global tags" : "グローバルタグ",
"The object type is not specified." : "オブジェクトタイプが指定されていません。",
"Enter new" : "新規に入力",
"Add" : "追加",
diff --git a/core/l10n/nl.js b/core/l10n/nl.js
index ed533d6d31c..560cdbcebbc 100644
--- a/core/l10n/nl.js
+++ b/core/l10n/nl.js
@@ -164,17 +164,20 @@ OC.L10N.register(
"change" : "wijzig",
"delete" : "verwijderen",
"access control" : "toegangscontrole",
+ "Could not unshare" : "Kon delen niet ongedaan maken",
"Share details could not be loaded for this item." : "Details van shares voor dit object konden niet worden geladen.",
"An error occured. Please try again" : "Er trad een fout op. Probeer het opnieuw",
"Share" : "Delen",
"Share with people on other ownClouds using the syntax username@example.com/owncloud" : "Delen met mensen op andere ownClouds via de syntax gebruikersnaam@voorbeeld.org/owncloud",
"Share with users or groups …" : "Delen met gebruikers of groepen ...",
"Share with users, groups or remote users …" : "Delen met gebruikers, groepen of externe gebruikers ...",
+ "Error removing share" : "Fout bij verwijderen share",
"Warning" : "Waarschuwing",
"Error while sending notification" : "Fout bij versturen melding",
"Non-existing tag #{tag}" : "Niet bestaande tag #{tag}",
"not assignable" : "niet toewijsbaar",
"invisible" : "onzichtbaar",
+ "({scope})" : "({scope})",
"Delete" : "Verwijder",
"Rename" : "Naam wijzigen",
"Global tags" : "Globale tags",
diff --git a/core/l10n/nl.json b/core/l10n/nl.json
index 906e1d23278..f5eb1c59b41 100644
--- a/core/l10n/nl.json
+++ b/core/l10n/nl.json
@@ -162,17 +162,20 @@
"change" : "wijzig",
"delete" : "verwijderen",
"access control" : "toegangscontrole",
+ "Could not unshare" : "Kon delen niet ongedaan maken",
"Share details could not be loaded for this item." : "Details van shares voor dit object konden niet worden geladen.",
"An error occured. Please try again" : "Er trad een fout op. Probeer het opnieuw",
"Share" : "Delen",
"Share with people on other ownClouds using the syntax username@example.com/owncloud" : "Delen met mensen op andere ownClouds via de syntax gebruikersnaam@voorbeeld.org/owncloud",
"Share with users or groups …" : "Delen met gebruikers of groepen ...",
"Share with users, groups or remote users …" : "Delen met gebruikers, groepen of externe gebruikers ...",
+ "Error removing share" : "Fout bij verwijderen share",
"Warning" : "Waarschuwing",
"Error while sending notification" : "Fout bij versturen melding",
"Non-existing tag #{tag}" : "Niet bestaande tag #{tag}",
"not assignable" : "niet toewijsbaar",
"invisible" : "onzichtbaar",
+ "({scope})" : "({scope})",
"Delete" : "Verwijder",
"Rename" : "Naam wijzigen",
"Global tags" : "Globale tags",
diff --git a/core/l10n/pt_BR.js b/core/l10n/pt_BR.js
index ec0605eaace..3d4e236bac2 100644
--- a/core/l10n/pt_BR.js
+++ b/core/l10n/pt_BR.js
@@ -164,17 +164,20 @@ OC.L10N.register(
"change" : "mudança",
"delete" : "remover",
"access control" : "controle de acesso",
+ "Could not unshare" : "Não foi possível descompartilhar",
"Share details could not be loaded for this item." : "Detalhes de compartilhamento não puderam ser carregados para este item.",
"An error occured. Please try again" : "Ocorreu um erro. Por favor tente novamente",
"Share" : "Compartilhar",
"Share with people on other ownClouds using the syntax username@example.com/owncloud" : "Compartilhar com usuários em outros ownClouds usando a sintaxe username@example.com/owncloud",
"Share with users or groups …" : "Compartilhar com usuários ou grupos ...",
"Share with users, groups or remote users …" : "Compartilhar com usuários, grupos ou usuários remoto ...",
+ "Error removing share" : "Erro na remoção do compartilhamento",
"Warning" : "Aviso",
"Error while sending notification" : "Erro ao enviar notificação",
"Non-existing tag #{tag}" : "Etiqueta não existente #{tag}",
"not assignable" : "não atribuível",
"invisible" : "invisível",
+ "({scope})" : "({scope})",
"Delete" : "Eliminar",
"Rename" : "Renomear",
"Global tags" : "Etiquetas Globais",
diff --git a/core/l10n/pt_BR.json b/core/l10n/pt_BR.json
index eb0e5c223cb..f2289975685 100644
--- a/core/l10n/pt_BR.json
+++ b/core/l10n/pt_BR.json
@@ -162,17 +162,20 @@
"change" : "mudança",
"delete" : "remover",
"access control" : "controle de acesso",
+ "Could not unshare" : "Não foi possível descompartilhar",
"Share details could not be loaded for this item." : "Detalhes de compartilhamento não puderam ser carregados para este item.",
"An error occured. Please try again" : "Ocorreu um erro. Por favor tente novamente",
"Share" : "Compartilhar",
"Share with people on other ownClouds using the syntax username@example.com/owncloud" : "Compartilhar com usuários em outros ownClouds usando a sintaxe username@example.com/owncloud",
"Share with users or groups …" : "Compartilhar com usuários ou grupos ...",
"Share with users, groups or remote users …" : "Compartilhar com usuários, grupos ou usuários remoto ...",
+ "Error removing share" : "Erro na remoção do compartilhamento",
"Warning" : "Aviso",
"Error while sending notification" : "Erro ao enviar notificação",
"Non-existing tag #{tag}" : "Etiqueta não existente #{tag}",
"not assignable" : "não atribuível",
"invisible" : "invisível",
+ "({scope})" : "({scope})",
"Delete" : "Eliminar",
"Rename" : "Renomear",
"Global tags" : "Etiquetas Globais",
diff --git a/core/l10n/pt_PT.js b/core/l10n/pt_PT.js
index dbed83d6c5f..653ec6ec8b5 100644
--- a/core/l10n/pt_PT.js
+++ b/core/l10n/pt_PT.js
@@ -164,17 +164,20 @@ OC.L10N.register(
"change" : "alterar",
"delete" : "apagar",
"access control" : "controlo de acesso",
+ "Could not unshare" : "Não foi possível cancelar a partilha",
"Share details could not be loaded for this item." : "Não foi possível carregar os detalhes de partilha para este item.",
"An error occured. Please try again" : "Ocorreu um erro. Por favor, tente de novo",
"Share" : "Compartilhar",
"Share with people on other ownClouds using the syntax username@example.com/owncloud" : "Compartilhe com as pessoas nas outras ownClouds utilizando a sintaxe username@example.com/owncloud",
"Share with users or groups …" : "Partilhar com utilizadores ou grupos...",
"Share with users, groups or remote users …" : "Partilhar com utilizadores, grupos ou utilizadores remotos...",
+ "Error removing share" : "Erro ao remover partilha",
"Warning" : "Aviso",
"Error while sending notification" : "Erro enquanto estava a enviar a notificação",
"Non-existing tag #{tag}" : "Etiqueta não existente #{tag}",
"not assignable" : "não atribuível",
"invisible" : "invisível",
+ "({scope})" : "({scope})",
"Delete" : "Apagar",
"Rename" : "Renomear",
"Global tags" : "Etiquetas gerais",
diff --git a/core/l10n/pt_PT.json b/core/l10n/pt_PT.json
index 298e821985b..af02e2bf081 100644
--- a/core/l10n/pt_PT.json
+++ b/core/l10n/pt_PT.json
@@ -162,17 +162,20 @@
"change" : "alterar",
"delete" : "apagar",
"access control" : "controlo de acesso",
+ "Could not unshare" : "Não foi possível cancelar a partilha",
"Share details could not be loaded for this item." : "Não foi possível carregar os detalhes de partilha para este item.",
"An error occured. Please try again" : "Ocorreu um erro. Por favor, tente de novo",
"Share" : "Compartilhar",
"Share with people on other ownClouds using the syntax username@example.com/owncloud" : "Compartilhe com as pessoas nas outras ownClouds utilizando a sintaxe username@example.com/owncloud",
"Share with users or groups …" : "Partilhar com utilizadores ou grupos...",
"Share with users, groups or remote users …" : "Partilhar com utilizadores, grupos ou utilizadores remotos...",
+ "Error removing share" : "Erro ao remover partilha",
"Warning" : "Aviso",
"Error while sending notification" : "Erro enquanto estava a enviar a notificação",
"Non-existing tag #{tag}" : "Etiqueta não existente #{tag}",
"not assignable" : "não atribuível",
"invisible" : "invisível",
+ "({scope})" : "({scope})",
"Delete" : "Apagar",
"Rename" : "Renomear",
"Global tags" : "Etiquetas gerais",
diff --git a/core/l10n/ru.js b/core/l10n/ru.js
index 93e919453a8..2b942444db1 100644
--- a/core/l10n/ru.js
+++ b/core/l10n/ru.js
@@ -164,16 +164,23 @@ OC.L10N.register(
"change" : "изменить",
"delete" : "удалить",
"access control" : "контроль доступа",
+ "Could not unshare" : "Не удается отменить доступ",
"Share details could not be loaded for this item." : "Не удалось загрузить информацию об общем доступе для этого элемента.",
"An error occured. Please try again" : "Произошла ошибка. Попробуйте ещё раз",
"Share" : "Поделиться",
"Share with people on other ownClouds using the syntax username@example.com/owncloud" : "Поделиться с людьми на других серверах ownCloud используя формат username@example.com/owncloud",
"Share with users or groups …" : "Поделиться с пользователями или группами ...",
"Share with users, groups or remote users …" : "Поделиться с пользователями, группами или удаленными пользователями ...",
+ "Error removing share" : "Ошибка удаления общего доступа",
"Warning" : "Предупреждение",
"Error while sending notification" : "Ошибка при отправке уведомления",
+ "Non-existing tag #{tag}" : "Несуществующий тег #{tag}",
+ "not assignable" : "не назначаемый",
+ "invisible" : "невидимый",
+ "({scope})" : "({scope})",
"Delete" : "Удалить",
"Rename" : "Переименовать",
+ "Global tags" : "Глобальные теги",
"The object type is not specified." : "Тип объекта не указан",
"Enter new" : "Ввести новое",
"Add" : "Добавить",
diff --git a/core/l10n/ru.json b/core/l10n/ru.json
index f88f24d6582..d90e88b37ae 100644
--- a/core/l10n/ru.json
+++ b/core/l10n/ru.json
@@ -162,16 +162,23 @@
"change" : "изменить",
"delete" : "удалить",
"access control" : "контроль доступа",
+ "Could not unshare" : "Не удается отменить доступ",
"Share details could not be loaded for this item." : "Не удалось загрузить информацию об общем доступе для этого элемента.",
"An error occured. Please try again" : "Произошла ошибка. Попробуйте ещё раз",
"Share" : "Поделиться",
"Share with people on other ownClouds using the syntax username@example.com/owncloud" : "Поделиться с людьми на других серверах ownCloud используя формат username@example.com/owncloud",
"Share with users or groups …" : "Поделиться с пользователями или группами ...",
"Share with users, groups or remote users …" : "Поделиться с пользователями, группами или удаленными пользователями ...",
+ "Error removing share" : "Ошибка удаления общего доступа",
"Warning" : "Предупреждение",
"Error while sending notification" : "Ошибка при отправке уведомления",
+ "Non-existing tag #{tag}" : "Несуществующий тег #{tag}",
+ "not assignable" : "не назначаемый",
+ "invisible" : "невидимый",
+ "({scope})" : "({scope})",
"Delete" : "Удалить",
"Rename" : "Переименовать",
+ "Global tags" : "Глобальные теги",
"The object type is not specified." : "Тип объекта не указан",
"Enter new" : "Ввести новое",
"Add" : "Добавить",
diff --git a/core/l10n/sq.js b/core/l10n/sq.js
index bdef0ba9196..bcdec3b9c73 100644
--- a/core/l10n/sq.js
+++ b/core/l10n/sq.js
@@ -164,17 +164,20 @@ OC.L10N.register(
"change" : "ndërroje",
"delete" : "fshije",
"access control" : "kontroll hyrjesh",
+ "Could not unshare" : "S’e shndau dot",
"Share details could not be loaded for this item." : "Për këtë objekt s’u ngarkuan dot hollësi ndarjeje.",
"An error occured. Please try again" : "Ndodhi një gabim. Ju lutemi, riprovoni",
"Share" : "Ndaje",
"Share with people on other ownClouds using the syntax username@example.com/owncloud" : "Ndajeni me persona në ownCloud-e të tjera duke përdorur sintaksën username@example.com/owncloud",
"Share with users or groups …" : "Ndajeni me përdorues ose grupe …",
"Share with users, groups or remote users …" : "Ndajeni me përdorues, grupe ose përdorues të largët …",
+ "Error removing share" : "Gabim në heqjen e ndarjes",
"Warning" : "Kujdes",
"Error while sending notification" : "Gabim gjatë dërgimit të njoftimit",
"Non-existing tag #{tag}" : "Etiketë #{tag} që s’ekziston",
"not assignable" : "e pacaktueshme",
"invisible" : "e padukshme",
+ "({scope})" : "({scope})",
"Delete" : "Fshije",
"Rename" : "Riemërtoje",
"Global tags" : "Etiketa globale",
diff --git a/core/l10n/sq.json b/core/l10n/sq.json
index 90a455c99aa..3fdd04bd429 100644
--- a/core/l10n/sq.json
+++ b/core/l10n/sq.json
@@ -162,17 +162,20 @@
"change" : "ndërroje",
"delete" : "fshije",
"access control" : "kontroll hyrjesh",
+ "Could not unshare" : "S’e shndau dot",
"Share details could not be loaded for this item." : "Për këtë objekt s’u ngarkuan dot hollësi ndarjeje.",
"An error occured. Please try again" : "Ndodhi një gabim. Ju lutemi, riprovoni",
"Share" : "Ndaje",
"Share with people on other ownClouds using the syntax username@example.com/owncloud" : "Ndajeni me persona në ownCloud-e të tjera duke përdorur sintaksën username@example.com/owncloud",
"Share with users or groups …" : "Ndajeni me përdorues ose grupe …",
"Share with users, groups or remote users …" : "Ndajeni me përdorues, grupe ose përdorues të largët …",
+ "Error removing share" : "Gabim në heqjen e ndarjes",
"Warning" : "Kujdes",
"Error while sending notification" : "Gabim gjatë dërgimit të njoftimit",
"Non-existing tag #{tag}" : "Etiketë #{tag} që s’ekziston",
"not assignable" : "e pacaktueshme",
"invisible" : "e padukshme",
+ "({scope})" : "({scope})",
"Delete" : "Fshije",
"Rename" : "Riemërtoje",
"Global tags" : "Etiketa globale",
diff --git a/core/shipped.json b/core/shipped.json
index 5dd8700bf1a..5f995326625 100644
--- a/core/shipped.json
+++ b/core/shipped.json
@@ -3,6 +3,7 @@
"activity",
"admin_audit",
"encryption",
+ "comments",
"dav",
"enterprise_key",
"external",
diff --git a/core/templates/layout.user.php b/core/templates/layout.user.php
index 7fe67159bb5..7905f5b7f3a 100644
--- a/core/templates/layout.user.php
+++ b/core/templates/layout.user.php
@@ -2,7 +2,7 @@
<!--[if lte IE 8]><html class="ng-csp ie ie8 lte9 lte8" data-placeholder-focus="false" lang="<?php p($_['language']); ?>" ><![endif]-->
<!--[if IE 9]><html class="ng-csp ie ie9 lte9" data-placeholder-focus="false" lang="<?php p($_['language']); ?>" ><![endif]-->
<!--[if (gt IE 9)|!(IE)]><!--><html class="ng-csp" data-placeholder-focus="false" lang="<?php p($_['language']); ?>" ><!--<![endif]-->
- <head data-user="<?php p($_['user_uid']); ?>" data-requesttoken="<?php p($_['requesttoken']); ?>"
+ <head data-user="<?php p($_['user_uid']); ?>" data-user-displayname="<?php p($_['user_displayname']); ?>" data-requesttoken="<?php p($_['requesttoken']); ?>"
<?php if ($_['updateAvailable']): ?>
data-update-version="<?php p($_['updateVersion']); ?>" data-update-link="<?php p($_['updateLink']); ?>"
<?php endif; ?>
diff --git a/core/vendor/davclient.js/lib/client.js b/core/vendor/davclient.js/lib/client.js
index 1a73c7db020..89c11516a38 100644
--- a/core/vendor/davclient.js/lib/client.js
+++ b/core/vendor/davclient.js/lib/client.js
@@ -1,17 +1,17 @@
if (typeof dav == 'undefined') { dav = {}; };
dav._XML_CHAR_MAP = {
- '<': '&lt;',
- '>': '&gt;',
- '&': '&amp;',
- '"': '&quot;',
- "'": '&apos;'
+ '<': '&lt;',
+ '>': '&gt;',
+ '&': '&amp;',
+ '"': '&quot;',
+ "'": '&apos;'
};
dav._escapeXml = function(s) {
- return s.replace(/[<>&"']/g, function (ch) {
- return dav._XML_CHAR_MAP[ch];
- });
+ return s.replace(/[<>&"']/g, function (ch) {
+ return dav._XML_CHAR_MAP[ch];
+ });
};
dav.Client = function(options) {
@@ -79,17 +79,16 @@ dav.Client.prototype = {
return this.request('PROPFIND', url, headers, body).then(
function(result) {
- var resultBody = this.parseMultiStatus(result.body);
if (depth===0) {
return {
status: result.status,
- body: resultBody[0],
+ body: result.body[0],
xhr: result.xhr
};
} else {
return {
status: result.status,
- body: resultBody,
+ body: result.body,
xhr: result.xhr
};
}
@@ -161,6 +160,7 @@ dav.Client.prototype = {
*/
request : function(method, url, headers, body) {
+ var self = this;
var xhr = this.xhrProvider();
if (this.userName) {
@@ -182,8 +182,13 @@ dav.Client.prototype = {
return;
}
+ var resultBody = xhr.response;
+ if (xhr.status === 207) {
+ resultBody = self.parseMultiStatus(xhr.response);
+ }
+
fulfill({
- body: xhr.response,
+ body: resultBody,
status: xhr.status,
xhr: xhr
});
@@ -238,7 +243,7 @@ dav.Client.prototype = {
}
}
- return content || propNode.textContent || propNode.text;
+ return content || propNode.textContent || propNode.text || '';
},
/**
diff --git a/db_structure.xml b/db_structure.xml
index 99bfa519b40..ea1b89e28da 100644
--- a/db_structure.xml
+++ b/db_structure.xml
@@ -1582,6 +1582,49 @@
<table>
<!--
+ default place to store per user and object read markers
+ -->
+ <name>*dbprefix*comments_read_markers</name>
+
+ <declaration>
+
+ <field>
+ <name>user_id</name>
+ <type>text</type>
+ <default></default>
+ <notnull>true</notnull>
+ <length>64</length>
+ </field>
+
+ <field>
+ <name>marker_datetime</name>
+ <type>timestamp</type>
+ <default></default>
+ <notnull>false</notnull>
+ </field>
+
+ <field>
+ <name>object_type</name>
+ <type>text</type>
+ <default></default>
+ <notnull>true</notnull>
+ <length>64</length>
+ </field>
+
+ <field>
+ <name>object_id</name>
+ <type>text</type>
+ <default></default>
+ <notnull>true</notnull>
+ <length>64</length>
+ </field>
+
+ </declaration>
+
+ </table>
+
+ <table>
+ <!--
Encrypted credentials storage
-->
<name>*dbprefix*credentials</name>
diff --git a/lib/base.php b/lib/base.php
index 56ff1cb8962..fc4a99287d0 100644
--- a/lib/base.php
+++ b/lib/base.php
@@ -645,7 +645,6 @@ class OC {
}
self::registerShareHooks();
self::registerLogRotate();
- self::registerLocalAddressBook();
self::registerEncryptionWrapper();
self::registerEncryptionHooks();
@@ -701,14 +700,6 @@ class OC {
\OC::$server->getEventLogger()->end('boot');
}
- private static function registerLocalAddressBook() {
- self::$server->getContactsManager()->register(function() {
- $userManager = \OC::$server->getUserManager();
- \OC::$server->getContactsManager()->registerAddressBook(
- new \OC\Contacts\LocalAddressBook($userManager));
- });
- }
-
/**
* register hooks for the cache
*/
diff --git a/lib/l10n/de_DE.js b/lib/l10n/de_DE.js
index 886d107bc7a..b434ac56aad 100644
--- a/lib/l10n/de_DE.js
+++ b/lib/l10n/de_DE.js
@@ -107,8 +107,10 @@ OC.L10N.register(
"Sharing %s failed, because resharing is not allowed" : "Freigabe von %s fehlgeschlagen, da das nochmalige Freigeben einer Freigabe nicht erlaubt ist",
"Sharing %s failed, because the sharing backend for %s could not find its source" : "Freigabe von %s fehlgeschlagen, da das Freigabe-Backend für %s nicht in dieser Quelle gefunden werden konnte",
"Sharing %s failed, because the file could not be found in the file cache" : "Freigabe von %s fehlgeschlagen, da die Datei im Datei-Cache nicht gefunden werden konnte",
+ "Expiration date is in the past" : "Das Ablaufdatum liegt in der Vergangenheit.",
"Could not find category \"%s\"" : "Die Kategorie „%s“ konnte nicht gefunden werden",
"Apps" : "Apps",
+ "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Nur die follgenden Zeichen sind im Benutzernamen erlaubt: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"",
"A valid username must be provided" : "Es muss ein gültiger Benutzername angegeben werden",
"A valid password must be provided" : "Es muss ein gültiges Passwort angegeben werden",
"The username is already being used" : "Der Benutzername existiert bereits",
diff --git a/lib/l10n/de_DE.json b/lib/l10n/de_DE.json
index 33a177eedea..09ce050c452 100644
--- a/lib/l10n/de_DE.json
+++ b/lib/l10n/de_DE.json
@@ -105,8 +105,10 @@
"Sharing %s failed, because resharing is not allowed" : "Freigabe von %s fehlgeschlagen, da das nochmalige Freigeben einer Freigabe nicht erlaubt ist",
"Sharing %s failed, because the sharing backend for %s could not find its source" : "Freigabe von %s fehlgeschlagen, da das Freigabe-Backend für %s nicht in dieser Quelle gefunden werden konnte",
"Sharing %s failed, because the file could not be found in the file cache" : "Freigabe von %s fehlgeschlagen, da die Datei im Datei-Cache nicht gefunden werden konnte",
+ "Expiration date is in the past" : "Das Ablaufdatum liegt in der Vergangenheit.",
"Could not find category \"%s\"" : "Die Kategorie „%s“ konnte nicht gefunden werden",
"Apps" : "Apps",
+ "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Nur die follgenden Zeichen sind im Benutzernamen erlaubt: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"",
"A valid username must be provided" : "Es muss ein gültiger Benutzername angegeben werden",
"A valid password must be provided" : "Es muss ein gültiges Passwort angegeben werden",
"The username is already being used" : "Der Benutzername existiert bereits",
diff --git a/lib/l10n/ja.js b/lib/l10n/ja.js
index 2de52bad390..e3433a18d7a 100644
--- a/lib/l10n/ja.js
+++ b/lib/l10n/ja.js
@@ -113,8 +113,11 @@ OC.L10N.register(
"Sharing %s failed, because resharing is not allowed" : "%s を共有できませんでした。再共有は許可されていません。",
"Sharing %s failed, because the sharing backend for %s could not find its source" : "%s の共有に失敗しました。%s のバックエンド共有に必要なソースが見つかりませんでした。",
"Sharing %s failed, because the file could not be found in the file cache" : "%s の共有に失敗しました。ファイルキャッシュにファイルがありませんでした。",
+ "Expiration date is in the past" : "有効期限が切れています",
+ "Cannot set expiration date more than %s days in the future" : "有効期限を%s日以降に設定できません。",
"Could not find category \"%s\"" : "カテゴリ \"%s\" が見つかりませんでした",
"Apps" : "アプリ",
+ "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "ユーザー名で利用できる文字列は、次のものです: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-\"",
"A valid username must be provided" : "有効なユーザー名を指定する必要があります",
"A valid password must be provided" : "有効なパスワードを指定する必要があります",
"The username is already being used" : "ユーザー名はすでに使われています",
diff --git a/lib/l10n/ja.json b/lib/l10n/ja.json
index 42b9b1912c7..a4b18386c7a 100644
--- a/lib/l10n/ja.json
+++ b/lib/l10n/ja.json
@@ -111,8 +111,11 @@
"Sharing %s failed, because resharing is not allowed" : "%s を共有できませんでした。再共有は許可されていません。",
"Sharing %s failed, because the sharing backend for %s could not find its source" : "%s の共有に失敗しました。%s のバックエンド共有に必要なソースが見つかりませんでした。",
"Sharing %s failed, because the file could not be found in the file cache" : "%s の共有に失敗しました。ファイルキャッシュにファイルがありませんでした。",
+ "Expiration date is in the past" : "有効期限が切れています",
+ "Cannot set expiration date more than %s days in the future" : "有効期限を%s日以降に設定できません。",
"Could not find category \"%s\"" : "カテゴリ \"%s\" が見つかりませんでした",
"Apps" : "アプリ",
+ "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "ユーザー名で利用できる文字列は、次のものです: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-\"",
"A valid username must be provided" : "有効なユーザー名を指定する必要があります",
"A valid password must be provided" : "有効なパスワードを指定する必要があります",
"The username is already being used" : "ユーザー名はすでに使われています",
diff --git a/lib/l10n/nl.js b/lib/l10n/nl.js
index b2427d3f1ee..e49ddc6d27c 100644
--- a/lib/l10n/nl.js
+++ b/lib/l10n/nl.js
@@ -118,6 +118,7 @@ OC.L10N.register(
"Cannot set expiration date more than %s days in the future" : "Kan vervaldatum niet verder dan %s dagen in de toekomst instellen",
"Could not find category \"%s\"" : "Kon categorie \"%s\" niet vinden",
"Apps" : "Apps",
+ "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Alleen de volgende tekens zijn toegestaan in een gebruikersnaam: \"a-z\", \"A-Z\", \"0-9\", en \"_.@-\"",
"A valid username must be provided" : "Er moet een geldige gebruikersnaam worden opgegeven",
"A valid password must be provided" : "Er moet een geldig wachtwoord worden opgegeven",
"The username is already being used" : "De gebruikersnaam bestaat al",
diff --git a/lib/l10n/nl.json b/lib/l10n/nl.json
index 094866dc08b..78b54fbbc6d 100644
--- a/lib/l10n/nl.json
+++ b/lib/l10n/nl.json
@@ -116,6 +116,7 @@
"Cannot set expiration date more than %s days in the future" : "Kan vervaldatum niet verder dan %s dagen in de toekomst instellen",
"Could not find category \"%s\"" : "Kon categorie \"%s\" niet vinden",
"Apps" : "Apps",
+ "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Alleen de volgende tekens zijn toegestaan in een gebruikersnaam: \"a-z\", \"A-Z\", \"0-9\", en \"_.@-\"",
"A valid username must be provided" : "Er moet een geldige gebruikersnaam worden opgegeven",
"A valid password must be provided" : "Er moet een geldig wachtwoord worden opgegeven",
"The username is already being used" : "De gebruikersnaam bestaat al",
diff --git a/lib/l10n/pt_BR.js b/lib/l10n/pt_BR.js
index a9a282882c6..1ffbdc17a7b 100644
--- a/lib/l10n/pt_BR.js
+++ b/lib/l10n/pt_BR.js
@@ -118,6 +118,7 @@ OC.L10N.register(
"Cannot set expiration date more than %s days in the future" : "Não é possível definir a data de validade mais de %s dias no futuro",
"Could not find category \"%s\"" : "Impossível localizar categoria \"%s\"",
"Apps" : "Aplicações",
+ "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Somente os seguintes caracteres são permitidos em um nome de usuário: \"a-z\", \"A-Z\", \"0-9\", e \"_.@-'\"",
"A valid username must be provided" : "Forneça um nome de usuário válido",
"A valid password must be provided" : "Forneça uma senha válida",
"The username is already being used" : "Este nome de usuário já está sendo usado",
diff --git a/lib/l10n/pt_BR.json b/lib/l10n/pt_BR.json
index 0a2ed3628dd..cb0e48af0db 100644
--- a/lib/l10n/pt_BR.json
+++ b/lib/l10n/pt_BR.json
@@ -116,6 +116,7 @@
"Cannot set expiration date more than %s days in the future" : "Não é possível definir a data de validade mais de %s dias no futuro",
"Could not find category \"%s\"" : "Impossível localizar categoria \"%s\"",
"Apps" : "Aplicações",
+ "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "Somente os seguintes caracteres são permitidos em um nome de usuário: \"a-z\", \"A-Z\", \"0-9\", e \"_.@-'\"",
"A valid username must be provided" : "Forneça um nome de usuário válido",
"A valid password must be provided" : "Forneça uma senha válida",
"The username is already being used" : "Este nome de usuário já está sendo usado",
diff --git a/lib/l10n/ru.js b/lib/l10n/ru.js
index 034f9e7f355..14a2b11dc01 100644
--- a/lib/l10n/ru.js
+++ b/lib/l10n/ru.js
@@ -118,6 +118,7 @@ OC.L10N.register(
"Cannot set expiration date more than %s days in the future" : "Невозможно установить дату окончания срока действия более %s дней",
"Could not find category \"%s\"" : "Категория \"%s\" не найдена",
"Apps" : "Приложения",
+ "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "В качестве имени пользователя допускаются следующие символы: \"a-z\", \"A-Z\", \"0-9\" и \"_.@-'\"",
"A valid username must be provided" : "Укажите правильное имя пользователя",
"A valid password must be provided" : "Укажите правильный пароль",
"The username is already being used" : "Имя пользователя уже используется",
diff --git a/lib/l10n/ru.json b/lib/l10n/ru.json
index 90c7fb81ada..592abd22e81 100644
--- a/lib/l10n/ru.json
+++ b/lib/l10n/ru.json
@@ -116,6 +116,7 @@
"Cannot set expiration date more than %s days in the future" : "Невозможно установить дату окончания срока действия более %s дней",
"Could not find category \"%s\"" : "Категория \"%s\" не найдена",
"Apps" : "Приложения",
+ "Only the following characters are allowed in a username: \"a-z\", \"A-Z\", \"0-9\", and \"_.@-'\"" : "В качестве имени пользователя допускаются следующие символы: \"a-z\", \"A-Z\", \"0-9\" и \"_.@-'\"",
"A valid username must be provided" : "Укажите правильное имя пользователя",
"A valid password must be provided" : "Укажите правильный пароль",
"The username is already being used" : "Имя пользователя уже используется",
diff --git a/lib/private/activitymanager.php b/lib/private/activitymanager.php
index e5235cb9bfe..68bb8c13401 100644
--- a/lib/private/activitymanager.php
+++ b/lib/private/activitymanager.php
@@ -310,20 +310,19 @@ class ActivityManager implements IManager {
/**
* @param string $type
- * @param int $id
+ * @param string $id
*/
public function setFormattingObject($type, $id) {
$this->formattingObjectType = $type;
- $this->formattingObjectId = $id;
+ $this->formattingObjectId = (string) $id;
}
/**
* @return bool
*/
public function isFormattingFilteredObject() {
- return 'filter' === $this->request->getParam('filter')
- && $this->formattingObjectType === $this->request->getParam('objecttype')
- && $this->formattingObjectId === $this->request->getParam('objectid');
+ return $this->formattingObjectType === $this->request->getParam('object_type')
+ && $this->formattingObjectId === $this->request->getParam('object_id');
}
/**
diff --git a/lib/private/appframework/dependencyinjection/dicontainer.php b/lib/private/appframework/dependencyinjection/dicontainer.php
index 61a04482431..5fc45fdd2e8 100644
--- a/lib/private/appframework/dependencyinjection/dicontainer.php
+++ b/lib/private/appframework/dependencyinjection/dicontainer.php
@@ -32,7 +32,6 @@ namespace OC\AppFramework\DependencyInjection;
use OC;
use OC\AppFramework\Http;
-use OC\AppFramework\Http\Request;
use OC\AppFramework\Http\Dispatcher;
use OC\AppFramework\Http\Output;
use OC\AppFramework\Core\API;
@@ -43,8 +42,6 @@ use OC\AppFramework\Middleware\SessionMiddleware;
use OC\AppFramework\Utility\SimpleContainer;
use OCP\AppFramework\IApi;
use OCP\AppFramework\IAppContainer;
-use OCP\AppFramework\Middleware;
-use OCP\IServerContainer;
class DIContainer extends SimpleContainer implements IAppContainer {
@@ -155,6 +152,10 @@ class DIContainer extends SimpleContainer implements IAppContainer {
return $this->getServer()->getL10N($c->query('AppName'));
});
+ $this->registerService('OCP\\L10N\\IFactory', function($c) {
+ return $this->getServer()->getL10NFactory();
+ });
+
$this->registerService('OCP\\ILogger', function($c) {
return $this->getServer()->getLogger();
});
@@ -255,6 +256,10 @@ class DIContainer extends SimpleContainer implements IAppContainer {
return $this->getServer()->getSession();
});
+ $this->registerService('OCP\\Security\\IContentSecurityPolicyManager', function($c) {
+ return $this->getServer()->getContentSecurityPolicyManager();
+ });
+
$this->registerService('ServerContainer', function ($c) {
return $this->getServer();
});
@@ -319,7 +324,8 @@ class DIContainer extends SimpleContainer implements IAppContainer {
$app->getServer()->getLogger(),
$c['AppName'],
$app->isLoggedIn(),
- $app->isAdminUser()
+ $app->isAdminUser(),
+ $app->getServer()->getContentSecurityPolicyManager()
);
});
diff --git a/lib/private/appframework/middleware/security/securitymiddleware.php b/lib/private/appframework/middleware/security/securitymiddleware.php
index 4ef043ad50f..f1ba98254f0 100644
--- a/lib/private/appframework/middleware/security/securitymiddleware.php
+++ b/lib/private/appframework/middleware/security/securitymiddleware.php
@@ -32,6 +32,8 @@ use OC\Appframework\Middleware\Security\Exceptions\CrossSiteRequestForgeryExcept
use OC\Appframework\Middleware\Security\Exceptions\NotAdminException;
use OC\Appframework\Middleware\Security\Exceptions\NotLoggedInException;
use OC\AppFramework\Utility\ControllerMethodReflector;
+use OC\Security\CSP\ContentSecurityPolicyManager;
+use OCP\AppFramework\Http\ContentSecurityPolicy;
use OCP\AppFramework\Http\RedirectResponse;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\AppFramework\Middleware;
@@ -52,15 +54,24 @@ use OC\AppFramework\Middleware\Security\Exceptions\SecurityException;
* check fails
*/
class SecurityMiddleware extends Middleware {
-
+ /** @var INavigationManager */
private $navigationManager;
+ /** @var IRequest */
private $request;
+ /** @var ControllerMethodReflector */
private $reflector;
+ /** @var string */
private $appName;
+ /** @var IURLGenerator */
private $urlGenerator;
+ /** @var ILogger */
private $logger;
+ /** @var bool */
private $isLoggedIn;
+ /** @var bool */
private $isAdminUser;
+ /** @var ContentSecurityPolicyManager */
+ private $contentSecurityPolicyManager;
/**
* @param IRequest $request
@@ -71,6 +82,7 @@ class SecurityMiddleware extends Middleware {
* @param string $appName
* @param bool $isLoggedIn
* @param bool $isAdminUser
+ * @param ContentSecurityPolicyManager $contentSecurityPolicyManager
*/
public function __construct(IRequest $request,
ControllerMethodReflector $reflector,
@@ -79,7 +91,8 @@ class SecurityMiddleware extends Middleware {
ILogger $logger,
$appName,
$isLoggedIn,
- $isAdminUser) {
+ $isAdminUser,
+ ContentSecurityPolicyManager $contentSecurityPolicyManager) {
$this->navigationManager = $navigationManager;
$this->request = $request;
$this->reflector = $reflector;
@@ -88,6 +101,7 @@ class SecurityMiddleware extends Middleware {
$this->logger = $logger;
$this->isLoggedIn = $isLoggedIn;
$this->isAdminUser = $isAdminUser;
+ $this->contentSecurityPolicyManager = $contentSecurityPolicyManager;
}
@@ -139,6 +153,25 @@ class SecurityMiddleware extends Middleware {
}
+ /**
+ * Performs the default CSP modifications that may be injected by other
+ * applications
+ *
+ * @param Controller $controller
+ * @param string $methodName
+ * @param Response $response
+ * @return Response
+ */
+ public function afterController($controller, $methodName, Response $response) {
+ $policy = !is_null($response->getContentSecurityPolicy()) ? $response->getContentSecurityPolicy() : new ContentSecurityPolicy();
+
+ $defaultPolicy = $this->contentSecurityPolicyManager->getDefaultPolicy();
+ $defaultPolicy = $this->contentSecurityPolicyManager->mergePolicies($defaultPolicy, $policy);
+
+ $response->setContentSecurityPolicy($defaultPolicy);
+
+ return $response;
+ }
/**
* If an SecurityException is being caught, ajax requests return a JSON error
diff --git a/lib/private/comments/manager.php b/lib/private/comments/manager.php
index 64977c48895..28bd3b0916a 100644
--- a/lib/private/comments/manager.php
+++ b/lib/private/comments/manager.php
@@ -25,7 +25,9 @@ use OCP\Comments\IComment;
use OCP\Comments\ICommentsManager;
use OCP\Comments\NotFoundException;
use OCP\IDBConnection;
+use OCP\IConfig;
use OCP\ILogger;
+use OCP\IUser;
class Manager implements ICommentsManager {
@@ -38,12 +40,17 @@ class Manager implements ICommentsManager {
/** @var IComment[] */
protected $commentsCache = [];
+ /** @var IConfig */
+ protected $config;
+
public function __construct(
IDBConnection $dbConn,
- ILogger $logger
+ ILogger $logger,
+ IConfig $config
) {
$this->dbConn = $dbConn;
$this->logger = $logger;
+ $this->config = $config;
}
/**
@@ -346,10 +353,12 @@ class Manager implements ICommentsManager {
/**
* @param $objectType string the object type, e.g. 'files'
* @param $objectId string the id of the object
+ * @param \DateTime $notOlderThan optional, timestamp of the oldest comments
+ * that may be returned
* @return Int
* @since 9.0.0
*/
- public function getNumberOfCommentsForObject($objectType, $objectId) {
+ public function getNumberOfCommentsForObject($objectType, $objectId, \DateTime $notOlderThan = null) {
$qb = $this->dbConn->getQueryBuilder();
$query = $qb->select($qb->createFunction('COUNT(`id`)'))
->from('comments')
@@ -358,6 +367,12 @@ class Manager implements ICommentsManager {
->setParameter('type', $objectType)
->setParameter('id', $objectId);
+ if(!is_null($notOlderThan)) {
+ $query
+ ->andWhere($qb->expr()->gt('creation_timestamp', $qb->createParameter('notOlderThan')))
+ ->setParameter('notOlderThan', $notOlderThan, 'datetime');
+ }
+
$resultStatement = $query->execute();
$data = $resultStatement->fetch(\PDO::FETCH_NUM);
$resultStatement->closeCursor();
@@ -566,4 +581,130 @@ class Manager implements ICommentsManager {
return is_int($affectedRows);
}
+
+ /**
+ * deletes the read markers for the specified user
+ *
+ * @param \OCP\IUser $user
+ * @return bool
+ * @since 9.0.0
+ */
+ public function deleteReadMarksFromUser(IUser $user) {
+ $qb = $this->dbConn->getQueryBuilder();
+ $query = $qb->delete('comments_read_markers')
+ ->where($qb->expr()->eq('user_id', $qb->createParameter('user_id')))
+ ->setParameter('user_id', $user->getUID());
+
+ try {
+ $affectedRows = $query->execute();
+ } catch (DriverException $e) {
+ $this->logger->logException($e, ['app' => 'core_comments']);
+ return false;
+ }
+ return ($affectedRows > 0);
+ }
+
+ /**
+ * sets the read marker for a given file to the specified date for the
+ * provided user
+ *
+ * @param string $objectType
+ * @param string $objectId
+ * @param \DateTime $dateTime
+ * @param IUser $user
+ * @since 9.0.0
+ */
+ public function setReadMark($objectType, $objectId, \DateTime $dateTime, IUser $user) {
+ $this->checkRoleParameters('Object', $objectType, $objectId);
+
+ $qb = $this->dbConn->getQueryBuilder();
+ $values = [
+ 'user_id' => $qb->createNamedParameter($user->getUID()),
+ 'marker_datetime' => $qb->createNamedParameter($dateTime, 'datetime'),
+ 'object_type' => $qb->createNamedParameter($objectType),
+ 'object_id' => $qb->createNamedParameter($objectId),
+ ];
+
+ // Strategy: try to update, if this does not return affected rows, do an insert.
+ $affectedRows = $qb
+ ->update('comments_read_markers')
+ ->set('user_id', $values['user_id'])
+ ->set('marker_datetime', $values['marker_datetime'])
+ ->set('object_type', $values['object_type'])
+ ->set('object_id', $values['object_id'])
+ ->where($qb->expr()->eq('user_id', $qb->createParameter('user_id')))
+ ->andWhere($qb->expr()->eq('object_type', $qb->createParameter('object_type')))
+ ->andWhere($qb->expr()->eq('object_id', $qb->createParameter('object_id')))
+ ->setParameter('user_id', $user->getUID(), \PDO::PARAM_STR)
+ ->setParameter('object_type', $objectType, \PDO::PARAM_STR)
+ ->setParameter('object_id', $objectId, \PDO::PARAM_STR)
+ ->execute();
+
+ if ($affectedRows > 0) {
+ return;
+ }
+
+ $qb->insert('comments_read_markers')
+ ->values($values)
+ ->execute();
+ }
+
+ /**
+ * returns the read marker for a given file to the specified date for the
+ * provided user. It returns null, when the marker is not present, i.e.
+ * no comments were marked as read.
+ *
+ * @param string $objectType
+ * @param string $objectId
+ * @param IUser $user
+ * @return \DateTime|null
+ * @since 9.0.0
+ */
+ public function getReadMark($objectType, $objectId, IUser $user) {
+ $qb = $this->dbConn->getQueryBuilder();
+ $resultStatement = $qb->select('marker_datetime')
+ ->from('comments_read_markers')
+ ->where($qb->expr()->eq('user_id', $qb->createParameter('user_id')))
+ ->andWhere($qb->expr()->eq('object_type', $qb->createParameter('object_type')))
+ ->andWhere($qb->expr()->eq('object_id', $qb->createParameter('object_id')))
+ ->setParameter('user_id', $user->getUID(), \PDO::PARAM_STR)
+ ->setParameter('object_type', $objectType, \PDO::PARAM_STR)
+ ->setParameter('object_id', $objectId, \PDO::PARAM_STR)
+ ->execute();
+
+ $data = $resultStatement->fetch();
+ $resultStatement->closeCursor();
+ if(!$data || is_null($data['marker_datetime'])) {
+ return null;
+ }
+
+ return new \DateTime($data['marker_datetime']);
+ }
+
+ /**
+ * deletes the read markers on the specified object
+ *
+ * @param string $objectType
+ * @param string $objectId
+ * @return bool
+ * @since 9.0.0
+ */
+ public function deleteReadMarksOnObject($objectType, $objectId) {
+ $this->checkRoleParameters('Object', $objectType, $objectId);
+
+ $qb = $this->dbConn->getQueryBuilder();
+ $query = $qb->delete('comments_read_markers')
+ ->where($qb->expr()->eq('object_type', $qb->createParameter('object_type')))
+ ->andWhere($qb->expr()->eq('object_id', $qb->createParameter('object_id')))
+ ->setParameter('object_type', $objectType)
+ ->setParameter('object_id', $objectId);
+
+ try {
+ $affectedRows = $query->execute();
+ } catch (DriverException $e) {
+ $this->logger->logException($e, ['app' => 'core_comments']);
+ return false;
+ }
+ return ($affectedRows > 0);
+ }
}
diff --git a/lib/private/comments/managerfactory.php b/lib/private/comments/managerfactory.php
index 281e8ca7fef..d3f6c44e539 100644
--- a/lib/private/comments/managerfactory.php
+++ b/lib/private/comments/managerfactory.php
@@ -51,7 +51,8 @@ class ManagerFactory implements ICommentsManagerFactory {
public function getManager() {
return new Manager(
$this->serverContainer->getDatabaseConnection(),
- $this->serverContainer->getLogger()
+ $this->serverContainer->getLogger(),
+ $this->serverContainer->getConfig()
);
}
}
diff --git a/lib/private/contacts/localaddressbook.php b/lib/private/contacts/localaddressbook.php
deleted file mode 100644
index 6fba63ae327..00000000000
--- a/lib/private/contacts/localaddressbook.php
+++ /dev/null
@@ -1,118 +0,0 @@
-<?php
-/**
- * @author Joas Schilling <nickvergessen@owncloud.com>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Thomas Müller <thomas.mueller@tmit.eu>
- *
- * @copyright Copyright (c) 2016, 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\Contacts;
-
-class LocalAddressBook implements \OCP\IAddressBook {
-
- /**
- * @var \OCP\IUserManager
- */
- private $userManager;
-
- /**
- * @param $userManager
- */
- public function __construct($userManager) {
- $this->userManager = $userManager;
- }
-
- /**
- * @return string defining the technical unique key
- */
- public function getKey() {
- return 'local';
- }
-
- /**
- * In comparison to getKey() this function returns a human readable (maybe translated) name
- *
- * @return mixed
- */
- public function getDisplayName() {
- return "Local users";
- }
-
- /**
- * @param string $pattern which should match within the $searchProperties
- * @param array $searchProperties defines the properties within the query pattern should match
- * @param array $options - for future use. One should always have options!
- * @return array an array of contacts which are arrays of key-value-pairs
- */
- public function search($pattern, $searchProperties, $options) {
- $users = array();
- if($pattern == '') {
- // Fetch all contacts
- $users = $this->userManager->search('');
- } else {
- foreach($searchProperties as $property) {
- $result = array();
- if($property === 'FN') {
- $result = $this->userManager->searchDisplayName($pattern);
- } else if ($property === 'id') {
- $result = $this->userManager->search($pattern);
- }
- if (is_array($result)) {
- $users = array_merge($users, $result);
- }
- }
- }
-
- $contacts = array();
- foreach($users as $user){
- $contact = array(
- "id" => $user->getUID(),
- "FN" => $user->getDisplayname(),
- "EMAIL" => array(),
- "IMPP" => array(
- "x-owncloud-handle:" . $user->getUID()
- )
- );
- $contacts[] = $contact;
- }
- return $contacts;
- }
-
- /**
- * @param array $properties this array if key-value-pairs defines a contact
- * @return array an array representing the contact just created or updated
- */
- public function createOrUpdate($properties) {
- return array();
- }
-
- /**
- * @return int
- */
- public function getPermissions() {
- return \OCP\Constants::PERMISSION_READ;
- }
-
- /**
- * @param object $id the unique identifier to a contact
- * @return bool successful or not
- */
- public function delete($id) {
- return false;
- }
-}
diff --git a/lib/private/files/view.php b/lib/private/files/view.php
index d4cc24ae0f5..2656e34cddf 100644
--- a/lib/private/files/view.php
+++ b/lib/private/files/view.php
@@ -46,6 +46,7 @@ use Icewind\Streams\CallbackWrapper;
use OC\Files\Mount\MoveableMount;
use OC\Files\Storage\Storage;
use OC\User\User;
+use OCP\Constants;
use OCP\Files\Cache\ICacheEntry;
use OCP\Files\FileNameTooLongException;
use OCP\Files\InvalidCharacterInPathException;
@@ -1335,7 +1336,7 @@ class View {
$data = $this->getCacheEntry($storage, $internalPath, $directory);
- if (!$data instanceof ICacheEntry || !isset($data['fileid'])) {
+ if (!$data instanceof ICacheEntry || !isset($data['fileid']) || !($data->getPermissions() && Constants::PERMISSION_READ)) {
return [];
}
@@ -1385,7 +1386,7 @@ class View {
$rootEntry = $subCache->get('');
}
- if ($rootEntry) {
+ if ($rootEntry && ($rootEntry->getPermissions() && Constants::PERMISSION_READ)) {
$relativePath = trim(substr($mountPoint, $dirLength), '/');
if ($pos = strpos($relativePath, '/')) {
//mountpoint inside subfolder add size to the correct folder
diff --git a/lib/private/security/csp/contentsecuritypolicy.php b/lib/private/security/csp/contentsecuritypolicy.php
new file mode 100644
index 00000000000..25eacfab1d6
--- /dev/null
+++ b/lib/private/security/csp/contentsecuritypolicy.php
@@ -0,0 +1,199 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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\Security\CSP;
+
+/**
+ * Class ContentSecurityPolicy extends the public class and adds getter and setters.
+ * This is necessary since we don't want to expose the setters and getters to the
+ * public API.
+ *
+ * @package OC\Security\CSP
+ */
+class ContentSecurityPolicy extends \OCP\AppFramework\Http\ContentSecurityPolicy {
+ /**
+ * @return boolean
+ */
+ public function isInlineScriptAllowed() {
+ return $this->inlineScriptAllowed;
+ }
+
+ /**
+ * @param boolean $inlineScriptAllowed
+ */
+ public function setInlineScriptAllowed($inlineScriptAllowed) {
+ $this->inlineScriptAllowed = $inlineScriptAllowed;
+ }
+
+ /**
+ * @return boolean
+ */
+ public function isEvalScriptAllowed() {
+ return $this->evalScriptAllowed;
+ }
+
+ /**
+ * @param boolean $evalScriptAllowed
+ */
+ public function setEvalScriptAllowed($evalScriptAllowed) {
+ $this->evalScriptAllowed = $evalScriptAllowed;
+ }
+
+ /**
+ * @return array
+ */
+ public function getAllowedScriptDomains() {
+ return $this->allowedScriptDomains;
+ }
+
+ /**
+ * @param array $allowedScriptDomains
+ */
+ public function setAllowedScriptDomains($allowedScriptDomains) {
+ $this->allowedScriptDomains = $allowedScriptDomains;
+ }
+
+ /**
+ * @return boolean
+ */
+ public function isInlineStyleAllowed() {
+ return $this->inlineStyleAllowed;
+ }
+
+ /**
+ * @param boolean $inlineStyleAllowed
+ */
+ public function setInlineStyleAllowed($inlineStyleAllowed) {
+ $this->inlineStyleAllowed = $inlineStyleAllowed;
+ }
+
+ /**
+ * @return array
+ */
+ public function getAllowedStyleDomains() {
+ return $this->allowedStyleDomains;
+ }
+
+ /**
+ * @param array $allowedStyleDomains
+ */
+ public function setAllowedStyleDomains($allowedStyleDomains) {
+ $this->allowedStyleDomains = $allowedStyleDomains;
+ }
+
+ /**
+ * @return array
+ */
+ public function getAllowedImageDomains() {
+ return $this->allowedImageDomains;
+ }
+
+ /**
+ * @param array $allowedImageDomains
+ */
+ public function setAllowedImageDomains($allowedImageDomains) {
+ $this->allowedImageDomains = $allowedImageDomains;
+ }
+
+ /**
+ * @return array
+ */
+ public function getAllowedConnectDomains() {
+ return $this->allowedConnectDomains;
+ }
+
+ /**
+ * @param array $allowedConnectDomains
+ */
+ public function setAllowedConnectDomains($allowedConnectDomains) {
+ $this->allowedConnectDomains = $allowedConnectDomains;
+ }
+
+ /**
+ * @return array
+ */
+ public function getAllowedMediaDomains() {
+ return $this->allowedMediaDomains;
+ }
+
+ /**
+ * @param array $allowedMediaDomains
+ */
+ public function setAllowedMediaDomains($allowedMediaDomains) {
+ $this->allowedMediaDomains = $allowedMediaDomains;
+ }
+
+ /**
+ * @return array
+ */
+ public function getAllowedObjectDomains() {
+ return $this->allowedObjectDomains;
+ }
+
+ /**
+ * @param array $allowedObjectDomains
+ */
+ public function setAllowedObjectDomains($allowedObjectDomains) {
+ $this->allowedObjectDomains = $allowedObjectDomains;
+ }
+
+ /**
+ * @return array
+ */
+ public function getAllowedFrameDomains() {
+ return $this->allowedFrameDomains;
+ }
+
+ /**
+ * @param array $allowedFrameDomains
+ */
+ public function setAllowedFrameDomains($allowedFrameDomains) {
+ $this->allowedFrameDomains = $allowedFrameDomains;
+ }
+
+ /**
+ * @return array
+ */
+ public function getAllowedFontDomains() {
+ return $this->allowedFontDomains;
+ }
+
+ /**
+ * @param array $allowedFontDomains
+ */
+ public function setAllowedFontDomains($allowedFontDomains) {
+ $this->allowedFontDomains = $allowedFontDomains;
+ }
+
+ /**
+ * @return array
+ */
+ public function getAllowedChildSrcDomains() {
+ return $this->allowedChildSrcDomains;
+ }
+
+ /**
+ * @param array $allowedChildSrcDomains
+ */
+ public function setAllowedChildSrcDomains($allowedChildSrcDomains) {
+ $this->allowedChildSrcDomains = $allowedChildSrcDomains;
+ }
+
+}
diff --git a/lib/private/security/csp/contentsecuritypolicymanager.php b/lib/private/security/csp/contentsecuritypolicymanager.php
new file mode 100644
index 00000000000..760cd36e56b
--- /dev/null
+++ b/lib/private/security/csp/contentsecuritypolicymanager.php
@@ -0,0 +1,73 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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\Security\CSP;
+
+use OCP\AppFramework\Http\ContentSecurityPolicy;
+use OCP\AppFramework\Http\EmptyContentSecurityPolicy;
+use OCP\Security\IContentSecurityPolicyManager;
+
+class ContentSecurityPolicyManager implements IContentSecurityPolicyManager {
+ /** @var ContentSecurityPolicy[] */
+ private $policies = [];
+
+ /** {@inheritdoc} */
+ public function addDefaultPolicy(EmptyContentSecurityPolicy $policy) {
+ $this->policies[] = $policy;
+ }
+
+ /**
+ * Get the configured default policy. This is not in the public namespace
+ * as it is only supposed to be used by core itself.
+ *
+ * @return ContentSecurityPolicy
+ */
+ public function getDefaultPolicy() {
+ $defaultPolicy = new \OC\Security\CSP\ContentSecurityPolicy();
+ foreach($this->policies as $policy) {
+ $defaultPolicy = $this->mergePolicies($defaultPolicy, $policy);
+ }
+ return $defaultPolicy;
+ }
+
+ /**
+ * Merges the first given policy with the second one
+ *
+ * @param ContentSecurityPolicy $defaultPolicy
+ * @param EmptyContentSecurityPolicy $originalPolicy
+ * @return ContentSecurityPolicy
+ */
+ public function mergePolicies(ContentSecurityPolicy $defaultPolicy,
+ EmptyContentSecurityPolicy $originalPolicy) {
+ foreach((object)(array)$originalPolicy as $name => $value) {
+ $setter = 'set'.ucfirst($name);
+ if(is_array($value)) {
+ $getter = 'get'.ucfirst($name);
+ $currentValues = is_array($defaultPolicy->$getter()) ? $defaultPolicy->$getter() : [];
+ $defaultPolicy->$setter(array_values(array_unique(array_merge($currentValues, $value))));
+ } elseif (is_bool($value)) {
+ $defaultPolicy->$setter($value);
+ }
+ }
+
+ return $defaultPolicy;
+ }
+}
diff --git a/lib/private/server.php b/lib/private/server.php
index d453a42d3a0..d3dbcba86ba 100644
--- a/lib/private/server.php
+++ b/lib/private/server.php
@@ -63,6 +63,7 @@ use OC\Lock\NoopLockingProvider;
use OC\Mail\Mailer;
use OC\Notification\Manager;
use OC\Security\CertificateManager;
+use OC\Security\CSP\ContentSecurityPolicyManager;
use OC\Security\Crypto;
use OC\Security\CSRF\CsrfTokenGenerator;
use OC\Security\CSRF\CsrfTokenManager;
@@ -74,6 +75,7 @@ use OC\Security\TrustedDomainHelper;
use OC\Session\CryptoWrapper;
use OC\Tagging\TagMapper;
use OCP\IServerContainer;
+use OCP\Security\IContentSecurityPolicyManager;
use Symfony\Component\EventDispatcher\EventDispatcher;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
@@ -598,6 +600,9 @@ class Server extends ServerContainer implements IServerContainer {
$sessionStorage
);
});
+ $this->registerService('ContentSecurityPolicyManager', function (Server $c) {
+ return new ContentSecurityPolicyManager();
+ });
$this->registerService('ShareManager', function(Server $c) {
$config = $c->getConfig();
$factoryClass = $config->getSystemValue('sharing.managerFactory', '\OC\Share20\ProviderFactory');
@@ -1221,6 +1226,13 @@ class Server extends ServerContainer implements IServerContainer {
}
/**
+ * @return IContentSecurityPolicyManager
+ */
+ public function getContentSecurityPolicyManager() {
+ return $this->query('ContentSecurityPolicyManager');
+ }
+
+ /**
* Not a public API as of 8.2, wait for 9.0
*
* @return \OCA\Files_External\Service\BackendService
diff --git a/lib/private/share20/defaultshareprovider.php b/lib/private/share20/defaultshareprovider.php
index 35b3f71f3de..224dddf4019 100644
--- a/lib/private/share20/defaultshareprovider.php
+++ b/lib/private/share20/defaultshareprovider.php
@@ -20,6 +20,7 @@
*/
namespace OC\Share20;
+use OCP\Files\File;
use OCP\Share\IShareProvider;
use OC\Share20\Exception\InvalidShare;
use OC\Share20\Exception\ProviderException;
@@ -251,6 +252,7 @@ class DefaultShareProvider implements IShareProvider {
/**
* Get all children of this share
+ * FIXME: remove once https://github.com/owncloud/core/pull/21660 is in
*
* @param \OCP\Share\IShare $parent
* @return IShare[]
@@ -265,12 +267,11 @@ class DefaultShareProvider implements IShareProvider {
->andWhere(
$qb->expr()->in(
'share_type',
- [
- $qb->expr()->literal(\OCP\Share::SHARE_TYPE_USER),
- $qb->expr()->literal(\OCP\Share::SHARE_TYPE_GROUP),
- $qb->expr()->literal(\OCP\Share::SHARE_TYPE_LINK),
- $qb->expr()->literal(self::SHARE_TYPE_USERGROUP),
- ]
+ $qb->createNamedParameter([
+ \OCP\Share::SHARE_TYPE_USER,
+ \OCP\Share::SHARE_TYPE_GROUP,
+ \OCP\Share::SHARE_TYPE_LINK,
+ ], IQueryBuilder::PARAM_INT_ARRAY)
)
)
->orderBy('id');
@@ -384,6 +385,63 @@ class DefaultShareProvider implements IShareProvider {
}
/**
+ * @inheritdoc
+ */
+ public function move(\OCP\Share\IShare $share, IUser $recipient) {
+ if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) {
+ // Just update the target
+ $qb = $this->dbConn->getQueryBuilder();
+ $qb->update('share')
+ ->set('file_target', $qb->createNamedParameter($share->getTarget()))
+ ->where($qb->expr()->eq('id', $qb->createNamedParameter($share->getId())))
+ ->execute();
+
+ } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
+
+ // Check if there is a usergroup share
+ $qb = $this->dbConn->getQueryBuilder();
+ $stmt = $qb->select('id')
+ ->from('share')
+ ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP)))
+ ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($recipient->getUID())))
+ ->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId())))
+ ->setMaxResults(1)
+ ->execute();
+
+ $data = $stmt->fetch();
+ $stmt->closeCursor();
+
+ if ($data === false) {
+ // No usergroup share yet. Create one.
+ $qb = $this->dbConn->getQueryBuilder();
+ $qb->insert('share')
+ ->values([
+ 'share_type' => $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP),
+ 'share_with' => $qb->createNamedParameter($recipient->getUID()),
+ 'uid_owner' => $qb->createNamedParameter($share->getShareOwner()->getUID()),
+ 'uid_initiator' => $qb->createNamedParameter($share->getSharedBy()->getUID()),
+ 'parent' => $qb->createNamedParameter($share->getId()),
+ 'item_type' => $qb->createNamedParameter($share->getNode() instanceof File ? 'file' : 'folder'),
+ 'item_source' => $qb->createNamedParameter($share->getNode()->getId()),
+ 'file_source' => $qb->createNamedParameter($share->getNode()->getId()),
+ 'file_target' => $qb->createNamedParameter($share->getTarget()),
+ 'permissions' => $qb->createNamedParameter($share->getPermissions()),
+ 'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()),
+ ])->execute();
+ } else {
+ // Already a usergroup share. Update it.
+ $qb = $this->dbConn->getQueryBuilder();
+ $qb->update('share')
+ ->set('file_target', $qb->createNamedParameter($share->getTarget()))
+ ->where($qb->expr()->eq('id', $qb->createNamedParameter($data['id'])))
+ ->execute();
+ }
+ }
+
+ return $share;
+ }
+
+ /**
* Get all shares by the given user. Sharetype and path can be used to filter.
*
* @param IUser $user
@@ -448,13 +506,9 @@ class DefaultShareProvider implements IShareProvider {
}
/**
- * Get share by id
- *
- * @param int $id
- * @return \OCP\Share\IShare
- * @throws ShareNotFound
+ * @inheritdoc
*/
- public function getShareById($id) {
+ public function getShareById($id, $recipient = null) {
$qb = $this->dbConn->getQueryBuilder();
$qb->select('*')
@@ -463,12 +517,11 @@ class DefaultShareProvider implements IShareProvider {
->andWhere(
$qb->expr()->in(
'share_type',
- [
- $qb->expr()->literal(\OCP\Share::SHARE_TYPE_USER),
- $qb->expr()->literal(\OCP\Share::SHARE_TYPE_GROUP),
- $qb->expr()->literal(\OCP\Share::SHARE_TYPE_LINK),
- $qb->expr()->literal(self::SHARE_TYPE_USERGROUP),
- ]
+ $qb->createNamedParameter([
+ \OCP\Share::SHARE_TYPE_USER,
+ \OCP\Share::SHARE_TYPE_GROUP,
+ \OCP\Share::SHARE_TYPE_LINK,
+ ], IQueryBuilder::PARAM_INT_ARRAY)
)
);
@@ -486,6 +539,11 @@ class DefaultShareProvider implements IShareProvider {
throw new ShareNotFound();
}
+ // If the recipient is set for a group share resolve to that user
+ if ($recipient !== null && $share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) {
+ $share = $this->resolveGroupShare($share, $recipient);
+ }
+
return $share;
}
@@ -518,16 +576,9 @@ class DefaultShareProvider implements IShareProvider {
}
/**
- * Get shared with the given user
- *
- * @param IUser $user get shares where this user is the recipient
- * @param int $shareType \OCP\Share::SHARE_TYPE_USER or \OCP\Share::SHARE_TYPE_GROUP are supported
- * @param int $limit The maximum number of shares, -1 for all
- * @param int $offset
- * @return IShare[]
- * @throws BackendError
+ * @inheritdoc
*/
- public function getSharedWith(IUser $user, $shareType, $limit, $offset) {
+ public function getSharedWith(IUser $user, $shareType, $node, $limit, $offset) {
/** @var Share[] $shares */
$shares = [];
@@ -549,6 +600,11 @@ class DefaultShareProvider implements IShareProvider {
$qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_USER)));
$qb->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($user->getUID())));
+ // Filter by node if provided
+ if ($node !== null) {
+ $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
+ }
+
$cursor = $qb->execute();
while($data = $cursor->fetch()) {
@@ -581,9 +637,14 @@ class DefaultShareProvider implements IShareProvider {
$qb->setMaxResults($limit - count($shares));
}
+ // Filter by node if provided
+ if ($node !== null) {
+ $qb->andWhere($qb->expr()->eq('file_source', $qb->createNamedParameter($node->getId())));
+ }
+
$groups = array_map(function(IGroup $group) { return $group->getGID(); }, $groups);
- $qb->where($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)));
+ $qb->andWhere($qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share::SHARE_TYPE_GROUP)));
$qb->andWhere($qb->expr()->in('share_with', $qb->createNamedParameter(
$groups,
IQueryBuilder::PARAM_STR_ARRAY
diff --git a/lib/private/share20/manager.php b/lib/private/share20/manager.php
index d6245f4beac..b22b81bb404 100644
--- a/lib/private/share20/manager.php
+++ b/lib/private/share20/manager.php
@@ -21,6 +21,7 @@
namespace OC\Share20;
+use OCP\Files\Node;
use OCP\Share\IManager;
use OCP\Share\IProviderFactory;
use OC\Share20\Exception\BackendError;
@@ -216,7 +217,9 @@ class Manager implements IManager {
* @return \DateTime|null The expiration date or null if $expireDate was null and it is not required
* @throws \OC\HintException
*/
- protected function validateExpirationDate($expirationDate) {
+ protected function validateExpirationDate(\OCP\Share\IShare $share) {
+
+ $expirationDate = $share->getExpirationDate();
if ($expirationDate !== null) {
//Make sure the expiration date is a date
@@ -243,18 +246,30 @@ class Manager implements IManager {
$message = $this->l->t('Cannot set expiration date more than %s days in the future', [$this->shareApiLinkDefaultExpireDays()]);
throw new \OC\HintException($message, $message, 404);
}
-
- return $expirationDate;
}
// If expiredate is empty set a default one if there is a default
if ($expirationDate === null && $this->shareApiLinkDefaultExpireDate()) {
- $date = new \DateTime();
- $date->setTime(0,0,0);
- $date->add(new \DateInterval('P'.$this->shareApiLinkDefaultExpireDays().'D'));
- return $date;
+ $expirationDate = new \DateTime();
+ $expirationDate->setTime(0,0,0);
+ $expirationDate->add(new \DateInterval('P'.$this->shareApiLinkDefaultExpireDays().'D'));
+ }
+
+ $accepted = true;
+ $message = '';
+ \OCP\Util::emitHook('\OC\Share', 'verifyExpirationDate', [
+ 'expirationDate' => &$expirationDate,
+ 'accepted' => &$accepted,
+ 'message' => &$message,
+ 'passwordSet' => $share->getPassword() === null,
+ ]);
+
+ if (!$accepted) {
+ throw new \Exception($message);
}
+ $share->setExpirationDate($expirationDate);
+
return $expirationDate;
}
@@ -435,7 +450,7 @@ class Manager implements IManager {
);
//Verify the expiration date
- $share->setExpirationDate($this->validateExpirationDate($share->getExpirationDate()));
+ $this->validateExpirationDate($share);
//Verify the password
$this->verifyPassword($share->getPassword());
@@ -572,7 +587,7 @@ class Manager implements IManager {
if ($share->getExpirationDate() !== $originalShare->getExpirationDate()) {
//Verify the expiration date
- $share->setExpirationDate($this->validateExpirationDate($share->getExpirationDate()));
+ $this->validateExpirationDate($share);
$expirationDateUpdated = true;
}
}
@@ -597,6 +612,7 @@ class Manager implements IManager {
/**
* Delete all the children of this share
+ * FIXME: remove once https://github.com/owncloud/core/pull/21660 is in
*
* @param \OCP\Share\IShare $share
* @return \OCP\Share\IShare[] List of deleted shares
@@ -699,6 +715,25 @@ class Manager implements IManager {
}
/**
+ * @inheritdoc
+ */
+ public function moveShare(\OCP\Share\IShare $share, IUser $recipient) {
+ if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) {
+ throw new \InvalidArgumentException('Can\'t change target of link share');
+ }
+
+ if (($share->getShareType() === \OCP\Share::SHARE_TYPE_USER && $share->getSharedWith() !== $recipient) ||
+ ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP && !$share->getSharedWith()->inGroup($recipient))) {
+ throw new \InvalidArgumentException('Invalid recipient');
+ }
+
+ list($providerId, ) = $this->splitFullId($share->getId());
+ $provider = $this->factory->getProvider($providerId);
+
+ $provider->move($share, $recipient);
+ }
+
+ /**
* Get shares shared by (initiated) by the provided user.
*
* @param IUser $user
@@ -722,29 +757,18 @@ class Manager implements IManager {
}
/**
- * Get shares shared with $user.
- *
- * @param IUser $user
- * @param int $shareType
- * @param int $limit The maximum number of shares returned, -1 for all
- * @param int $offset
- * @return \OCP\Share\IShare[]
+ * @inheritdoc
*/
- public function getSharedWith(IUser $user, $shareType, $limit = 50, $offset = 0) {
+ public function getSharedWith(IUser $user, $shareType, $node = null, $limit = 50, $offset = 0) {
$provider = $this->factory->getProviderForType($shareType);
- return $provider->getSharedWith($user, $shareType, $limit, $offset);
+ return $provider->getSharedWith($user, $shareType, $node, $limit, $offset);
}
/**
- * Retrieve a share by the share id
- *
- * @param string $id
- * @return Share
- *
- * @throws ShareNotFound
+ * @inheritdoc
*/
- public function getShareById($id) {
+ public function getShareById($id, $recipient = null) {
if ($id === null) {
throw new ShareNotFound();
}
@@ -752,7 +776,7 @@ class Manager implements IManager {
list($providerId, $id) = $this->splitFullId($id);
$provider = $this->factory->getProvider($providerId);
- $share = $provider->getShareById($id);
+ $share = $provider->getShareById($id, $recipient);
return $share;
}
@@ -810,7 +834,9 @@ class Manager implements IManager {
}
if (!empty($newHash)) {
- //TODO update hash!
+ $share->setPassword($newHash);
+ $provider = $this->factory->getProviderForType($share->getShareType());
+ $provider->update($share);
}
return true;
diff --git a/lib/private/systemtag/managerfactory.php b/lib/private/systemtag/managerfactory.php
index 7b7b9558b04..7ad4f922600 100644
--- a/lib/private/systemtag/managerfactory.php
+++ b/lib/private/systemtag/managerfactory.php
@@ -20,11 +20,10 @@
*/
namespace OC\SystemTag;
-use OCP\SystemTag\ISystemTagManagerFactory;
-use OCP\SystemTag\ISystemTagManager;
-use OC\SystemTag\SystemTagManager;
-use OC\SystemTag\SystemTagObjectMapper;
use OCP\IServerContainer;
+use OCP\SystemTag\ISystemTagManager;
+use OCP\SystemTag\ISystemTagManagerFactory;
+use OCP\SystemTag\ISystemTagObjectMapper;
/**
* Default factory class for system tag managers
@@ -58,7 +57,8 @@ class ManagerFactory implements ISystemTagManagerFactory {
*/
public function getManager() {
return new SystemTagManager(
- $this->serverContainer->getDatabaseConnection()
+ $this->serverContainer->getDatabaseConnection(),
+ $this->serverContainer->getEventDispatcher()
);
}
@@ -72,7 +72,8 @@ class ManagerFactory implements ISystemTagManagerFactory {
public function getObjectMapper() {
return new SystemTagObjectMapper(
$this->serverContainer->getDatabaseConnection(),
- $this->getManager()
+ $this->getManager(),
+ $this->serverContainer->getEventDispatcher()
);
}
}
diff --git a/lib/private/systemtag/systemtagmanager.php b/lib/private/systemtag/systemtagmanager.php
index af912a74936..76a60a91328 100644
--- a/lib/private/systemtag/systemtagmanager.php
+++ b/lib/private/systemtag/systemtagmanager.php
@@ -26,17 +26,20 @@ use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
use OCP\SystemTag\ISystemTagManager;
+use OCP\SystemTag\ManagerEvent;
use OCP\SystemTag\TagAlreadyExistsException;
use OCP\SystemTag\TagNotFoundException;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
class SystemTagManager implements ISystemTagManager {
const TAG_TABLE = 'systemtag';
- /**
- * @var IDBConnection
- */
- private $connection;
+ /** @var IDBConnection */
+ protected $connection;
+
+ /** @var EventDispatcherInterface */
+ protected $dispatcher;
/**
* Prepared query for selecting tags directly
@@ -46,12 +49,14 @@ class SystemTagManager implements ISystemTagManager {
private $selectTagQuery;
/**
- * Constructor.
- *
- * @param IDBConnection $connection database connection
- */
- public function __construct(IDBConnection $connection) {
+ * Constructor.
+ *
+ * @param IDBConnection $connection database connection
+ * @param EventDispatcherInterface $dispatcher
+ */
+ public function __construct(IDBConnection $connection, EventDispatcherInterface $dispatcher) {
$this->connection = $connection;
+ $this->dispatcher = $dispatcher;
$query = $this->connection->getQueryBuilder();
$this->selectTagQuery = $query->select('*')
@@ -190,14 +195,20 @@ class SystemTagManager implements ISystemTagManager {
);
}
- $tagId = $this->connection->lastInsertId('*PREFIX*' . self::TAG_TABLE);
+ $tagId = $query->getLastInsertId();
- return new SystemTag(
+ $tag = new SystemTag(
(int)$tagId,
$tagName,
(bool)$userVisible,
(bool)$userAssignable
);
+
+ $this->dispatcher->dispatch(ManagerEvent::EVENT_CREATE, new ManagerEvent(
+ ManagerEvent::EVENT_CREATE, $tag
+ ));
+
+ return $tag;
}
/**
@@ -207,6 +218,22 @@ class SystemTagManager implements ISystemTagManager {
$userVisible = (int)$userVisible;
$userAssignable = (int)$userAssignable;
+ try {
+ $tags = $this->getTagsByIds($tagId);
+ } catch (TagNotFoundException $e) {
+ throw new TagNotFoundException(
+ 'Tag does not exist', 0, null, [$tagId]
+ );
+ }
+
+ $beforeUpdate = array_shift($tags);
+ $afterUpdate = new SystemTag(
+ (int) $tagId,
+ $tagName,
+ (bool) $userVisible,
+ (bool) $userAssignable
+ );
+
$query = $this->connection->getQueryBuilder();
$query->update(self::TAG_TABLE)
->set('name', $query->createParameter('name'))
@@ -231,6 +258,10 @@ class SystemTagManager implements ISystemTagManager {
$e
);
}
+
+ $this->dispatcher->dispatch(ManagerEvent::EVENT_UPDATE, new ManagerEvent(
+ ManagerEvent::EVENT_UPDATE, $afterUpdate, $beforeUpdate
+ ));
}
/**
@@ -242,10 +273,21 @@ class SystemTagManager implements ISystemTagManager {
}
$tagNotFoundException = null;
+ $tags = [];
try {
- $this->getTagsByIds($tagIds);
+ $tags = $this->getTagsByIds($tagIds);
} catch (TagNotFoundException $e) {
$tagNotFoundException = $e;
+
+ // Get existing tag objects for the hooks later
+ $existingTags = array_diff($tagIds, $tagNotFoundException->getMissingTags());
+ if (!empty($existingTags)) {
+ try {
+ $tags = $this->getTagsByIds($existingTags);
+ } catch (TagNotFoundException $e) {
+ // Ignore further errors...
+ }
+ }
}
// delete relationships first
@@ -261,6 +303,12 @@ class SystemTagManager implements ISystemTagManager {
->setParameter('tagids', $tagIds, IQueryBuilder::PARAM_INT_ARRAY)
->execute();
+ foreach ($tags as $tag) {
+ $this->dispatcher->dispatch(ManagerEvent::EVENT_DELETE, new ManagerEvent(
+ ManagerEvent::EVENT_DELETE, $tag
+ ));
+ }
+
if ($tagNotFoundException !== null) {
throw new TagNotFoundException(
'Tag id(s) not found', 0, $tagNotFoundException, $tagNotFoundException->getMissingTags()
diff --git a/lib/private/systemtag/systemtagobjectmapper.php b/lib/private/systemtag/systemtagobjectmapper.php
index 458b558ad97..1efb4f0f6e0 100644
--- a/lib/private/systemtag/systemtagobjectmapper.php
+++ b/lib/private/systemtag/systemtagobjectmapper.php
@@ -28,31 +28,34 @@ use OCP\IDBConnection;
use OCP\SystemTag\ISystemTag;
use OCP\SystemTag\ISystemTagManager;
use OCP\SystemTag\ISystemTagObjectMapper;
+use OCP\SystemTag\MapperEvent;
use OCP\SystemTag\TagNotFoundException;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
class SystemTagObjectMapper implements ISystemTagObjectMapper {
const RELATION_TABLE = 'systemtag_object_mapping';
- /**
- * @var ISystemTagManager
- */
- private $tagManager;
+ /** @var ISystemTagManager */
+ protected $tagManager;
- /**
- * @var IDBConnection
- */
- private $connection;
+ /** @var IDBConnection */
+ protected $connection;
+
+ /** @var EventDispatcherInterface */
+ protected $dispatcher;
/**
* Constructor.
*
* @param IDBConnection $connection database connection
* @param ISystemTagManager $tagManager system tag manager
+ * @param EventDispatcherInterface $dispatcher
*/
- public function __construct(IDBConnection $connection, ISystemTagManager $tagManager) {
+ public function __construct(IDBConnection $connection, ISystemTagManager $tagManager, EventDispatcherInterface $dispatcher) {
$this->connection = $connection;
$this->tagManager = $tagManager;
+ $this->dispatcher = $dispatcher;
}
/**
@@ -143,6 +146,13 @@ class SystemTagObjectMapper implements ISystemTagObjectMapper {
// ignore existing relations
}
}
+
+ $this->dispatcher->dispatch(MapperEvent::EVENT_ASSIGN, new MapperEvent(
+ MapperEvent::EVENT_ASSIGN,
+ $objectType,
+ $objId,
+ $tagIds
+ ));
}
/**
@@ -164,6 +174,13 @@ class SystemTagObjectMapper implements ISystemTagObjectMapper {
->setParameter('objecttype', $objectType)
->setParameter('tagids', $tagIds, IQueryBuilder::PARAM_INT_ARRAY)
->execute();
+
+ $this->dispatcher->dispatch(MapperEvent::EVENT_UNASSIGN, new MapperEvent(
+ MapperEvent::EVENT_UNASSIGN,
+ $objectType,
+ $objId,
+ $tagIds
+ ));
}
/**
diff --git a/lib/private/templatelayout.php b/lib/private/templatelayout.php
index 7f17ab15335..bc66c0dfb1e 100644
--- a/lib/private/templatelayout.php
+++ b/lib/private/templatelayout.php
@@ -130,7 +130,7 @@ class TemplateLayout extends \OC_Template {
$this->assign('user_displayname', $userDisplayName);
$this->assign('user_uid', \OC_User::getUser());
$this->assign('appsmanagement_active', $appsMgmtActive);
- $this->assign('enableAvatars', $this->config->getSystemValue('enable_avatars', true));
+ $this->assign('enableAvatars', $this->config->getSystemValue('enable_avatars', true) === true);
if (\OC_User::getUser() === false) {
$this->assign('userAvatarSet', false);
diff --git a/lib/private/user/user.php b/lib/private/user/user.php
index e8ce6b8cc87..5e556575118 100644
--- a/lib/private/user/user.php
+++ b/lib/private/user/user.php
@@ -211,6 +211,7 @@ class User implements IUser {
\OC\Files\Cache\Storage::remove('home::' . $this->uid);
\OC::$server->getCommentsManager()->deleteReferencesOfActor('user', $this->uid);
+ \OC::$server->getCommentsManager()->deleteReadMarksFromUser($this);
}
if ($this->emitter) {
diff --git a/lib/private/util.php b/lib/private/util.php
index 64695d95a03..28541eff773 100644
--- a/lib/private/util.php
+++ b/lib/private/util.php
@@ -152,6 +152,16 @@ class OC_Util {
return $storage;
});
+ \OC\Files\Filesystem::addStorageWrapper('enable_sharing', function ($mountPoint, \OCP\Files\Storage $storage, \OCP\Files\Mount\IMountPoint $mount) {
+ if (!$mount->getOption('enable_sharing', true)) {
+ return new \OC\Files\Storage\Wrapper\PermissionsMask([
+ 'storage' => $storage,
+ 'mask' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_SHARE
+ ]);
+ }
+ return $storage;
+ });
+
// install storage availability wrapper, before most other wrappers
\OC\Files\Filesystem::addStorageWrapper('oc_availability', function ($mountPoint, $storage) {
if (!$storage->isLocal()) {
diff --git a/lib/public/appframework/controller.php b/lib/public/appframework/controller.php
index 973c9044684..c3744683300 100644
--- a/lib/public/appframework/controller.php
+++ b/lib/public/appframework/controller.php
@@ -72,7 +72,7 @@ abstract class Controller {
* @since 6.0.0 - parameter $appName was added in 7.0.0 - parameter $app was removed in 7.0.0
*/
public function __construct($appName,
- IRequest $request){
+ IRequest $request) {
$this->appName = $appName;
$this->request = $request;
diff --git a/lib/public/appframework/http/contentsecuritypolicy.php b/lib/public/appframework/http/contentsecuritypolicy.php
index 35da4f05e80..7762ca809a2 100644
--- a/lib/public/appframework/http/contentsecuritypolicy.php
+++ b/lib/public/appframework/http/contentsecuritypolicy.php
@@ -38,17 +38,17 @@ use OCP\AppFramework\Http;
* @package OCP\AppFramework\Http
* @since 8.1.0
*/
-class ContentSecurityPolicy {
+class ContentSecurityPolicy extends EmptyContentSecurityPolicy {
/** @var bool Whether inline JS snippets are allowed */
- private $inlineScriptAllowed = false;
+ protected $inlineScriptAllowed = false;
/**
* @var bool Whether eval in JS scripts is allowed
* TODO: Disallow per default
* @link https://github.com/owncloud/core/issues/11925
*/
- private $evalScriptAllowed = true;
+ protected $evalScriptAllowed = true;
/** @var array Domains from which scripts can get loaded */
- private $allowedScriptDomains = [
+ protected $allowedScriptDomains = [
'\'self\'',
];
/**
@@ -56,342 +56,33 @@ class ContentSecurityPolicy {
* TODO: Disallow per default
* @link https://github.com/owncloud/core/issues/13458
*/
- private $inlineStyleAllowed = true;
+ protected $inlineStyleAllowed = true;
/** @var array Domains from which CSS can get loaded */
- private $allowedStyleDomains = [
+ protected $allowedStyleDomains = [
'\'self\'',
];
/** @var array Domains from which images can get loaded */
- private $allowedImageDomains = [
+ protected $allowedImageDomains = [
'\'self\'',
'data:',
'blob:',
];
/** @var array Domains to which connections can be done */
- private $allowedConnectDomains = [
+ protected $allowedConnectDomains = [
'\'self\'',
];
/** @var array Domains from which media elements can be loaded */
- private $allowedMediaDomains = [
+ protected $allowedMediaDomains = [
'\'self\'',
];
/** @var array Domains from which object elements can be loaded */
- private $allowedObjectDomains = [];
+ protected $allowedObjectDomains = [];
/** @var array Domains from which iframes can be loaded */
- private $allowedFrameDomains = [];
+ protected $allowedFrameDomains = [];
/** @var array Domains from which fonts can be loaded */
- private $allowedFontDomains = [
+ protected $allowedFontDomains = [
'\'self\'',
];
/** @var array Domains from which web-workers and nested browsing content can load elements */
- private $allowedChildSrcDomains = [];
-
- /**
- * Whether inline JavaScript snippets are allowed or forbidden
- * @param bool $state
- * @return $this
- * @since 8.1.0
- */
- public function allowInlineScript($state = false) {
- $this->inlineScriptAllowed = $state;
- return $this;
- }
-
- /**
- * Whether eval in JavaScript is allowed or forbidden
- * @param bool $state
- * @return $this
- * @since 8.1.0
- */
- public function allowEvalScript($state = true) {
- $this->evalScriptAllowed = $state;
- return $this;
- }
-
- /**
- * Allows to execute JavaScript files from a specific domain. Use * to
- * allow JavaScript from all domains.
- * @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized.
- * @return $this
- * @since 8.1.0
- */
- public function addAllowedScriptDomain($domain) {
- $this->allowedScriptDomains[] = $domain;
- return $this;
- }
-
- /**
- * Remove the specified allowed script domain from the allowed domains.
- *
- * @param string $domain
- * @return $this
- * @since 8.1.0
- */
- public function disallowScriptDomain($domain) {
- $this->allowedScriptDomains = array_diff($this->allowedScriptDomains, [$domain]);
- return $this;
- }
-
- /**
- * Whether inline CSS snippets are allowed or forbidden
- * @param bool $state
- * @return $this
- * @since 8.1.0
- */
- public function allowInlineStyle($state = true) {
- $this->inlineStyleAllowed = $state;
- return $this;
- }
-
- /**
- * Allows to execute CSS files from a specific domain. Use * to allow
- * CSS from all domains.
- * @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized.
- * @return $this
- * @since 8.1.0
- */
- public function addAllowedStyleDomain($domain) {
- $this->allowedStyleDomains[] = $domain;
- return $this;
- }
-
- /**
- * Remove the specified allowed style domain from the allowed domains.
- *
- * @param string $domain
- * @return $this
- * @since 8.1.0
- */
- public function disallowStyleDomain($domain) {
- $this->allowedStyleDomains = array_diff($this->allowedStyleDomains, [$domain]);
- return $this;
- }
-
- /**
- * Allows using fonts from a specific domain. Use * to allow
- * fonts from all domains.
- * @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized.
- * @return $this
- * @since 8.1.0
- */
- public function addAllowedFontDomain($domain) {
- $this->allowedFontDomains[] = $domain;
- return $this;
- }
-
- /**
- * Remove the specified allowed font domain from the allowed domains.
- *
- * @param string $domain
- * @return $this
- * @since 8.1.0
- */
- public function disallowFontDomain($domain) {
- $this->allowedFontDomains = array_diff($this->allowedFontDomains, [$domain]);
- return $this;
- }
-
- /**
- * Allows embedding images from a specific domain. Use * to allow
- * images from all domains.
- * @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized.
- * @return $this
- * @since 8.1.0
- */
- public function addAllowedImageDomain($domain) {
- $this->allowedImageDomains[] = $domain;
- return $this;
- }
-
- /**
- * Remove the specified allowed image domain from the allowed domains.
- *
- * @param string $domain
- * @return $this
- * @since 8.1.0
- */
- public function disallowImageDomain($domain) {
- $this->allowedImageDomains = array_diff($this->allowedImageDomains, [$domain]);
- return $this;
- }
-
- /**
- * To which remote domains the JS connect to.
- * @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized.
- * @return $this
- * @since 8.1.0
- */
- public function addAllowedConnectDomain($domain) {
- $this->allowedConnectDomains[] = $domain;
- return $this;
- }
-
- /**
- * Remove the specified allowed connect domain from the allowed domains.
- *
- * @param string $domain
- * @return $this
- * @since 8.1.0
- */
- public function disallowConnectDomain($domain) {
- $this->allowedConnectDomains = array_diff($this->allowedConnectDomains, [$domain]);
- return $this;
- }
-
- /**
- * From which domains media elements can be embedded.
- * @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized.
- * @return $this
- * @since 8.1.0
- */
- public function addAllowedMediaDomain($domain) {
- $this->allowedMediaDomains[] = $domain;
- return $this;
- }
-
- /**
- * Remove the specified allowed media domain from the allowed domains.
- *
- * @param string $domain
- * @return $this
- * @since 8.1.0
- */
- public function disallowMediaDomain($domain) {
- $this->allowedMediaDomains = array_diff($this->allowedMediaDomains, [$domain]);
- return $this;
- }
-
- /**
- * From which domains objects such as <object>, <embed> or <applet> are executed
- * @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized.
- * @return $this
- * @since 8.1.0
- */
- public function addAllowedObjectDomain($domain) {
- $this->allowedObjectDomains[] = $domain;
- return $this;
- }
-
- /**
- * Remove the specified allowed object domain from the allowed domains.
- *
- * @param string $domain
- * @return $this
- * @since 8.1.0
- */
- public function disallowObjectDomain($domain) {
- $this->allowedObjectDomains = array_diff($this->allowedObjectDomains, [$domain]);
- return $this;
- }
-
- /**
- * Which domains can be embedded in an iframe
- * @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized.
- * @return $this
- * @since 8.1.0
- */
- public function addAllowedFrameDomain($domain) {
- $this->allowedFrameDomains[] = $domain;
- return $this;
- }
-
- /**
- * Remove the specified allowed frame domain from the allowed domains.
- *
- * @param string $domain
- * @return $this
- * @since 8.1.0
- */
- public function disallowFrameDomain($domain) {
- $this->allowedFrameDomains = array_diff($this->allowedFrameDomains, [$domain]);
- return $this;
- }
-
- /**
- * Domains from which web-workers and nested browsing content can load elements
- * @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized.
- * @return $this
- * @since 8.1.0
- */
- public function addAllowedChildSrcDomain($domain) {
- $this->allowedChildSrcDomains[] = $domain;
- return $this;
- }
-
- /**
- * Remove the specified allowed child src domain from the allowed domains.
- *
- * @param string $domain
- * @return $this
- * @since 8.1.0
- */
- public function disallowChildSrcDomain($domain) {
- $this->allowedChildSrcDomains = array_diff($this->allowedChildSrcDomains, [$domain]);
- return $this;
- }
-
- /**
- * Get the generated Content-Security-Policy as a string
- * @return string
- * @since 8.1.0
- */
- public function buildPolicy() {
- $policy = "default-src 'none';";
-
- if(!empty($this->allowedScriptDomains)) {
- $policy .= 'script-src ' . implode(' ', $this->allowedScriptDomains);
- if($this->inlineScriptAllowed) {
- $policy .= ' \'unsafe-inline\'';
- }
- if($this->evalScriptAllowed) {
- $policy .= ' \'unsafe-eval\'';
- }
- $policy .= ';';
- }
-
- if(!empty($this->allowedStyleDomains)) {
- $policy .= 'style-src ' . implode(' ', $this->allowedStyleDomains);
- if($this->inlineStyleAllowed) {
- $policy .= ' \'unsafe-inline\'';
- }
- $policy .= ';';
- }
-
- if(!empty($this->allowedImageDomains)) {
- $policy .= 'img-src ' . implode(' ', $this->allowedImageDomains);
- $policy .= ';';
- }
-
- if(!empty($this->allowedFontDomains)) {
- $policy .= 'font-src ' . implode(' ', $this->allowedFontDomains);
- $policy .= ';';
- }
-
- if(!empty($this->allowedConnectDomains)) {
- $policy .= 'connect-src ' . implode(' ', $this->allowedConnectDomains);
- $policy .= ';';
- }
-
- if(!empty($this->allowedMediaDomains)) {
- $policy .= 'media-src ' . implode(' ', $this->allowedMediaDomains);
- $policy .= ';';
- }
-
- if(!empty($this->allowedObjectDomains)) {
- $policy .= 'object-src ' . implode(' ', $this->allowedObjectDomains);
- $policy .= ';';
- }
-
- if(!empty($this->allowedFrameDomains)) {
- $policy .= 'frame-src ' . implode(' ', $this->allowedFrameDomains);
- $policy .= ';';
- }
-
- if(!empty($this->allowedChildSrcDomains)) {
- $policy .= 'child-src ' . implode(' ', $this->allowedChildSrcDomains);
- $policy .= ';';
- }
-
- return rtrim($policy, ';');
- }
+ protected $allowedChildSrcDomains = [];
}
diff --git a/lib/public/appframework/http/emptycontentsecuritypolicy.php b/lib/public/appframework/http/emptycontentsecuritypolicy.php
new file mode 100644
index 00000000000..33860dcdb0f
--- /dev/null
+++ b/lib/public/appframework/http/emptycontentsecuritypolicy.php
@@ -0,0 +1,387 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author sualko <klaus@jsxc.org>
+ *
+ * @copyright Copyright (c) 2016, 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 OCP\AppFramework\Http;
+
+use OCP\AppFramework\Http;
+
+/**
+ * Class EmptyContentSecurityPolicy is a simple helper which allows applications
+ * to modify the Content-Security-Policy sent by ownCloud. Per default the policy
+ * is forbidding everything.
+ *
+ * As alternative with sane exemptions look at ContentSecurityPolicy
+ *
+ * @see \OCP\AppFramework\Http\ContentSecurityPolicy
+ * @package OCP\AppFramework\Http
+ * @since 9.0.0
+ */
+class EmptyContentSecurityPolicy {
+ /** @var bool Whether inline JS snippets are allowed */
+ protected $inlineScriptAllowed = null;
+ /**
+ * @var bool Whether eval in JS scripts is allowed
+ * TODO: Disallow per default
+ * @link https://github.com/owncloud/core/issues/11925
+ */
+ protected $evalScriptAllowed = null;
+ /** @var array Domains from which scripts can get loaded */
+ protected $allowedScriptDomains = null;
+ /**
+ * @var bool Whether inline CSS is allowed
+ * TODO: Disallow per default
+ * @link https://github.com/owncloud/core/issues/13458
+ */
+ protected $inlineStyleAllowed = null;
+ /** @var array Domains from which CSS can get loaded */
+ protected $allowedStyleDomains = null;
+ /** @var array Domains from which images can get loaded */
+ protected $allowedImageDomains = null;
+ /** @var array Domains to which connections can be done */
+ protected $allowedConnectDomains = null;
+ /** @var array Domains from which media elements can be loaded */
+ protected $allowedMediaDomains = null;
+ /** @var array Domains from which object elements can be loaded */
+ protected $allowedObjectDomains = null;
+ /** @var array Domains from which iframes can be loaded */
+ protected $allowedFrameDomains = null;
+ /** @var array Domains from which fonts can be loaded */
+ protected $allowedFontDomains = null;
+ /** @var array Domains from which web-workers and nested browsing content can load elements */
+ protected $allowedChildSrcDomains = null;
+
+ /**
+ * Whether inline JavaScript snippets are allowed or forbidden
+ * @param bool $state
+ * @return $this
+ * @since 8.1.0
+ */
+ public function allowInlineScript($state = false) {
+ $this->inlineScriptAllowed = $state;
+ return $this;
+ }
+
+ /**
+ * Whether eval in JavaScript is allowed or forbidden
+ * @param bool $state
+ * @return $this
+ * @since 8.1.0
+ */
+ public function allowEvalScript($state = true) {
+ $this->evalScriptAllowed = $state;
+ return $this;
+ }
+
+ /**
+ * Allows to execute JavaScript files from a specific domain. Use * to
+ * allow JavaScript from all domains.
+ * @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized.
+ * @return $this
+ * @since 8.1.0
+ */
+ public function addAllowedScriptDomain($domain) {
+ $this->allowedScriptDomains[] = $domain;
+ return $this;
+ }
+
+ /**
+ * Remove the specified allowed script domain from the allowed domains.
+ *
+ * @param string $domain
+ * @return $this
+ * @since 8.1.0
+ */
+ public function disallowScriptDomain($domain) {
+ $this->allowedScriptDomains = array_diff($this->allowedScriptDomains, [$domain]);
+ return $this;
+ }
+
+ /**
+ * Whether inline CSS snippets are allowed or forbidden
+ * @param bool $state
+ * @return $this
+ * @since 8.1.0
+ */
+ public function allowInlineStyle($state = true) {
+ $this->inlineStyleAllowed = $state;
+ return $this;
+ }
+
+ /**
+ * Allows to execute CSS files from a specific domain. Use * to allow
+ * CSS from all domains.
+ * @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized.
+ * @return $this
+ * @since 8.1.0
+ */
+ public function addAllowedStyleDomain($domain) {
+ $this->allowedStyleDomains[] = $domain;
+ return $this;
+ }
+
+ /**
+ * Remove the specified allowed style domain from the allowed domains.
+ *
+ * @param string $domain
+ * @return $this
+ * @since 8.1.0
+ */
+ public function disallowStyleDomain($domain) {
+ $this->allowedStyleDomains = array_diff($this->allowedStyleDomains, [$domain]);
+ return $this;
+ }
+
+ /**
+ * Allows using fonts from a specific domain. Use * to allow
+ * fonts from all domains.
+ * @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized.
+ * @return $this
+ * @since 8.1.0
+ */
+ public function addAllowedFontDomain($domain) {
+ $this->allowedFontDomains[] = $domain;
+ return $this;
+ }
+
+ /**
+ * Remove the specified allowed font domain from the allowed domains.
+ *
+ * @param string $domain
+ * @return $this
+ * @since 8.1.0
+ */
+ public function disallowFontDomain($domain) {
+ $this->allowedFontDomains = array_diff($this->allowedFontDomains, [$domain]);
+ return $this;
+ }
+
+ /**
+ * Allows embedding images from a specific domain. Use * to allow
+ * images from all domains.
+ * @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized.
+ * @return $this
+ * @since 8.1.0
+ */
+ public function addAllowedImageDomain($domain) {
+ $this->allowedImageDomains[] = $domain;
+ return $this;
+ }
+
+ /**
+ * Remove the specified allowed image domain from the allowed domains.
+ *
+ * @param string $domain
+ * @return $this
+ * @since 8.1.0
+ */
+ public function disallowImageDomain($domain) {
+ $this->allowedImageDomains = array_diff($this->allowedImageDomains, [$domain]);
+ return $this;
+ }
+
+ /**
+ * To which remote domains the JS connect to.
+ * @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized.
+ * @return $this
+ * @since 8.1.0
+ */
+ public function addAllowedConnectDomain($domain) {
+ $this->allowedConnectDomains[] = $domain;
+ return $this;
+ }
+
+ /**
+ * Remove the specified allowed connect domain from the allowed domains.
+ *
+ * @param string $domain
+ * @return $this
+ * @since 8.1.0
+ */
+ public function disallowConnectDomain($domain) {
+ $this->allowedConnectDomains = array_diff($this->allowedConnectDomains, [$domain]);
+ return $this;
+ }
+
+ /**
+ * From which domains media elements can be embedded.
+ * @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized.
+ * @return $this
+ * @since 8.1.0
+ */
+ public function addAllowedMediaDomain($domain) {
+ $this->allowedMediaDomains[] = $domain;
+ return $this;
+ }
+
+ /**
+ * Remove the specified allowed media domain from the allowed domains.
+ *
+ * @param string $domain
+ * @return $this
+ * @since 8.1.0
+ */
+ public function disallowMediaDomain($domain) {
+ $this->allowedMediaDomains = array_diff($this->allowedMediaDomains, [$domain]);
+ return $this;
+ }
+
+ /**
+ * From which domains objects such as <object>, <embed> or <applet> are executed
+ * @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized.
+ * @return $this
+ * @since 8.1.0
+ */
+ public function addAllowedObjectDomain($domain) {
+ $this->allowedObjectDomains[] = $domain;
+ return $this;
+ }
+
+ /**
+ * Remove the specified allowed object domain from the allowed domains.
+ *
+ * @param string $domain
+ * @return $this
+ * @since 8.1.0
+ */
+ public function disallowObjectDomain($domain) {
+ $this->allowedObjectDomains = array_diff($this->allowedObjectDomains, [$domain]);
+ return $this;
+ }
+
+ /**
+ * Which domains can be embedded in an iframe
+ * @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized.
+ * @return $this
+ * @since 8.1.0
+ */
+ public function addAllowedFrameDomain($domain) {
+ $this->allowedFrameDomains[] = $domain;
+ return $this;
+ }
+
+ /**
+ * Remove the specified allowed frame domain from the allowed domains.
+ *
+ * @param string $domain
+ * @return $this
+ * @since 8.1.0
+ */
+ public function disallowFrameDomain($domain) {
+ $this->allowedFrameDomains = array_diff($this->allowedFrameDomains, [$domain]);
+ return $this;
+ }
+
+ /**
+ * Domains from which web-workers and nested browsing content can load elements
+ * @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized.
+ * @return $this
+ * @since 8.1.0
+ */
+ public function addAllowedChildSrcDomain($domain) {
+ $this->allowedChildSrcDomains[] = $domain;
+ return $this;
+ }
+
+ /**
+ * Remove the specified allowed child src domain from the allowed domains.
+ *
+ * @param string $domain
+ * @return $this
+ * @since 8.1.0
+ */
+ public function disallowChildSrcDomain($domain) {
+ $this->allowedChildSrcDomains = array_diff($this->allowedChildSrcDomains, [$domain]);
+ return $this;
+ }
+
+ /**
+ * Get the generated Content-Security-Policy as a string
+ * @return string
+ * @since 8.1.0
+ */
+ public function buildPolicy() {
+ $policy = "default-src 'none';";
+
+ if(!empty($this->allowedScriptDomains) || $this->inlineScriptAllowed || $this->evalScriptAllowed) {
+ $policy .= 'script-src ';
+ if(is_array($this->allowedScriptDomains)) {
+ $policy .= implode(' ', $this->allowedScriptDomains);
+ }
+ if($this->inlineScriptAllowed) {
+ $policy .= ' \'unsafe-inline\'';
+ }
+ if($this->evalScriptAllowed) {
+ $policy .= ' \'unsafe-eval\'';
+ }
+ $policy .= ';';
+ }
+
+ if(!empty($this->allowedStyleDomains) || $this->inlineStyleAllowed) {
+ $policy .= 'style-src ';
+ if(is_array($this->allowedStyleDomains)) {
+ $policy .= implode(' ', $this->allowedStyleDomains);
+ }
+ if($this->inlineStyleAllowed) {
+ $policy .= ' \'unsafe-inline\'';
+ }
+ $policy .= ';';
+ }
+
+ if(!empty($this->allowedImageDomains)) {
+ $policy .= 'img-src ' . implode(' ', $this->allowedImageDomains);
+ $policy .= ';';
+ }
+
+ if(!empty($this->allowedFontDomains)) {
+ $policy .= 'font-src ' . implode(' ', $this->allowedFontDomains);
+ $policy .= ';';
+ }
+
+ if(!empty($this->allowedConnectDomains)) {
+ $policy .= 'connect-src ' . implode(' ', $this->allowedConnectDomains);
+ $policy .= ';';
+ }
+
+ if(!empty($this->allowedMediaDomains)) {
+ $policy .= 'media-src ' . implode(' ', $this->allowedMediaDomains);
+ $policy .= ';';
+ }
+
+ if(!empty($this->allowedObjectDomains)) {
+ $policy .= 'object-src ' . implode(' ', $this->allowedObjectDomains);
+ $policy .= ';';
+ }
+
+ if(!empty($this->allowedFrameDomains)) {
+ $policy .= 'frame-src ' . implode(' ', $this->allowedFrameDomains);
+ $policy .= ';';
+ }
+
+ if(!empty($this->allowedChildSrcDomains)) {
+ $policy .= 'child-src ' . implode(' ', $this->allowedChildSrcDomains);
+ $policy .= ';';
+ }
+
+ return rtrim($policy, ';');
+ }
+}
diff --git a/lib/public/comments/icommentsmanager.php b/lib/public/comments/icommentsmanager.php
index b8fb3ca7f5d..f5b290bf8b2 100644
--- a/lib/public/comments/icommentsmanager.php
+++ b/lib/public/comments/icommentsmanager.php
@@ -115,10 +115,12 @@ interface ICommentsManager {
/**
* @param $objectType string the object type, e.g. 'files'
* @param $objectId string the id of the object
+ * @param \DateTime $notOlderThan optional, timestamp of the oldest comments
+ * that may be returned
* @return Int
* @since 9.0.0
*/
- public function getNumberOfCommentsForObject($objectType, $objectId);
+ public function getNumberOfCommentsForObject($objectType, $objectId, \DateTime $notOlderThan = null);
/**
* creates a new comment and returns it. At this point of time, it is not
@@ -188,4 +190,48 @@ interface ICommentsManager {
*/
public function deleteCommentsAtObject($objectType, $objectId);
+ /**
+ * sets the read marker for a given file to the specified date for the
+ * provided user
+ *
+ * @param string $objectType
+ * @param string $objectId
+ * @param \DateTime $dateTime
+ * @param \OCP\IUser $user
+ * @since 9.0.0
+ */
+ public function setReadMark($objectType, $objectId, \DateTime $dateTime, \OCP\IUser $user);
+
+ /**
+ * returns the read marker for a given file to the specified date for the
+ * provided user. It returns null, when the marker is not present, i.e.
+ * no comments were marked as read.
+ *
+ * @param string $objectType
+ * @param string $objectId
+ * @param \OCP\IUser $user
+ * @return \DateTime|null
+ * @since 9.0.0
+ */
+ public function getReadMark($objectType, $objectId, \OCP\IUser $user);
+
+ /**
+ * deletes the read markers for the specified user
+ *
+ * @param \OCP\IUser $user
+ * @return bool
+ * @since 9.0.0
+ */
+ public function deleteReadMarksFromUser(\OCP\IUser $user);
+
+ /**
+ * deletes the read markers on the specified object
+ *
+ * @param string $objectType
+ * @param string $objectId
+ * @return bool
+ * @since 9.0.0
+ */
+ public function deleteReadMarksOnObject($objectType, $objectId);
+
}
diff --git a/lib/public/files/storagenotavailableexception.php b/lib/public/files/storagenotavailableexception.php
index 4572a69f047..f9ac79d66ec 100644
--- a/lib/public/files/storagenotavailableexception.php
+++ b/lib/public/files/storagenotavailableexception.php
@@ -54,7 +54,7 @@ class StorageNotAvailableException extends HintException {
* @param \Exception $previous
* @since 6.0.0
*/
- public function __construct($message = '', $code = 0, \Exception $previous = null) {
+ public function __construct($message = '', $code = self::STATUS_ERROR, \Exception $previous = null) {
$l = \OC::$server->getL10N('core');
parent::__construct($message, $l->t('Storage not available'), $code, $previous);
}
diff --git a/lib/public/iservercontainer.php b/lib/public/iservercontainer.php
index ce1364cc4ea..de48daeef88 100644
--- a/lib/public/iservercontainer.php
+++ b/lib/public/iservercontainer.php
@@ -42,6 +42,7 @@
// use OCP namespace for all classes that are considered public.
// This means that they should be used by apps instead of the internal ownCloud classes
namespace OCP;
+use OCP\Security\IContentSecurityPolicyManager;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
@@ -512,4 +513,10 @@ interface IServerContainer {
* @since 9.0.0
*/
public function getShareManager();
+
+ /**
+ * @return IContentSecurityPolicyManager
+ * @since 9.0.0
+ */
+ public function getContentSecurityPolicyManager();
}
diff --git a/lib/public/security/icontentsecuritypolicymanager.php b/lib/public/security/icontentsecuritypolicymanager.php
new file mode 100644
index 00000000000..10f1efe995f
--- /dev/null
+++ b/lib/public/security/icontentsecuritypolicymanager.php
@@ -0,0 +1,50 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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 OCP\Security;
+use OCP\AppFramework\Http\EmptyContentSecurityPolicy;
+
+/**
+ * Used for Content Security Policy manipulations
+ *
+ * @package OCP\Security
+ * @since 9.0.0
+ */
+interface IContentSecurityPolicyManager {
+ /**
+ * Allows to inject something into the default content policy. This is for
+ * example useful when you're injecting Javascript code into a view belonging
+ * to another controller and cannot modify its Content-Security-Policy itself.
+ * Note that the adjustment is only applied to applications that use AppFramework
+ * controllers.
+ *
+ * To use this from your `app.php` use `\OC::$server->getContentSecurityPolicyManager()->addDefaultPolicy($policy)`,
+ * $policy has to be of type `\OCP\AppFramework\Http\ContentSecurityPolicy`.
+ *
+ * WARNING: Using this API incorrectly may make the instance more insecure.
+ * Do think twice before adding whitelisting resources. Please do also note
+ * that it is not possible to use the `disallowXYZ` functions.
+ *
+ * @param EmptyContentSecurityPolicy $policy
+ * @since 9.0.0
+ */
+ public function addDefaultPolicy(EmptyContentSecurityPolicy $policy);
+}
diff --git a/lib/public/share/imanager.php b/lib/public/share/imanager.php
index 6531c14a857..15b9f34764e 100644
--- a/lib/public/share/imanager.php
+++ b/lib/public/share/imanager.php
@@ -37,13 +37,15 @@ interface IManager {
* Create a Share
*
* @param IShare $share
- * @return Share The share object
+ * @return IShare The share object
* @since 9.0.0
*/
public function createShare(IShare $share);
/**
- * Update a share
+ * Update a share.
+ * The target of the share can't be changed this way: use moveShare
+ * The share can't be removed this way (permission 0): use deleteShare
*
* @param IShare $share
* @return IShare The share object
@@ -73,6 +75,18 @@ interface IManager {
public function deleteFromSelf(IShare $share, IUser $recipient);
/**
+ * Move the share as a recipient of the share.
+ * This is updating the share target. So where the recipient has the share mounted.
+ *
+ * @param IShare $share
+ * @param IUser $recipient
+ * @return IShare
+ * @throws \InvalidArgumentException If $share is a link share or the $recipient does not match
+ * @since 9.0.0
+ */
+ public function moveShare(IShare $share, IUser $recipient);
+
+ /**
* Get shares shared by (initiated) by the provided user.
*
* @param IUser $user
@@ -88,31 +102,37 @@ interface IManager {
/**
* Get shares shared with $user.
+ * Filter by $node if provided
*
* @param IUser $user
* @param int $shareType
+ * @param File|Folder|null $node
* @param int $limit The maximum number of shares returned, -1 for all
* @param int $offset
* @return IShare[]
* @since 9.0.0
*/
- public function getSharedWith(IUser $user, $shareType, $limit = 50, $offset = 0);
+ public function getSharedWith(IUser $user, $shareType, $node = null, $limit = 50, $offset = 0);
/**
- * Retrieve a share by the share id
+ * Retrieve a share by the share id.
+ * If the recipient is set make sure to retrieve the file for that user.
+ * This makes sure that if a user has moved/deleted a group share this
+ * is reflected.
*
* @param string $id
- * @return Share
+ * @param IUser|null $recipient
+ * @return IShare
* @throws ShareNotFound
* @since 9.0.0
*/
- public function getShareById($id);
+ public function getShareById($id, $recipient = null);
/**
* Get the share by token possible with password
*
* @param string $token
- * @return Share
+ * @return IShare
* @throws ShareNotFound
* @since 9.0.0
*/
diff --git a/lib/public/share/ishareprovider.php b/lib/public/share/ishareprovider.php
index 50964c88dd6..42a2881718e 100644
--- a/lib/public/share/ishareprovider.php
+++ b/lib/public/share/ishareprovider.php
@@ -23,6 +23,7 @@ namespace OCP\Share;
use OC\Share20\Exception\ShareNotFound;
use OC\Share20\Exception\BackendError;
+use OCP\Files\Node;
use OCP\IUser;
/**
@@ -79,6 +80,19 @@ interface IShareProvider {
public function deleteFromSelf(\OCP\Share\IShare $share, IUser $recipient);
/**
+ * Move a share as a recipient.
+ * This is updating the share target. Thus the mount point of the recipient.
+ * This may require special handling. If a user moves a group share
+ * the target should only be changed for them.
+ *
+ * @param \OCP\Share\IShare $share
+ * @param IUser $recipient
+ * @return \OCP\Share\IShare
+ * @since 9.0.0
+ */
+ public function move(\OCP\Share\IShare $share, IUser $recipient);
+
+ /**
* Get all shares by the given user
*
* @param IUser $user
@@ -96,11 +110,12 @@ interface IShareProvider {
* Get share by id
*
* @param int $id
+ * @param IUser|null $recipient
* @return \OCP\Share\IShare
* @throws ShareNotFound
* @since 9.0.0
*/
- public function getShareById($id);
+ public function getShareById($id, $recipient = null);
/**
* Get shares for a given path
@@ -116,12 +131,13 @@ interface IShareProvider {
*
* @param IUser $user get shares where this user is the recipient
* @param int $shareType
+ * @param Node|null $node
* @param int $limit The max number of entries returned, -1 for all
* @param int $offset
* @return \OCP\Share\IShare[]
* @since 9.0.0
*/
- public function getSharedWith(IUser $user, $shareType, $limit, $offset);
+ public function getSharedWith(IUser $user, $shareType, $node, $limit, $offset);
/**
* Get a share by token
diff --git a/lib/public/systemtag/managerevent.php b/lib/public/systemtag/managerevent.php
new file mode 100644
index 00000000000..a0429fc9f67
--- /dev/null
+++ b/lib/public/systemtag/managerevent.php
@@ -0,0 +1,85 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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 OCP\SystemTag;
+
+use Symfony\Component\EventDispatcher\Event;
+
+/**
+ * Class ManagerEvent
+ *
+ * @package OCP\SystemTag
+ * @since 9.0.0
+ */
+class ManagerEvent extends Event {
+
+ const EVENT_CREATE = 'OCP\SystemTag\ISystemTagManager::createTag';
+ const EVENT_UPDATE = 'OCP\SystemTag\ISystemTagManager::updateTag';
+ const EVENT_DELETE = 'OCP\SystemTag\ISystemTagManager::deleteTag';
+
+ /** @var string */
+ protected $event;
+ /** @var ISystemTag */
+ protected $tag;
+ /** @var ISystemTag */
+ protected $beforeTag;
+
+ /**
+ * DispatcherEvent constructor.
+ *
+ * @param string $event
+ * @param ISystemTag $tag
+ * @param ISystemTag $beforeTag
+ * @since 9.0.0
+ */
+ public function __construct($event, ISystemTag $tag, ISystemTag $beforeTag = null) {
+ $this->event = $event;
+ $this->tag = $tag;
+ $this->beforeTag = $beforeTag;
+ }
+
+ /**
+ * @return string
+ * @since 9.0.0
+ */
+ public function getEvent() {
+ return $this->event;
+ }
+
+ /**
+ * @return ISystemTag
+ * @since 9.0.0
+ */
+ public function getTag() {
+ return $this->tag;
+ }
+
+ /**
+ * @return ISystemTag
+ * @since 9.0.0
+ */
+ public function getTagBefore() {
+ if ($this->event !== self::EVENT_UPDATE) {
+ throw new \BadMethodCallException('getTagBefore is only available on the update Event');
+ }
+ return $this->beforeTag;
+ }
+}
diff --git a/lib/public/systemtag/mapperevent.php b/lib/public/systemtag/mapperevent.php
new file mode 100644
index 00000000000..e05a5ce09c8
--- /dev/null
+++ b/lib/public/systemtag/mapperevent.php
@@ -0,0 +1,93 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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 OCP\SystemTag;
+
+use Symfony\Component\EventDispatcher\Event;
+
+/**
+ * Class MapperEvent
+ *
+ * @package OCP\SystemTag
+ * @since 9.0.0
+ */
+class MapperEvent extends Event {
+
+ const EVENT_ASSIGN = 'OCP\SystemTag\ISystemTagObjectMapper::assignTags';
+ const EVENT_UNASSIGN = 'OCP\SystemTag\ISystemTagObjectMapper::unassignTags';
+
+ /** @var string */
+ protected $event;
+ /** @var string */
+ protected $objectType;
+ /** @var string */
+ protected $objectId;
+ /** @var int[] */
+ protected $tags;
+
+ /**
+ * DispatcherEvent constructor.
+ *
+ * @param string $event
+ * @param string $objectType
+ * @param string $objectId
+ * @param int[] $tags
+ * @since 9.0.0
+ */
+ public function __construct($event, $objectType, $objectId, array $tags) {
+ $this->event = $event;
+ $this->objectType = $objectType;
+ $this->objectId = $objectId;
+ $this->tags = $tags;
+ }
+
+ /**
+ * @return string
+ * @since 9.0.0
+ */
+ public function getEvent() {
+ return $this->event;
+ }
+
+ /**
+ * @return string
+ * @since 9.0.0
+ */
+ public function getObjectType() {
+ return $this->objectType;
+ }
+
+ /**
+ * @return string
+ * @since 9.0.0
+ */
+ public function getObjectId() {
+ return $this->objectId;
+ }
+
+ /**
+ * @return int[]
+ * @since 9.0.0
+ */
+ public function getTags() {
+ return $this->tags;
+ }
+}
diff --git a/settings/controller/checksetupcontroller.php b/settings/controller/checksetupcontroller.php
index 7b995bf2c1b..26194bb1180 100644
--- a/settings/controller/checksetupcontroller.php
+++ b/settings/controller/checksetupcontroller.php
@@ -329,7 +329,6 @@ Raw output
return new DataResponse(
[
'serverHasInternetConnection' => $this->isInternetConnectionWorking(),
- 'dataDirectoryProtected' => $this->util->isHtaccessWorking($this->config),
'isMemcacheConfigured' => $this->isMemcacheConfigured(),
'memcacheDocs' => $this->urlGenerator->linkToDocs('admin-performance'),
'isUrandomAvailable' => $this->isUrandomAvailable(),
diff --git a/settings/js/admin.js b/settings/js/admin.js
index 6660bfe9b48..90b1de68370 100644
--- a/settings/js/admin.js
+++ b/settings/js/admin.js
@@ -171,9 +171,10 @@ $(document).ready(function(){
OC.SetupChecks.checkWellKnownUrl('/.well-known/caldav/', oc_defaults.docPlaceholderUrl, $('#postsetupchecks').data('check-wellknown') === 'true'),
OC.SetupChecks.checkWellKnownUrl('/.well-known/carddav/', oc_defaults.docPlaceholderUrl, $('#postsetupchecks').data('check-wellknown') === 'true'),
OC.SetupChecks.checkSetup(),
- OC.SetupChecks.checkGeneric()
- ).then(function(check1, check2, check3, check4, check5) {
- var messages = [].concat(check1, check2, check3, check4, check5);
+ OC.SetupChecks.checkGeneric(),
+ OC.SetupChecks.checkDataProtected()
+ ).then(function(check1, check2, check3, check4, check5, check6) {
+ var messages = [].concat(check1, check2, check3, check4, check5, check6);
var $el = $('#postsetupchecks');
$el.find('.loading').addClass('hidden');
diff --git a/settings/l10n/de_DE.js b/settings/l10n/de_DE.js
index 70090b9e2b6..3f314c54816 100644
--- a/settings/l10n/de_DE.js
+++ b/settings/l10n/de_DE.js
@@ -180,6 +180,7 @@ OC.L10N.register(
"More" : "Mehr",
"Less" : "Weniger",
"The logfile is bigger than 100 MB. Downloading it may take some time!" : "Die Logdatei ist größer als 100 MB. Es kann etwas Zeit beanspruchen, sie herunterzuladen!",
+ "What to log" : "Was geloggt wird",
"SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "SQLite wird als Datenbank verwendet. Bei größeren Installationen wird empfohlen, auf ein anderes Datenbank-Backend zu wechseln.",
"Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "Insbesondere bei der Nutzung des Desktop Clients zur Dateisynchronisierung wird vom Einsatz von SQLite abgeraten.",
"To migrate to another database use the command line tool: 'occ db:convert-type', or see the <a target=\"_blank\" href=\"%s\">documentation ↗</a>." : "Um auf eine andere Datenbank zu migrieren, benutzen Sie bitte die Kommandozeile: „occ db:convert-type“ oder konsultieren Sie die <a target=\"_blank\" href=\"%s\">Dokumentation ↗</a>.",
@@ -193,6 +194,8 @@ OC.L10N.register(
"Developer documentation" : "Dokumentation für Entwickler",
"Experimental applications ahead" : "Experimentelle Apps nachfolgend",
"Experimental apps are not checked for security issues, new or known to be unstable and under heavy development. Installing them can cause data loss or security breaches." : "Experimentelle Apps sind nicht auf Sicherheitsprobleme hin überprüft, sind neu oder bekanntermaßen instabil und befinden sich in intensiver Entwicklung. Ihre Installation kann Datenverlust oder Sicherheitslücken hervorrufen.",
+ "by %s" : "durch %s",
+ "%s-licensed" : "%s-Lizensiert",
"Documentation:" : "Dokumentation:",
"User documentation" : "Dokumentation für Benutzer",
"Admin documentation" : "Dokumentation für Administratoren",
@@ -202,6 +205,7 @@ OC.L10N.register(
"Enable only for specific groups" : "Nur für bestimmte Gruppen aktivieren",
"Uninstall App" : "App deinstallieren",
"Enable experimental apps" : "Experimentelle Apps aktivieren",
+ "SSL Root Certificates" : "SSL Root Zertifikate",
"Common Name" : "Common Name",
"Valid until" : "Gültig bis",
"Issued By" : "Ausgestellt von:",
@@ -217,8 +221,11 @@ OC.L10N.register(
"Commercial support" : "Kommerzieller Support",
"Profile picture" : "Profilbild",
"Upload new" : "Neues hochladen",
+ "Select from Files" : "Aus Dateien wählen",
"Remove image" : "Bild entfernen",
+ "png or jpg, max. 20 MB" : "png oder jpg, max. 20 MB",
"Cancel" : "Abbrechen",
+ "Choose as profile picture" : "Als Profilbild auswählen",
"Full name" : "Vollständiger Name",
"No display name set" : "Kein Anzeigename angegeben",
"Email" : "E-Mail",
diff --git a/settings/l10n/de_DE.json b/settings/l10n/de_DE.json
index 8f6931db5c2..0df74f1d602 100644
--- a/settings/l10n/de_DE.json
+++ b/settings/l10n/de_DE.json
@@ -178,6 +178,7 @@
"More" : "Mehr",
"Less" : "Weniger",
"The logfile is bigger than 100 MB. Downloading it may take some time!" : "Die Logdatei ist größer als 100 MB. Es kann etwas Zeit beanspruchen, sie herunterzuladen!",
+ "What to log" : "Was geloggt wird",
"SQLite is used as database. For larger installations we recommend to switch to a different database backend." : "SQLite wird als Datenbank verwendet. Bei größeren Installationen wird empfohlen, auf ein anderes Datenbank-Backend zu wechseln.",
"Especially when using the desktop client for file syncing the use of SQLite is discouraged." : "Insbesondere bei der Nutzung des Desktop Clients zur Dateisynchronisierung wird vom Einsatz von SQLite abgeraten.",
"To migrate to another database use the command line tool: 'occ db:convert-type', or see the <a target=\"_blank\" href=\"%s\">documentation ↗</a>." : "Um auf eine andere Datenbank zu migrieren, benutzen Sie bitte die Kommandozeile: „occ db:convert-type“ oder konsultieren Sie die <a target=\"_blank\" href=\"%s\">Dokumentation ↗</a>.",
@@ -191,6 +192,8 @@
"Developer documentation" : "Dokumentation für Entwickler",
"Experimental applications ahead" : "Experimentelle Apps nachfolgend",
"Experimental apps are not checked for security issues, new or known to be unstable and under heavy development. Installing them can cause data loss or security breaches." : "Experimentelle Apps sind nicht auf Sicherheitsprobleme hin überprüft, sind neu oder bekanntermaßen instabil und befinden sich in intensiver Entwicklung. Ihre Installation kann Datenverlust oder Sicherheitslücken hervorrufen.",
+ "by %s" : "durch %s",
+ "%s-licensed" : "%s-Lizensiert",
"Documentation:" : "Dokumentation:",
"User documentation" : "Dokumentation für Benutzer",
"Admin documentation" : "Dokumentation für Administratoren",
@@ -200,6 +203,7 @@
"Enable only for specific groups" : "Nur für bestimmte Gruppen aktivieren",
"Uninstall App" : "App deinstallieren",
"Enable experimental apps" : "Experimentelle Apps aktivieren",
+ "SSL Root Certificates" : "SSL Root Zertifikate",
"Common Name" : "Common Name",
"Valid until" : "Gültig bis",
"Issued By" : "Ausgestellt von:",
@@ -215,8 +219,11 @@
"Commercial support" : "Kommerzieller Support",
"Profile picture" : "Profilbild",
"Upload new" : "Neues hochladen",
+ "Select from Files" : "Aus Dateien wählen",
"Remove image" : "Bild entfernen",
+ "png or jpg, max. 20 MB" : "png oder jpg, max. 20 MB",
"Cancel" : "Abbrechen",
+ "Choose as profile picture" : "Als Profilbild auswählen",
"Full name" : "Vollständiger Name",
"No display name set" : "Kein Anzeigename angegeben",
"Email" : "E-Mail",
diff --git a/settings/l10n/fr.js b/settings/l10n/fr.js
index 7f0034ae940..43a6ef4358e 100644
--- a/settings/l10n/fr.js
+++ b/settings/l10n/fr.js
@@ -205,6 +205,7 @@ OC.L10N.register(
"Experimental applications ahead" : "Attention! Applications expérimentales",
"Experimental apps are not checked for security issues, new or known to be unstable and under heavy development. Installing them can cause data loss or security breaches." : "Les applications expérimentales n'ont pas fait l'objet de tests de sécurité, sont encore en développement et peuvent être instables. Les installer peut causer des pertes de données ou des failles de sécurité. ",
"by %s" : "par %s",
+ "%s-licensed" : "Sous licence %s",
"Documentation:" : "Documentation :",
"User documentation" : "Documentation utilisateur",
"Admin documentation" : "Documentation administrateur",
diff --git a/settings/l10n/fr.json b/settings/l10n/fr.json
index b690dc5d8e6..9b5fbd02401 100644
--- a/settings/l10n/fr.json
+++ b/settings/l10n/fr.json
@@ -203,6 +203,7 @@
"Experimental applications ahead" : "Attention! Applications expérimentales",
"Experimental apps are not checked for security issues, new or known to be unstable and under heavy development. Installing them can cause data loss or security breaches." : "Les applications expérimentales n'ont pas fait l'objet de tests de sécurité, sont encore en développement et peuvent être instables. Les installer peut causer des pertes de données ou des failles de sécurité. ",
"by %s" : "par %s",
+ "%s-licensed" : "Sous licence %s",
"Documentation:" : "Documentation :",
"User documentation" : "Documentation utilisateur",
"Admin documentation" : "Documentation administrateur",
diff --git a/settings/l10n/ja.js b/settings/l10n/ja.js
index 65881d30d34..2d092c302d1 100644
--- a/settings/l10n/ja.js
+++ b/settings/l10n/ja.js
@@ -209,6 +209,8 @@ OC.L10N.register(
"Admin documentation" : "管理者ドキュメント",
"Show description …" : "説明を表示 ...",
"Hide description …" : "説明を隠す ...",
+ "This app has no minimum ownCloud version assigned. This will be an error in ownCloud 11 and later." : "このアプリはownCloudの最小バージョンが指定されていません.ownCloud 11 以降でエラーが発生する可能性があります.",
+ "This app has no maximum ownCloud version assigned. This will be an error in ownCloud 11 and later." : "このアプリはownCloudの最大バージョンが指定されていません.ownCloud 11 以降でエラーが発生する可能性があります.",
"This app cannot be installed because the following dependencies are not fulfilled:" : "次の依存関係が満たされないためこのアプリをインストールできません:",
"Enable only for specific groups" : "特定のグループのみ有効に",
"Uninstall App" : "アプリをアンインストール",
diff --git a/settings/l10n/ja.json b/settings/l10n/ja.json
index 05d43d0cd62..996a53df9c5 100644
--- a/settings/l10n/ja.json
+++ b/settings/l10n/ja.json
@@ -207,6 +207,8 @@
"Admin documentation" : "管理者ドキュメント",
"Show description …" : "説明を表示 ...",
"Hide description …" : "説明を隠す ...",
+ "This app has no minimum ownCloud version assigned. This will be an error in ownCloud 11 and later." : "このアプリはownCloudの最小バージョンが指定されていません.ownCloud 11 以降でエラーが発生する可能性があります.",
+ "This app has no maximum ownCloud version assigned. This will be an error in ownCloud 11 and later." : "このアプリはownCloudの最大バージョンが指定されていません.ownCloud 11 以降でエラーが発生する可能性があります.",
"This app cannot be installed because the following dependencies are not fulfilled:" : "次の依存関係が満たされないためこのアプリをインストールできません:",
"Enable only for specific groups" : "特定のグループのみ有効に",
"Uninstall App" : "アプリをアンインストール",
diff --git a/settings/l10n/nl.js b/settings/l10n/nl.js
index 2aead6ae80f..60962c89c61 100644
--- a/settings/l10n/nl.js
+++ b/settings/l10n/nl.js
@@ -204,6 +204,8 @@ OC.L10N.register(
"Developer documentation" : "Ontwikkelaarsdocumentatie",
"Experimental applications ahead" : "Experimentele applicaties vooraan",
"Experimental apps are not checked for security issues, new or known to be unstable and under heavy development. Installing them can cause data loss or security breaches." : "Experimentele apps zijn niet gecontroleerd op beveiligingsproblemen, zijn nieuw of staan bekend als instabiel en worden volop ontwikkeld. Installatie kan leiden tot gegevensverlies of beveiligingsincidenten.",
+ "by %s" : "op %s",
+ "%s-licensed" : "%s-licensed",
"Documentation:" : "Documentatie:",
"User documentation" : "Gebruikersdocumentatie",
"Admin documentation" : "Beheerdocumentatie",
diff --git a/settings/l10n/nl.json b/settings/l10n/nl.json
index b72ac4f860c..2c201e83e30 100644
--- a/settings/l10n/nl.json
+++ b/settings/l10n/nl.json
@@ -202,6 +202,8 @@
"Developer documentation" : "Ontwikkelaarsdocumentatie",
"Experimental applications ahead" : "Experimentele applicaties vooraan",
"Experimental apps are not checked for security issues, new or known to be unstable and under heavy development. Installing them can cause data loss or security breaches." : "Experimentele apps zijn niet gecontroleerd op beveiligingsproblemen, zijn nieuw of staan bekend als instabiel en worden volop ontwikkeld. Installatie kan leiden tot gegevensverlies of beveiligingsincidenten.",
+ "by %s" : "op %s",
+ "%s-licensed" : "%s-licensed",
"Documentation:" : "Documentatie:",
"User documentation" : "Gebruikersdocumentatie",
"Admin documentation" : "Beheerdocumentatie",
diff --git a/settings/l10n/pt_BR.js b/settings/l10n/pt_BR.js
index 7d04bc3c04b..144c4a812f2 100644
--- a/settings/l10n/pt_BR.js
+++ b/settings/l10n/pt_BR.js
@@ -204,6 +204,8 @@ OC.L10N.register(
"Developer documentation" : "Documentação do desenvolvedor",
"Experimental applications ahead" : "Aplicações experimentais à frente",
"Experimental apps are not checked for security issues, new or known to be unstable and under heavy development. Installing them can cause data loss or security breaches." : "Aplicativos experimentais não são marcados por questões de segurança, por serem novos ou conhecidos como instáveis e sob forte desenvolvimento. Instalá-los pode causar perda de dados ou falhas de segurança.",
+ "by %s" : "por %s",
+ "%s-licensed" : "%s-licenciado",
"Documentation:" : "Documentação:",
"User documentation" : "Documentação do usuário",
"Admin documentation" : "Documentação para Administração",
diff --git a/settings/l10n/pt_BR.json b/settings/l10n/pt_BR.json
index 100b7e60f22..fb06b056b44 100644
--- a/settings/l10n/pt_BR.json
+++ b/settings/l10n/pt_BR.json
@@ -202,6 +202,8 @@
"Developer documentation" : "Documentação do desenvolvedor",
"Experimental applications ahead" : "Aplicações experimentais à frente",
"Experimental apps are not checked for security issues, new or known to be unstable and under heavy development. Installing them can cause data loss or security breaches." : "Aplicativos experimentais não são marcados por questões de segurança, por serem novos ou conhecidos como instáveis e sob forte desenvolvimento. Instalá-los pode causar perda de dados ou falhas de segurança.",
+ "by %s" : "por %s",
+ "%s-licensed" : "%s-licenciado",
"Documentation:" : "Documentação:",
"User documentation" : "Documentação do usuário",
"Admin documentation" : "Documentação para Administração",
diff --git a/settings/l10n/ru.js b/settings/l10n/ru.js
index 77e57b69323..33697368a27 100644
--- a/settings/l10n/ru.js
+++ b/settings/l10n/ru.js
@@ -204,6 +204,8 @@ OC.L10N.register(
"Developer documentation" : "Документация для разработчиков",
"Experimental applications ahead" : "Экспериментальные приложения",
"Experimental apps are not checked for security issues, new or known to be unstable and under heavy development. Installing them can cause data loss or security breaches." : "Экспериментальные приложения не проверялись на наличие уязвимостей безопасности, они также могут быть нестабильны, т.к. находятся в активной разработке. Их установка может повлечь потерю информации или нарушение безопасности.",
+ "by %s" : "от %s",
+ "%s-licensed" : "Лицензия %s",
"Documentation:" : "Документация:",
"User documentation" : "Пользовательская документация",
"Admin documentation" : "Документация для администратора",
diff --git a/settings/l10n/ru.json b/settings/l10n/ru.json
index dc1a2101aca..959588abf57 100644
--- a/settings/l10n/ru.json
+++ b/settings/l10n/ru.json
@@ -202,6 +202,8 @@
"Developer documentation" : "Документация для разработчиков",
"Experimental applications ahead" : "Экспериментальные приложения",
"Experimental apps are not checked for security issues, new or known to be unstable and under heavy development. Installing them can cause data loss or security breaches." : "Экспериментальные приложения не проверялись на наличие уязвимостей безопасности, они также могут быть нестабильны, т.к. находятся в активной разработке. Их установка может повлечь потерю информации или нарушение безопасности.",
+ "by %s" : "от %s",
+ "%s-licensed" : "Лицензия %s",
"Documentation:" : "Документация:",
"User documentation" : "Пользовательская документация",
"Admin documentation" : "Документация для администратора",
diff --git a/settings/l10n/zh_TW.js b/settings/l10n/zh_TW.js
index 755befc2d5f..14e94ae0f30 100644
--- a/settings/l10n/zh_TW.js
+++ b/settings/l10n/zh_TW.js
@@ -277,7 +277,7 @@ OC.L10N.register(
"Unlimited" : "無限制",
"Other" : "其他",
"Full Name" : "全名",
- "Group Admin for" : "是以下群組的管理員",
+ "Group Admin for" : "指定群組管理者",
"Quota" : "容量限制",
"Storage Location" : "儲存位置",
"User Backend" : "用戶後端",
diff --git a/settings/l10n/zh_TW.json b/settings/l10n/zh_TW.json
index e89a00e338f..0fc46a9ee8f 100644
--- a/settings/l10n/zh_TW.json
+++ b/settings/l10n/zh_TW.json
@@ -275,7 +275,7 @@
"Unlimited" : "無限制",
"Other" : "其他",
"Full Name" : "全名",
- "Group Admin for" : "是以下群組的管理員",
+ "Group Admin for" : "指定群組管理者",
"Quota" : "容量限制",
"Storage Location" : "儲存位置",
"User Backend" : "用戶後端",
diff --git a/settings/personal.php b/settings/personal.php
index 261a459a921..d2d4fc90f5e 100644
--- a/settings/personal.php
+++ b/settings/personal.php
@@ -149,7 +149,7 @@ $tmpl->assign('activelanguage', $userLang);
$tmpl->assign('passwordChangeSupported', OC_User::canUserChangePassword(OC_User::getUser()));
$tmpl->assign('displayNameChangeSupported', OC_User::canUserChangeDisplayName(OC_User::getUser()));
$tmpl->assign('displayName', OC_User::getDisplayName());
-$tmpl->assign('enableAvatars', $config->getSystemValue('enable_avatars', true));
+$tmpl->assign('enableAvatars', $config->getSystemValue('enable_avatars', true) === true);
$tmpl->assign('avatarChangeSupported', OC_User::canUserChangeAvatar(OC_User::getUser()));
$tmpl->assign('certs', $certificateManager->listCertificates());
$tmpl->assign('showCertificates', $enableCertImport);
diff --git a/settings/users.php b/settings/users.php
index 9d89ff65b4a..0dff85c1ade 100644
--- a/settings/users.php
+++ b/settings/users.php
@@ -116,7 +116,7 @@ $tmpl->assign('quota_preset', $quotaPreset);
$tmpl->assign('default_quota', $defaultQuota);
$tmpl->assign('defaultQuotaIsUserDefined', $defaultQuotaIsUserDefined);
$tmpl->assign('recoveryAdminEnabled', $recoveryAdminEnabled);
-$tmpl->assign('enableAvatars', \OC::$server->getConfig()->getSystemValue('enable_avatars', true));
+$tmpl->assign('enableAvatars', \OC::$server->getConfig()->getSystemValue('enable_avatars', true) === true);
$tmpl->assign('show_storage_location', $config->getAppValue('core', 'umgmt_show_storage_location', 'false'));
$tmpl->assign('show_last_login', $config->getAppValue('core', 'umgmt_show_last_login', 'false'));
diff --git a/tests/karma.config.js b/tests/karma.config.js
index 467b270b350..4a7a9ad236e 100644
--- a/tests/karma.config.js
+++ b/tests/karma.config.js
@@ -83,6 +83,18 @@ module.exports = function(config) {
testFiles: ['apps/files_versions/tests/js/**/*.js']
},
{
+ name: 'comments',
+ srcFiles: [
+ // need to enforce loading order...
+ 'apps/comments/js/app.js',
+ 'apps/comments/js/commentmodel.js',
+ 'apps/comments/js/commentcollection.js',
+ 'apps/comments/js/commentstabview.js',
+ 'apps/comments/js/filesplugin'
+ ],
+ testFiles: ['apps/comments/tests/js/**/*.js']
+ },
+ {
name: 'systemtags',
srcFiles: [
// need to enforce loading order...
diff --git a/tests/lib/appframework/http/ContentSecurityPolicyTest.php b/tests/lib/appframework/http/ContentSecurityPolicyTest.php
index 6d9c6d7b8d9..adf03185e9f 100644
--- a/tests/lib/appframework/http/ContentSecurityPolicyTest.php
+++ b/tests/lib/appframework/http/ContentSecurityPolicyTest.php
@@ -426,21 +426,4 @@ class ContentSecurityPolicyTest extends \Test\TestCase {
$this->contentSecurityPolicy->disallowChildSrcDomain('www.owncloud.org')->disallowChildSrcDomain('www.owncloud.com');
$this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
}
-
- public function testConfigureStacked() {
- $expectedPolicy = "default-src 'none';script-src 'self' script.owncloud.org;style-src 'self' style.owncloud.org;img-src 'self' data: blob: img.owncloud.org;font-src 'self' font.owncloud.org;connect-src 'self' connect.owncloud.org;media-src 'self' media.owncloud.org;object-src objects.owncloud.org;frame-src frame.owncloud.org;child-src child.owncloud.org";
-
- $this->contentSecurityPolicy->allowInlineStyle(false)
- ->allowEvalScript(false)
- ->addAllowedScriptDomain('script.owncloud.org')
- ->addAllowedStyleDomain('style.owncloud.org')
- ->addAllowedFontDomain('font.owncloud.org')
- ->addAllowedImageDomain('img.owncloud.org')
- ->addAllowedConnectDomain('connect.owncloud.org')
- ->addAllowedMediaDomain('media.owncloud.org')
- ->addAllowedObjectDomain('objects.owncloud.org')
- ->addAllowedChildSrcDomain('child.owncloud.org')
- ->addAllowedFrameDomain('frame.owncloud.org');
- $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
- }
}
diff --git a/tests/lib/appframework/http/EmptyContentSecurityPolicyTest.php b/tests/lib/appframework/http/EmptyContentSecurityPolicyTest.php
new file mode 100644
index 00000000000..0d0f92de819
--- /dev/null
+++ b/tests/lib/appframework/http/EmptyContentSecurityPolicyTest.php
@@ -0,0 +1,430 @@
+<?php
+/**
+ * Copyright (c) 2015 Lukas Reschke lukas@owncloud.com
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ */
+
+
+namespace OC\AppFramework\Http;
+
+use OCP\AppFramework\Http;
+use OCP\AppFramework\Http\EmptyContentSecurityPolicy;
+
+/**
+ * Class ContentSecurityPolicyTest
+ *
+ * @package OC\AppFramework\Http
+ */
+class EmptyContentSecurityPolicyTest extends \Test\TestCase {
+
+ /** @var EmptyContentSecurityPolicy */
+ private $contentSecurityPolicy;
+
+ public function setUp() {
+ parent::setUp();
+ $this->contentSecurityPolicy = new EmptyContentSecurityPolicy();
+ }
+
+ public function testGetPolicyDefault() {
+ $defaultPolicy = "default-src 'none'";
+ $this->assertSame($defaultPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyScriptDomainValid() {
+ $expectedPolicy = "default-src 'none';script-src www.owncloud.com";
+
+ $this->contentSecurityPolicy->addAllowedScriptDomain('www.owncloud.com');
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyScriptDomainValidMultiple() {
+ $expectedPolicy = "default-src 'none';script-src www.owncloud.com www.owncloud.org";
+
+ $this->contentSecurityPolicy->addAllowedScriptDomain('www.owncloud.com');
+ $this->contentSecurityPolicy->addAllowedScriptDomain('www.owncloud.org');
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyDisallowScriptDomain() {
+ $expectedPolicy = "default-src 'none'";
+
+ $this->contentSecurityPolicy->addAllowedScriptDomain('www.owncloud.com');
+ $this->contentSecurityPolicy->disallowScriptDomain('www.owncloud.com');
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyDisallowScriptDomainMultiple() {
+ $expectedPolicy = "default-src 'none';script-src www.owncloud.com";
+
+ $this->contentSecurityPolicy->addAllowedScriptDomain('www.owncloud.com');
+ $this->contentSecurityPolicy->disallowScriptDomain('www.owncloud.org');
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyDisallowScriptDomainMultipleStacked() {
+ $expectedPolicy = "default-src 'none'";
+
+ $this->contentSecurityPolicy->addAllowedScriptDomain('www.owncloud.com');
+ $this->contentSecurityPolicy->disallowScriptDomain('www.owncloud.org')->disallowScriptDomain('www.owncloud.com');
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyScriptAllowInline() {
+ $expectedPolicy = "default-src 'none';script-src 'unsafe-inline'";
+
+ $this->contentSecurityPolicy->allowInlineScript(true);
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyScriptAllowInlineWithDomain() {
+ $expectedPolicy = "default-src 'none';script-src www.owncloud.com 'unsafe-inline'";
+
+ $this->contentSecurityPolicy->addAllowedScriptDomain('www.owncloud.com');
+ $this->contentSecurityPolicy->allowInlineScript(true);
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyScriptAllowInlineAndEval() {
+ $expectedPolicy = "default-src 'none';script-src 'unsafe-inline' 'unsafe-eval'";
+
+ $this->contentSecurityPolicy->allowInlineScript(true);
+ $this->contentSecurityPolicy->allowEvalScript(true);
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyStyleDomainValid() {
+ $expectedPolicy = "default-src 'none';style-src www.owncloud.com";
+
+ $this->contentSecurityPolicy->addAllowedStyleDomain('www.owncloud.com');
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyStyleDomainValidMultiple() {
+ $expectedPolicy = "default-src 'none';style-src www.owncloud.com www.owncloud.org";
+
+ $this->contentSecurityPolicy->addAllowedStyleDomain('www.owncloud.com');
+ $this->contentSecurityPolicy->addAllowedStyleDomain('www.owncloud.org');
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyDisallowStyleDomain() {
+ $expectedPolicy = "default-src 'none'";
+
+ $this->contentSecurityPolicy->addAllowedStyleDomain('www.owncloud.com');
+ $this->contentSecurityPolicy->disallowStyleDomain('www.owncloud.com');
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyDisallowStyleDomainMultiple() {
+ $expectedPolicy = "default-src 'none';style-src www.owncloud.com";
+
+ $this->contentSecurityPolicy->addAllowedStyleDomain('www.owncloud.com');
+ $this->contentSecurityPolicy->disallowStyleDomain('www.owncloud.org');
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyDisallowStyleDomainMultipleStacked() {
+ $expectedPolicy = "default-src 'none'";
+
+ $this->contentSecurityPolicy->addAllowedStyleDomain('www.owncloud.com');
+ $this->contentSecurityPolicy->disallowStyleDomain('www.owncloud.org')->disallowStyleDomain('www.owncloud.com');
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyStyleAllowInline() {
+ $expectedPolicy = "default-src 'none';style-src 'unsafe-inline'";
+
+ $this->contentSecurityPolicy->allowInlineStyle(true);
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyStyleAllowInlineWithDomain() {
+ $expectedPolicy = "default-src 'none';style-src www.owncloud.com 'unsafe-inline'";
+
+ $this->contentSecurityPolicy->addAllowedStyleDomain('www.owncloud.com');
+ $this->contentSecurityPolicy->allowInlineStyle(true);
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyStyleDisallowInline() {
+ $expectedPolicy = "default-src 'none'";
+
+ $this->contentSecurityPolicy->allowInlineStyle(false);
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyImageDomainValid() {
+ $expectedPolicy = "default-src 'none';img-src www.owncloud.com";
+
+ $this->contentSecurityPolicy->addAllowedImageDomain('www.owncloud.com');
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyImageDomainValidMultiple() {
+ $expectedPolicy = "default-src 'none';img-src www.owncloud.com www.owncloud.org";
+
+ $this->contentSecurityPolicy->addAllowedImageDomain('www.owncloud.com');
+ $this->contentSecurityPolicy->addAllowedImageDomain('www.owncloud.org');
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyDisallowImageDomain() {
+ $expectedPolicy = "default-src 'none'";
+
+ $this->contentSecurityPolicy->addAllowedImageDomain('www.owncloud.com');
+ $this->contentSecurityPolicy->disallowImageDomain('www.owncloud.com');
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyDisallowImageDomainMultiple() {
+ $expectedPolicy = "default-src 'none';img-src www.owncloud.com";
+
+ $this->contentSecurityPolicy->addAllowedImageDomain('www.owncloud.com');
+ $this->contentSecurityPolicy->disallowImageDomain('www.owncloud.org');
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyDisallowImageDomainMultipleStakes() {
+ $expectedPolicy = "default-src 'none'";
+
+ $this->contentSecurityPolicy->addAllowedImageDomain('www.owncloud.com');
+ $this->contentSecurityPolicy->disallowImageDomain('www.owncloud.org')->disallowImageDomain('www.owncloud.com');
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyFontDomainValid() {
+ $expectedPolicy = "default-src 'none';font-src www.owncloud.com";
+
+ $this->contentSecurityPolicy->addAllowedFontDomain('www.owncloud.com');
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyFontDomainValidMultiple() {
+ $expectedPolicy = "default-src 'none';font-src www.owncloud.com www.owncloud.org";
+
+ $this->contentSecurityPolicy->addAllowedFontDomain('www.owncloud.com');
+ $this->contentSecurityPolicy->addAllowedFontDomain('www.owncloud.org');
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyDisallowFontDomain() {
+ $expectedPolicy = "default-src 'none'";
+
+ $this->contentSecurityPolicy->addAllowedFontDomain('www.owncloud.com');
+ $this->contentSecurityPolicy->disallowFontDomain('www.owncloud.com');
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyDisallowFontDomainMultiple() {
+ $expectedPolicy = "default-src 'none';font-src www.owncloud.com";
+
+ $this->contentSecurityPolicy->addAllowedFontDomain('www.owncloud.com');
+ $this->contentSecurityPolicy->disallowFontDomain('www.owncloud.org');
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyDisallowFontDomainMultipleStakes() {
+ $expectedPolicy = "default-src 'none'";
+
+ $this->contentSecurityPolicy->addAllowedFontDomain('www.owncloud.com');
+ $this->contentSecurityPolicy->disallowFontDomain('www.owncloud.org')->disallowFontDomain('www.owncloud.com');
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyConnectDomainValid() {
+ $expectedPolicy = "default-src 'none';connect-src www.owncloud.com";
+
+ $this->contentSecurityPolicy->addAllowedConnectDomain('www.owncloud.com');
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyConnectDomainValidMultiple() {
+ $expectedPolicy = "default-src 'none';connect-src www.owncloud.com www.owncloud.org";
+
+ $this->contentSecurityPolicy->addAllowedConnectDomain('www.owncloud.com');
+ $this->contentSecurityPolicy->addAllowedConnectDomain('www.owncloud.org');
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyDisallowConnectDomain() {
+ $expectedPolicy = "default-src 'none'";
+
+ $this->contentSecurityPolicy->addAllowedConnectDomain('www.owncloud.com');
+ $this->contentSecurityPolicy->disallowConnectDomain('www.owncloud.com');
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyDisallowConnectDomainMultiple() {
+ $expectedPolicy = "default-src 'none';connect-src www.owncloud.com";
+
+ $this->contentSecurityPolicy->addAllowedConnectDomain('www.owncloud.com');
+ $this->contentSecurityPolicy->disallowConnectDomain('www.owncloud.org');
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyDisallowConnectDomainMultipleStakes() {
+ $expectedPolicy = "default-src 'none'";
+
+ $this->contentSecurityPolicy->addAllowedConnectDomain('www.owncloud.com');
+ $this->contentSecurityPolicy->disallowConnectDomain('www.owncloud.org')->disallowConnectDomain('www.owncloud.com');
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyMediaDomainValid() {
+ $expectedPolicy = "default-src 'none';media-src www.owncloud.com";
+
+ $this->contentSecurityPolicy->addAllowedMediaDomain('www.owncloud.com');
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyMediaDomainValidMultiple() {
+ $expectedPolicy = "default-src 'none';media-src www.owncloud.com www.owncloud.org";
+
+ $this->contentSecurityPolicy->addAllowedMediaDomain('www.owncloud.com');
+ $this->contentSecurityPolicy->addAllowedMediaDomain('www.owncloud.org');
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyDisallowMediaDomain() {
+ $expectedPolicy = "default-src 'none'";
+
+ $this->contentSecurityPolicy->addAllowedMediaDomain('www.owncloud.com');
+ $this->contentSecurityPolicy->disallowMediaDomain('www.owncloud.com');
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyDisallowMediaDomainMultiple() {
+ $expectedPolicy = "default-src 'none';media-src www.owncloud.com";
+
+ $this->contentSecurityPolicy->addAllowedMediaDomain('www.owncloud.com');
+ $this->contentSecurityPolicy->disallowMediaDomain('www.owncloud.org');
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyDisallowMediaDomainMultipleStakes() {
+ $expectedPolicy = "default-src 'none'";
+
+ $this->contentSecurityPolicy->addAllowedMediaDomain('www.owncloud.com');
+ $this->contentSecurityPolicy->disallowMediaDomain('www.owncloud.org')->disallowMediaDomain('www.owncloud.com');
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyObjectDomainValid() {
+ $expectedPolicy = "default-src 'none';object-src www.owncloud.com";
+
+ $this->contentSecurityPolicy->addAllowedObjectDomain('www.owncloud.com');
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyObjectDomainValidMultiple() {
+ $expectedPolicy = "default-src 'none';object-src www.owncloud.com www.owncloud.org";
+
+ $this->contentSecurityPolicy->addAllowedObjectDomain('www.owncloud.com');
+ $this->contentSecurityPolicy->addAllowedObjectDomain('www.owncloud.org');
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyDisallowObjectDomain() {
+ $expectedPolicy = "default-src 'none'";
+
+ $this->contentSecurityPolicy->addAllowedObjectDomain('www.owncloud.com');
+ $this->contentSecurityPolicy->disallowObjectDomain('www.owncloud.com');
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyDisallowObjectDomainMultiple() {
+ $expectedPolicy = "default-src 'none';object-src www.owncloud.com";
+
+ $this->contentSecurityPolicy->addAllowedObjectDomain('www.owncloud.com');
+ $this->contentSecurityPolicy->disallowObjectDomain('www.owncloud.org');
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyDisallowObjectDomainMultipleStakes() {
+ $expectedPolicy = "default-src 'none'";
+
+ $this->contentSecurityPolicy->addAllowedObjectDomain('www.owncloud.com');
+ $this->contentSecurityPolicy->disallowObjectDomain('www.owncloud.org')->disallowObjectDomain('www.owncloud.com');
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetAllowedFrameDomain() {
+ $expectedPolicy = "default-src 'none';frame-src www.owncloud.com";
+
+ $this->contentSecurityPolicy->addAllowedFrameDomain('www.owncloud.com');
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyFrameDomainValidMultiple() {
+ $expectedPolicy = "default-src 'none';frame-src www.owncloud.com www.owncloud.org";
+
+ $this->contentSecurityPolicy->addAllowedFrameDomain('www.owncloud.com');
+ $this->contentSecurityPolicy->addAllowedFrameDomain('www.owncloud.org');
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyDisallowFrameDomain() {
+ $expectedPolicy = "default-src 'none'";
+
+ $this->contentSecurityPolicy->addAllowedFrameDomain('www.owncloud.com');
+ $this->contentSecurityPolicy->disallowFrameDomain('www.owncloud.com');
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyDisallowFrameDomainMultiple() {
+ $expectedPolicy = "default-src 'none';frame-src www.owncloud.com";
+
+ $this->contentSecurityPolicy->addAllowedFrameDomain('www.owncloud.com');
+ $this->contentSecurityPolicy->disallowFrameDomain('www.owncloud.org');
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyDisallowFrameDomainMultipleStakes() {
+ $expectedPolicy = "default-src 'none'";
+
+ $this->contentSecurityPolicy->addAllowedFrameDomain('www.owncloud.com');
+ $this->contentSecurityPolicy->disallowFrameDomain('www.owncloud.org')->disallowFrameDomain('www.owncloud.com');
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetAllowedChildSrcDomain() {
+ $expectedPolicy = "default-src 'none';child-src child.owncloud.com";
+
+ $this->contentSecurityPolicy->addAllowedChildSrcDomain('child.owncloud.com');
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyChildSrcValidMultiple() {
+ $expectedPolicy = "default-src 'none';child-src child.owncloud.com child.owncloud.org";
+
+ $this->contentSecurityPolicy->addAllowedChildSrcDomain('child.owncloud.com');
+ $this->contentSecurityPolicy->addAllowedChildSrcDomain('child.owncloud.org');
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyDisallowChildSrcDomain() {
+ $expectedPolicy = "default-src 'none'";
+
+ $this->contentSecurityPolicy->addAllowedChildSrcDomain('www.owncloud.com');
+ $this->contentSecurityPolicy->disallowChildSrcDomain('www.owncloud.com');
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyDisallowChildSrcDomainMultiple() {
+ $expectedPolicy = "default-src 'none';child-src www.owncloud.com";
+
+ $this->contentSecurityPolicy->addAllowedChildSrcDomain('www.owncloud.com');
+ $this->contentSecurityPolicy->disallowChildSrcDomain('www.owncloud.org');
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+
+ public function testGetPolicyDisallowChildSrcDomainMultipleStakes() {
+ $expectedPolicy = "default-src 'none'";
+
+ $this->contentSecurityPolicy->addAllowedChildSrcDomain('www.owncloud.com');
+ $this->contentSecurityPolicy->disallowChildSrcDomain('www.owncloud.org')->disallowChildSrcDomain('www.owncloud.com');
+ $this->assertSame($expectedPolicy, $this->contentSecurityPolicy->buildPolicy());
+ }
+}
diff --git a/tests/lib/appframework/middleware/security/SecurityMiddlewareTest.php b/tests/lib/appframework/middleware/security/SecurityMiddlewareTest.php
index 62223bbc2d9..9e71a3d0961 100644
--- a/tests/lib/appframework/middleware/security/SecurityMiddlewareTest.php
+++ b/tests/lib/appframework/middleware/security/SecurityMiddlewareTest.php
@@ -32,6 +32,7 @@ use OC\Appframework\Middleware\Security\Exceptions\NotAdminException;
use OC\Appframework\Middleware\Security\Exceptions\NotLoggedInException;
use OC\AppFramework\Middleware\Security\Exceptions\SecurityException;
use OC\AppFramework\Utility\ControllerMethodReflector;
+use OC\Security\CSP\ContentSecurityPolicy;
use OCP\AppFramework\Http\RedirectResponse;
use OCP\AppFramework\Http\JSONResponse;
use OCP\AppFramework\Http\TemplateResponse;
@@ -48,6 +49,7 @@ class SecurityMiddlewareTest extends \Test\TestCase {
private $logger;
private $navigationManager;
private $urlGenerator;
+ private $contentSecurityPolicyManager;
protected function setUp() {
parent::setUp();
@@ -72,6 +74,10 @@ class SecurityMiddlewareTest extends \Test\TestCase {
'OCP\IRequest')
->disableOriginalConstructor()
->getMock();
+ $this->contentSecurityPolicyManager = $this->getMockBuilder(
+ 'OC\Security\CSP\ContentSecurityPolicyManager')
+ ->disableOriginalConstructor()
+ ->getMock();
$this->middleware = $this->getMiddleware(true, true);
$this->secException = new SecurityException('hey', false);
$this->secAjaxException = new SecurityException('hey', true);
@@ -91,7 +97,8 @@ class SecurityMiddlewareTest extends \Test\TestCase {
$this->logger,
'files',
$isLoggedIn,
- $isAdminUser
+ $isAdminUser,
+ $this->contentSecurityPolicyManager
);
}
@@ -410,5 +417,31 @@ class SecurityMiddlewareTest extends \Test\TestCase {
$this->assertTrue($response instanceof JSONResponse);
}
-
+ public function testAfterController() {
+ $response = $this->getMockBuilder('\OCP\AppFramework\Http\Response')->disableOriginalConstructor()->getMock();
+ $defaultPolicy = new ContentSecurityPolicy();
+ $defaultPolicy->addAllowedImageDomain('defaultpolicy');
+ $currentPolicy = new ContentSecurityPolicy();
+ $currentPolicy->addAllowedConnectDomain('currentPolicy');
+ $mergedPolicy = new ContentSecurityPolicy();
+ $mergedPolicy->addAllowedMediaDomain('mergedPolicy');
+ $response
+ ->expects($this->exactly(2))
+ ->method('getContentSecurityPolicy')
+ ->willReturn($currentPolicy);
+ $this->contentSecurityPolicyManager
+ ->expects($this->once())
+ ->method('getDefaultPolicy')
+ ->willReturn($defaultPolicy);
+ $this->contentSecurityPolicyManager
+ ->expects($this->once())
+ ->method('mergePolicies')
+ ->with($defaultPolicy, $currentPolicy)
+ ->willReturn($mergedPolicy);
+ $response->expects($this->once())
+ ->method('setContentSecurityPolicy')
+ ->with($mergedPolicy);
+
+ $this->middleware->afterController($this->controller, 'test', $response);
+ }
}
diff --git a/tests/lib/comments/fakemanager.php b/tests/lib/comments/fakemanager.php
index e5cf58dda4f..7186529e718 100644
--- a/tests/lib/comments/fakemanager.php
+++ b/tests/lib/comments/fakemanager.php
@@ -19,7 +19,7 @@ class FakeManager implements \OCP\Comments\ICommentsManager {
\DateTime $notOlderThan = null
) {}
- public function getNumberOfCommentsForObject($objectType, $objectId) {}
+ public function getNumberOfCommentsForObject($objectType, $objectId, \DateTime $notOlderThan = null) {}
public function create($actorType, $actorId, $objectType, $objectId) {}
@@ -30,4 +30,12 @@ class FakeManager implements \OCP\Comments\ICommentsManager {
public function deleteReferencesOfActor($actorType, $actorId) {}
public function deleteCommentsAtObject($objectType, $objectId) {}
+
+ public function setReadMark($objectType, $objectId, \DateTime $dateTime, \OCP\IUser $user) {}
+
+ public function getReadMark($objectType, $objectId, \OCP\IUser $user) {}
+
+ public function deleteReadMarksFromUser(\OCP\IUser $user) {}
+
+ public function deleteReadMarksOnObject($objectType, $objectId) {}
}
diff --git a/tests/lib/comments/manager.php b/tests/lib/comments/manager.php
index cc2eebb64d1..a71f78f2818 100644
--- a/tests/lib/comments/manager.php
+++ b/tests/lib/comments/manager.php
@@ -561,4 +561,73 @@ class Test_Comments_Manager extends TestCase
$this->assertTrue($wasSuccessful);
}
+ public function testSetMarkRead() {
+ $user = $this->getMock('\OCP\IUser');
+ $user->expects($this->any())
+ ->method('getUID')
+ ->will($this->returnValue('alice'));
+
+ $dateTimeSet = new \DateTime();
+
+ $manager = $this->getManager();
+ $manager->setReadMark('robot', '36', $dateTimeSet, $user);
+
+ $dateTimeGet = $manager->getReadMark('robot', '36', $user);
+
+ $this->assertEquals($dateTimeGet, $dateTimeSet);
+ }
+
+ public function testSetMarkReadUpdate() {
+ $user = $this->getMock('\OCP\IUser');
+ $user->expects($this->any())
+ ->method('getUID')
+ ->will($this->returnValue('alice'));
+
+ $dateTimeSet = new \DateTime('yesterday');
+
+ $manager = $this->getManager();
+ $manager->setReadMark('robot', '36', $dateTimeSet, $user);
+
+ $dateTimeSet = new \DateTime('today');
+ $manager->setReadMark('robot', '36', $dateTimeSet, $user);
+
+ $dateTimeGet = $manager->getReadMark('robot', '36', $user);
+
+ $this->assertEquals($dateTimeGet, $dateTimeSet);
+ }
+
+ public function testReadMarkDeleteUser() {
+ $user = $this->getMock('\OCP\IUser');
+ $user->expects($this->any())
+ ->method('getUID')
+ ->will($this->returnValue('alice'));
+
+ $dateTimeSet = new \DateTime();
+
+ $manager = $this->getManager();
+ $manager->setReadMark('robot', '36', $dateTimeSet, $user);
+
+ $manager->deleteReadMarksFromUser($user);
+ $dateTimeGet = $manager->getReadMark('robot', '36', $user);
+
+ $this->assertNull($dateTimeGet);
+ }
+
+ public function testReadMarkDeleteObject() {
+ $user = $this->getMock('\OCP\IUser');
+ $user->expects($this->any())
+ ->method('getUID')
+ ->will($this->returnValue('alice'));
+
+ $dateTimeSet = new \DateTime();
+
+ $manager = $this->getManager();
+ $manager->setReadMark('robot', '36', $dateTimeSet, $user);
+
+ $manager->deleteReadMarksOnObject('robot', '36');
+ $dateTimeGet = $manager->getReadMark('robot', '36', $user);
+
+ $this->assertNull($dateTimeGet);
+ }
+
}
diff --git a/tests/lib/contacts/localadressbook.php b/tests/lib/contacts/localadressbook.php
deleted file mode 100644
index ad3c088e3cd..00000000000
--- a/tests/lib/contacts/localadressbook.php
+++ /dev/null
@@ -1,114 +0,0 @@
-<?php
-use OC\Contacts\LocalAddressBook;
-use OCP\IUser;
-
-/**
- * ownCloud
- *
- * @author Thomas Müller
- * @copyright 2014 Thomas Müller thomas.mueller@tmit.eu
- *
- * 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/>.
- */
-
-class Test_LocalAddressBook extends \Test\TestCase
-{
-
- public function testSearchFN() {
- $stub = $this->getMockForAbstractClass('\OCP\IUserManager', array('searchDisplayName'));
-
- $stub->expects($this->any())->method('searchDisplayName')->will($this->returnValue(array(
- new SimpleUserForTesting('tom', 'Thomas'),
- new SimpleUserForTesting('tomtom', 'Thomas T.'),
- )));
-
- $localAddressBook = new LocalAddressBook($stub);
-
- $result = $localAddressBook->search('tom', array('FN'), array());
- $this->assertEquals(2, count($result));
- }
-
- public function testSearchId() {
- $stub = $this->getMockForAbstractClass('\OCP\IUserManager', array('searchDisplayName'));
-
- $stub->expects($this->any())->method('search')->will($this->returnValue(array(
- new SimpleUserForTesting('tom', 'Thomas'),
- new SimpleUserForTesting('tomtom', 'Thomas T.'),
- )));
-
- $localAddressBook = new LocalAddressBook($stub);
-
- $result = $localAddressBook->search('tom', array('id'), array());
- $this->assertEquals(2, count($result));
- }
-}
-
-
-class SimpleUserForTesting implements IUser {
-
- private $uid;
- private $displayName;
-
- public function __construct($uid, $displayName) {
-
- $this->uid = $uid;
- $this->displayName = $displayName;
- }
-
- public function getUID() {
- return $this->uid;
- }
-
- public function getDisplayName() {
- return $this->displayName;
- }
-
- public function setDisplayName($displayName) {
- }
-
- public function getLastLogin() {
- }
-
- public function updateLastLoginTimestamp() {
- }
-
- public function delete() {
- }
-
- public function setPassword($password, $recoveryPassword = null) {
- }
-
- public function getHome() {
- }
-
- public function getBackendClassName() {
- }
-
- public function canChangeAvatar() {
- }
-
- public function canChangePassword() {
- }
-
- public function canChangeDisplayName() {
- }
-
- public function isEnabled() {
- }
-
- public function setEnabled($enabled) {
- }
-
- public function getEMailAddress() {
- }
-
- public function getAvatarImage($size) {
- }
-
- public function getCloudId() {
- }
-
- public function setEMailAddress($mailAddress) {
- }
-}
diff --git a/tests/lib/security/csp/ContentSecurityPolicyManagerTest.php b/tests/lib/security/csp/ContentSecurityPolicyManagerTest.php
new file mode 100644
index 00000000000..975c35d3780
--- /dev/null
+++ b/tests/lib/security/csp/ContentSecurityPolicyManagerTest.php
@@ -0,0 +1,66 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, 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/>
+ *
+ */
+
+use OC\Security\CSP\ContentSecurityPolicyManager;
+
+class ContentSecurityPolicyManagerTest extends \Test\TestCase {
+ /** @var ContentSecurityPolicyManager */
+ private $contentSecurityPolicyManager;
+
+ public function setUp() {
+ parent::setUp();
+ $this->contentSecurityPolicyManager = new ContentSecurityPolicyManager();
+ }
+
+ public function testAddDefaultPolicy() {
+ $this->contentSecurityPolicyManager->addDefaultPolicy(new \OCP\AppFramework\Http\ContentSecurityPolicy());
+ }
+
+ public function testGetDefaultPolicyWithPolicies() {
+ $policy = new \OCP\AppFramework\Http\ContentSecurityPolicy();
+ $policy->addAllowedFontDomain('mydomain.com');
+ $policy->addAllowedImageDomain('anotherdomain.de');
+ $this->contentSecurityPolicyManager->addDefaultPolicy($policy);
+ $policy = new \OCP\AppFramework\Http\ContentSecurityPolicy();
+ $policy->addAllowedFontDomain('example.com');
+ $policy->addAllowedImageDomain('example.org');
+ $policy->allowInlineScript(true);
+ $this->contentSecurityPolicyManager->addDefaultPolicy($policy);
+ $policy = new \OCP\AppFramework\Http\EmptyContentSecurityPolicy();
+ $policy->addAllowedChildSrcDomain('childdomain');
+ $policy->addAllowedFontDomain('anotherFontDomain');
+ $this->contentSecurityPolicyManager->addDefaultPolicy($policy);
+
+ $expected = new \OC\Security\CSP\ContentSecurityPolicy();
+ $expected->allowInlineScript(true);
+ $expected->addAllowedFontDomain('mydomain.com');
+ $expected->addAllowedFontDomain('example.com');
+ $expected->addAllowedFontDomain('anotherFontDomain');
+ $expected->addAllowedImageDomain('anotherdomain.de');
+ $expected->addAllowedImageDomain('example.org');
+ $expected->addAllowedChildSrcDomain('childdomain');
+ $expectedStringPolicy = 'default-src \'none\';script-src \'self\' \'unsafe-inline\' \'unsafe-eval\';style-src \'self\' \'unsafe-inline\';img-src \'self\' data: blob: anotherdomain.de example.org;font-src \'self\' mydomain.com example.com anotherFontDomain;connect-src \'self\';media-src \'self\';child-src childdomain';
+
+ $this->assertEquals($expected, $this->contentSecurityPolicyManager->getDefaultPolicy());
+ $this->assertSame($expectedStringPolicy, $this->contentSecurityPolicyManager->getDefaultPolicy()->buildPolicy());
+ }
+
+}
diff --git a/tests/lib/server.php b/tests/lib/server.php
index 8b2fec1f5a1..d13f9be0c9e 100644
--- a/tests/lib/server.php
+++ b/tests/lib/server.php
@@ -62,10 +62,12 @@ class Server extends \Test\TestCase {
['CapabilitiesManager', '\OC\CapabilitiesManager'],
['ContactsManager', '\OC\ContactsManager'],
['ContactsManager', '\OCP\Contacts\IManager'],
+ ['ContentSecurityPolicyManager', '\OC\Security\CSP\ContentSecurityPolicyManager'],
['CommentsManager', '\OCP\Comments\ICommentsManager'],
['Crypto', '\OC\Security\Crypto'],
['Crypto', '\OCP\Security\ICrypto'],
['CryptoWrapper', '\OC\Session\CryptoWrapper'],
+ ['CsrfTokenManager', '\OC\Security\CSRF\CsrfTokenManager'],
['DatabaseConnection', '\OC\DB\Connection'],
['DatabaseConnection', '\OCP\IDBConnection'],
diff --git a/tests/lib/share20/defaultshareprovidertest.php b/tests/lib/share20/defaultshareprovidertest.php
index 45f2bedcb63..504bd776036 100644
--- a/tests/lib/share20/defaultshareprovidertest.php
+++ b/tests/lib/share20/defaultshareprovidertest.php
@@ -247,6 +247,44 @@ class DefaultShareProviderTest extends \Test\TestCase {
$this->assertEquals('myTarget', $share->getTarget());
}
+ public function testGetShareByIdUserGroupShare() {
+ $id = $this->addShareToDB(\OCP\Share::SHARE_TYPE_GROUP, 'group0', 'user0', 'user0', 'file', 42, 'myTarget', 31, null, null);
+ $this->addShareToDB(2, 'user1', 'user0', 'user0', 'file', 42, 'userTarget', 0, null, null, $id);
+
+ $user0 = $this->getMock('OCP\IUser');
+ $user0->method('getUID')->willReturn('user0');
+ $user1 = $this->getMock('OCP\IUser');
+ $user1->method('getUID')->willReturn('user1');
+
+ $group0 = $this->getMock('OCP\IGroup');
+ $group0->method('inGroup')->with($user1)->willReturn(true);
+
+ $node = $this->getMock('\OCP\Files\Folder');
+ $node->method('getId')->willReturn(42);
+
+ $this->rootFolder->method('getUserFolder')->with('user0')->will($this->returnSelf());
+ $this->rootFolder->method('getById')->willReturn([$node]);
+
+ $this->userManager->method('get')->will($this->returnValueMap([
+ ['user0', $user0],
+ ['user1', $user1],
+ ]));
+ $this->groupManager->method('get')->with('group0')->willReturn($group0);
+
+ $share = $this->provider->getShareById($id, $user1);
+
+ $this->assertEquals($id, $share->getId());
+ $this->assertEquals(\OCP\Share::SHARE_TYPE_GROUP, $share->getShareType());
+ $this->assertSame($group0, $share->getSharedWith());
+ $this->assertSame($user0, $share->getSharedBy());
+ $this->assertSame($user0, $share->getShareOwner());
+ $this->assertSame($node, $share->getNode());
+ $this->assertEquals(0, $share->getPermissions());
+ $this->assertEquals(null, $share->getToken());
+ $this->assertEquals(null, $share->getExpirationDate());
+ $this->assertEquals('userTarget', $share->getTarget());
+ }
+
public function testGetShareByIdLinkShare() {
$qb = $this->dbConn->getQueryBuilder();
@@ -824,7 +862,7 @@ class DefaultShareProviderTest extends \Test\TestCase {
$this->rootFolder->method('getUserFolder')->with('shareOwner')->will($this->returnSelf());
$this->rootFolder->method('getById')->with(42)->willReturn([$file]);
- $share = $this->provider->getSharedWith($user, \OCP\Share::SHARE_TYPE_USER, 1 , 0);
+ $share = $this->provider->getSharedWith($user, \OCP\Share::SHARE_TYPE_USER, null, 1 , 0);
$this->assertCount(1, $share);
$share = $share[0];
@@ -894,7 +932,7 @@ class DefaultShareProviderTest extends \Test\TestCase {
$this->rootFolder->method('getUserFolder')->with('shareOwner')->will($this->returnSelf());
$this->rootFolder->method('getById')->with(42)->willReturn([$file]);
- $share = $this->provider->getSharedWith($user, \OCP\Share::SHARE_TYPE_GROUP, 20 , 1);
+ $share = $this->provider->getSharedWith($user, \OCP\Share::SHARE_TYPE_GROUP, null, 20 , 1);
$this->assertCount(1, $share);
$share = $share[0];
@@ -979,7 +1017,7 @@ class DefaultShareProviderTest extends \Test\TestCase {
$this->rootFolder->method('getUserFolder')->with('shareOwner')->will($this->returnSelf());
$this->rootFolder->method('getById')->with(42)->willReturn([$file]);
- $share = $this->provider->getSharedWith($user, \OCP\Share::SHARE_TYPE_GROUP, -1, 0);
+ $share = $this->provider->getSharedWith($user, \OCP\Share::SHARE_TYPE_GROUP, null, -1, 0);
$this->assertCount(1, $share);
$share = $share[0];
@@ -992,6 +1030,78 @@ class DefaultShareProviderTest extends \Test\TestCase {
$this->assertSame('userTarget', $share->getTarget());
}
+ public function testGetSharedWithUserWithNode() {
+ $this->addShareToDB(\OCP\Share::SHARE_TYPE_USER, 'user0', 'user1', 'user1',
+ 'file', 42, 'myTarget', 31, null, null, null);
+ $id = $this->addShareToDB(\OCP\Share::SHARE_TYPE_USER, 'user0', 'user1', 'user1',
+ 'file', 43, 'myTarget', 31, null, null, null);
+
+ $user0 = $this->getMock('\OCP\IUser');
+ $user0->method('getUID')->willReturn('user0');
+ $user1 = $this->getMock('\OCP\IUser');
+ $user1->method('getUID')->willReturn('user1');
+
+ $this->userManager->method('get')->willReturnMap([
+ ['user0', $user0],
+ ['user1', $user1],
+ ]);
+
+ $file = $this->getMock('\OCP\Files\File');
+ $file->method('getId')->willReturn(43);
+ $this->rootFolder->method('getUserFolder')->with('user1')->will($this->returnSelf());
+ $this->rootFolder->method('getById')->with(43)->willReturn([$file]);
+
+ $share = $this->provider->getSharedWith($user0, \OCP\Share::SHARE_TYPE_USER, $file, -1, 0);
+ $this->assertCount(1, $share);
+
+ $share = $share[0];
+ $this->assertEquals($id, $share->getId());
+ $this->assertSame($user0, $share->getSharedWith());
+ $this->assertSame($user1, $share->getShareOwner());
+ $this->assertSame($user1, $share->getSharedBy());
+ $this->assertSame($file, $share->getNode());
+ $this->assertEquals(\OCP\Share::SHARE_TYPE_USER, $share->getShareType());
+ }
+
+ public function testGetSharedWithGroupWithNode() {
+ $this->addShareToDB(\OCP\Share::SHARE_TYPE_GROUP, 'group0', 'user1', 'user1',
+ 'file', 42, 'myTarget', 31, null, null, null);
+ $id = $this->addShareToDB(\OCP\Share::SHARE_TYPE_GROUP, 'group0', 'user1', 'user1',
+ 'file', 43, 'myTarget', 31, null, null, null);
+
+ $user0 = $this->getMock('\OCP\IUser');
+ $user0->method('getUID')->willReturn('user0');
+ $user1 = $this->getMock('\OCP\IUser');
+ $user1->method('getUID')->willReturn('user1');
+
+ $this->userManager->method('get')->willReturnMap([
+ ['user0', $user0],
+ ['user1', $user1],
+ ]);
+
+ $group0 = $this->getMock('\OCP\IGroup');
+ $group0->method('getGID')->willReturn('group0');
+
+ $this->groupManager->method('get')->with('group0')->willReturn($group0);
+ $this->groupManager->method('getUserGroups')->with($user0)->willReturn([$group0]);
+
+ $node = $this->getMock('\OCP\Files\Folder');
+ $node->method('getId')->willReturn(43);
+ $this->rootFolder->method('getUserFolder')->with('user1')->will($this->returnSelf());
+ $this->rootFolder->method('getById')->with(43)->willReturn([$node]);
+
+ $share = $this->provider->getSharedWith($user0, \OCP\Share::SHARE_TYPE_GROUP, $node, -1, 0);
+ $this->assertCount(1, $share);
+
+ $share = $share[0];
+ $this->assertEquals($id, $share->getId());
+ $this->assertSame($group0, $share->getSharedWith());
+ $this->assertSame($user1, $share->getShareOwner());
+ $this->assertSame($user1, $share->getSharedBy());
+ $this->assertSame($node, $share->getNode());
+ $this->assertEquals(\OCP\Share::SHARE_TYPE_GROUP, $share->getShareType());
+ }
+
public function testGetSharesBy() {
$qb = $this->dbConn->getQueryBuilder();
$qb->insert('share')
@@ -1782,6 +1892,75 @@ class DefaultShareProviderTest extends \Test\TestCase {
$stmt->closeCursor();
+ }
+
+ public function testMoveUserShare() {
+ $id = $this->addShareToDB(\OCP\Share::SHARE_TYPE_USER, 'user0', 'user1', 'user1', 'file',
+ 42, 'mytaret', 31, null, null);
+
+ $user0 = $this->getMock('\OCP\IUser');
+ $user0->method('getUID')->willReturn('user0');
+ $user1 = $this->getMock('\OCP\IUser');
+ $user1->method('getUID')->willReturn('user1');
+
+ $this->userManager->method('get')->will($this->returnValueMap([
+ ['user0', $user0],
+ ['user1', $user1],
+ ]));
+
+ $file = $this->getMock('\OCP\Files\File');
+ $file->method('getId')->willReturn(42);
+
+ $this->rootFolder->method('getUserFolder')->with('user1')->will($this->returnSelf());
+ $this->rootFolder->method('getById')->willReturn([$file]);
+
+ $share = $this->provider->getShareById($id, null);
+
+ $share->setTarget('/newTarget');
+ $this->provider->move($share, $user0);
+
+ $share = $this->provider->getShareById($id, null);
+ $this->assertSame('/newTarget', $share->getTarget());
+ }
+
+ public function testMoveGroupShare() {
+ $id = $this->addShareToDB(\OCP\Share::SHARE_TYPE_GROUP, 'group0', 'user1', 'user1', 'file',
+ 42, 'mytaret', 31, null, null);
+
+ $user0 = $this->getMock('\OCP\IUser');
+ $user0->method('getUID')->willReturn('user0');
+ $user1 = $this->getMock('\OCP\IUser');
+ $user1->method('getUID')->willReturn('user1');
+
+ $group0 = $this->getMock('\OCP\IGroup');
+ $group0->method('getGID')->willReturn('group0');
+ $group0->method('inGroup')->with($user0)->willReturn(true);
+
+ $this->groupManager->method('get')->with('group0')->willReturn($group0);
+
+ $this->userManager->method('get')->will($this->returnValueMap([
+ ['user0', $user0],
+ ['user1', $user1],
+ ]));
+
+ $folder = $this->getMock('\OCP\Files\Folder');
+ $folder->method('getId')->willReturn(42);
+
+ $this->rootFolder->method('getUserFolder')->with('user1')->will($this->returnSelf());
+ $this->rootFolder->method('getById')->willReturn([$folder]);
+
+ $share = $this->provider->getShareById($id, $user0);
+
+ $share->setTarget('/newTarget');
+ $this->provider->move($share, $user0);
+
+ $share = $this->provider->getShareById($id, $user0);
+ $this->assertSame('/newTarget', $share->getTarget());
+
+ $share->setTarget('/ultraNewTarget');
+ $this->provider->move($share, $user0);
+ $share = $this->provider->getShareById($id, $user0);
+ $this->assertSame('/ultraNewTarget', $share->getTarget());
}
}
diff --git a/tests/lib/share20/managertest.php b/tests/lib/share20/managertest.php
index 53b1374eade..6043d30a0bb 100644
--- a/tests/lib/share20/managertest.php
+++ b/tests/lib/share20/managertest.php
@@ -607,7 +607,10 @@ class ManagerTest extends \Test\TestCase {
$past = new \DateTime();
$past->sub(new \DateInterval('P1D'));
- $this->invokePrivate($this->manager, 'validateExpirationDate', [$past]);
+ $share = $this->manager->newShare();
+ $share->setExpirationDate($past);
+
+ $this->invokePrivate($this->manager, 'validateExpirationDate', [$share]);
}
/**
@@ -615,12 +618,14 @@ class ManagerTest extends \Test\TestCase {
* @expectedExceptionMessage Expiration date is enforced
*/
public function testvalidateExpirationDateEnforceButNotSet() {
+ $share = $this->manager->newShare();
+
$this->config->method('getAppValue')
->will($this->returnValueMap([
['core', 'shareapi_enforce_expire_date', 'no', 'yes'],
]));
- $this->invokePrivate($this->manager, 'validateExpirationDate', [null]);
+ $this->invokePrivate($this->manager, 'validateExpirationDate', [$share]);
}
public function testvalidateExpirationDateEnforceToFarIntoFuture() {
@@ -628,6 +633,9 @@ class ManagerTest extends \Test\TestCase {
$future = new \DateTime();
$future->add(new \DateInterval('P7D'));
+ $share = $this->manager->newShare();
+ $share->setExpirationDate($future);
+
$this->config->method('getAppValue')
->will($this->returnValueMap([
['core', 'shareapi_enforce_expire_date', 'no', 'yes'],
@@ -635,7 +643,7 @@ class ManagerTest extends \Test\TestCase {
]));
try {
- $this->invokePrivate($this->manager, 'validateExpirationDate', [$future]);
+ $this->invokePrivate($this->manager, 'validateExpirationDate', [$share]);
} catch (\OC\HintException $e) {
$this->assertEquals('Cannot set expiration date more than 3 days in the future', $e->getMessage());
$this->assertEquals('Cannot set expiration date more than 3 days in the future', $e->getHint());
@@ -648,31 +656,61 @@ class ManagerTest extends \Test\TestCase {
$future = new \DateTime();
$future->add(new \DateInterval('P2D'));
$future->setTime(0,0,0);
- $expected = $future->format(\DateTime::ISO8601);
+
+ $expected = clone $future;
$future->setTime(1,2,3);
+ $share = $this->manager->newShare();
+ $share->setExpirationDate($future);
+
$this->config->method('getAppValue')
->will($this->returnValueMap([
['core', 'shareapi_enforce_expire_date', 'no', 'yes'],
['core', 'shareapi_expire_after_n_days', '7', '3'],
]));
- $future = $this->invokePrivate($this->manager, 'validateExpirationDate', [$future]);
+ $hookListner = $this->getMockBuilder('Dummy')->setMethods(['listener'])->getMock();
+ \OCP\Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListner, 'listener');
+ $hookListner->expects($this->once())->method('listener')->with($this->callback(function ($data) use ($future) {
+ return $data['expirationDate'] == $future;
+ }));
- $this->assertEquals($expected, $future->format(\DateTime::ISO8601));
+ $future = $this->invokePrivate($this->manager, 'validateExpirationDate', [$share]);
+
+ $this->assertEquals($expected, $future);
}
public function testvalidateExpirationDateNoDateNoDefaultNull() {
$date = new \DateTime();
$date->add(new \DateInterval('P5D'));
- $res = $this->invokePrivate($this->manager, 'validateExpirationDate', [$date]);
+ $expected = clone $date;
+ $expected->setTime(0,0,0);
+
+ $share = $this->manager->newShare();
+ $share->setExpirationDate($date);
+
+ $hookListner = $this->getMockBuilder('Dummy')->setMethods(['listener'])->getMock();
+ \OCP\Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListner, 'listener');
+ $hookListner->expects($this->once())->method('listener')->with($this->callback(function ($data) use ($expected) {
+ return $data['expirationDate'] == $expected;
+ }));
+
+ $res = $this->invokePrivate($this->manager, 'validateExpirationDate', [$share]);
- $this->assertEquals($date, $res);
+ $this->assertEquals($expected, $res);
}
public function testvalidateExpirationDateNoDateNoDefault() {
- $date = $this->invokePrivate($this->manager, 'validateExpirationDate', [null]);
+ $hookListner = $this->getMockBuilder('Dummy')->setMethods(['listener'])->getMock();
+ \OCP\Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListner, 'listener');
+ $hookListner->expects($this->once())->method('listener')->with($this->callback(function ($data) {
+ return $data['expirationDate'] === null;
+ }));
+
+ $share = $this->manager->newShare();
+
+ $date = $this->invokePrivate($this->manager, 'validateExpirationDate', [$share]);
$this->assertNull($date);
}
@@ -682,15 +720,70 @@ class ManagerTest extends \Test\TestCase {
$future->add(new \DateInterval('P3D'));
$future->setTime(0,0,0);
+ $expected = clone $future;
+
+ $share = $this->manager->newShare();
+ $share->setExpirationDate($future);
+
$this->config->method('getAppValue')
->will($this->returnValueMap([
['core', 'shareapi_default_expire_date', 'no', 'yes'],
['core', 'shareapi_expire_after_n_days', '7', '3'],
]));
- $date = $this->invokePrivate($this->manager, 'validateExpirationDate', [null]);
+ $hookListner = $this->getMockBuilder('Dummy')->setMethods(['listener'])->getMock();
+ \OCP\Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListner, 'listener');
+ $hookListner->expects($this->once())->method('listener')->with($this->callback(function ($data) use ($expected) {
+ return $data['expirationDate'] == $expected;
+ }));
+
+ $this->invokePrivate($this->manager, 'validateExpirationDate', [$share]);
+
+ $this->assertEquals($expected, $share->getExpirationDate());
+ }
+
+ public function testValidateExpirationDateHookModification() {
+ $nextWeek = new \DateTime();
+ $nextWeek->add(new \DateInterval('P7D'));
+ $nextWeek->setTime(0,0,0);
+
+ $save = clone $nextWeek;
+
+ $hookListner = $this->getMockBuilder('Dummy')->setMethods(['listener'])->getMock();
+ \OCP\Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListner, 'listener');
+ $hookListner->expects($this->once())->method('listener')->will($this->returnCallback(function ($data) {
+ $data['expirationDate']->sub(new \DateInterval('P2D'));
+ }));
- $this->assertEquals($future->format(\DateTime::ISO8601), $date->format(\DateTime::ISO8601));
+ $share = $this->manager->newShare();
+ $share->setExpirationDate($nextWeek);
+
+ $this->invokePrivate($this->manager, 'validateExpirationDate', [$share]);
+
+ $save->sub(new \DateInterval('P2D'));
+ $this->assertEquals($save, $share->getExpirationDate());
+ }
+
+ /**
+ * @expectedException \Exception
+ * @expectedExceptionMessage Invalid date!
+ */
+ public function testValidateExpirationDateHookException() {
+ $nextWeek = new \DateTime();
+ $nextWeek->add(new \DateInterval('P7D'));
+ $nextWeek->setTime(0,0,0);
+
+ $share = $this->manager->newShare();
+ $share->setExpirationDate($nextWeek);
+
+ $hookListner = $this->getMockBuilder('Dummy')->setMethods(['listener'])->getMock();
+ \OCP\Util::connectHook('\OC\Share', 'verifyExpirationDate', $hookListner, 'listener');
+ $hookListner->expects($this->once())->method('listener')->will($this->returnCallback(function ($data) {
+ $data['accepted'] = false;
+ $data['message'] = 'Invalid date!';
+ }));
+
+ $this->invokePrivate($this->manager, 'validateExpirationDate', [$share]);
}
/**
@@ -1332,7 +1425,7 @@ class ManagerTest extends \Test\TestCase {
$date = new \DateTime();
- $share = new \OC\Share20\Share();
+ $share = $this->manager->newShare();
$share->setShareType(\OCP\Share::SHARE_TYPE_LINK)
->setNode($path)
->setSharedBy($sharedBy)
@@ -1355,8 +1448,7 @@ class ManagerTest extends \Test\TestCase {
->with($path);
$manager->expects($this->once())
->method('validateExpirationDate')
- ->with($date)
- ->will($this->returnArgument(0));
+ ->with($share);
$manager->expects($this->once())
->method('verifyPassword')
->with('password');
@@ -1553,6 +1645,27 @@ class ManagerTest extends \Test\TestCase {
$this->assertTrue($this->manager->checkPassword($share, 'password'));
}
+ public function testCheckPasswordUpdateShare() {
+ $share = $this->manager->newShare();
+ $share->setShareType(\OCP\Share::SHARE_TYPE_LINK)
+ ->setPassword('passwordHash');
+
+ $this->hasher->method('verify')->with('password', 'passwordHash', '')
+ ->will($this->returnCallback(function($pass, $hash, &$newHash) {
+ $newHash = 'newHash';
+
+ return true;
+ }));
+
+ $this->defaultProvider->expects($this->once())
+ ->method('update')
+ ->with($this->callback(function (\OCP\Share\IShare $share) {
+ return $share->getPassword() === 'newHash';
+ }));
+
+ $this->assertTrue($this->manager->checkPassword($share, 'password'));
+ }
+
/**
* @expectedException Exception
* @expectedExceptionMessage The Share API is disabled
@@ -1761,14 +1874,10 @@ class ManagerTest extends \Test\TestCase {
$tomorrow->setTime(0,0,0);
$tomorrow->add(new \DateInterval('P1D'));
- $manager->expects($this->once())->method('canShare')->willReturn(true);
- $manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
- $manager->expects($this->once())->method('validateExpirationDate')->with($tomorrow)->willReturn($tomorrow);
-
$file = $this->getMock('OCP\Files\File', [], [], 'File');
$file->method('getId')->willReturn(100);
- $share = new \OC\Share20\Share();
+ $share = $this->manager->newShare();
$share->setProviderId('foo')
->setId('42')
->setShareType(\OCP\Share::SHARE_TYPE_LINK)
@@ -1778,6 +1887,10 @@ class ManagerTest extends \Test\TestCase {
->setExpirationDate($tomorrow)
->setNode($file);
+ $manager->expects($this->once())->method('canShare')->willReturn(true);
+ $manager->expects($this->once())->method('getShareById')->with('foo:42')->willReturn($originalShare);
+ $manager->expects($this->once())->method('validateExpirationDate')->with($share);
+
$this->defaultProvider->expects($this->once())
->method('update')
->with($share)
@@ -1795,6 +1908,79 @@ class ManagerTest extends \Test\TestCase {
$manager->updateShare($share);
}
+
+ /**
+ * @expectedException \InvalidArgumentException
+ * @expectedExceptionMessage Can't change target of link share
+ */
+ public function testMoveShareLink() {
+ $share = $this->manager->newShare();
+ $share->setShareType(\OCP\Share::SHARE_TYPE_LINK);
+
+ $recipient = $this->getMock('\OCP\IUser');
+
+ $this->manager->moveShare($share, $recipient);
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ * @expectedExceptionMessage Invalid recipient
+ */
+ public function testMoveShareUserNotRecipient() {
+ $share = $this->manager->newShare();
+ $share->setShareType(\OCP\Share::SHARE_TYPE_USER);
+
+ $sharedWith = $this->getMock('\OCP\IUser');
+ $share->setSharedWith($sharedWith);
+
+ $recipient = $this->getMock('\OCP\IUser');
+
+ $this->manager->moveShare($share, $recipient);
+ }
+
+ public function testMoveShareUser() {
+ $share = $this->manager->newShare();
+ $share->setShareType(\OCP\Share::SHARE_TYPE_USER);
+
+ $recipient = $this->getMock('\OCP\IUser');
+ $share->setSharedWith($recipient);
+
+ $this->defaultProvider->method('move')->with($share, $recipient)->will($this->returnArgument(0));
+
+ $this->manager->moveShare($share, $recipient);
+ }
+
+ /**
+ * @expectedException \InvalidArgumentException
+ * @expectedExceptionMessage Invalid recipient
+ */
+ public function testMoveShareGroupNotRecipient() {
+ $share = $this->manager->newShare();
+ $share->setShareType(\OCP\Share::SHARE_TYPE_GROUP);
+
+ $sharedWith = $this->getMock('\OCP\IGroup');
+ $share->setSharedWith($sharedWith);
+
+ $recipient = $this->getMock('\OCP\IUser');
+ $sharedWith->method('inGroup')->with($recipient)->willReturn(false);
+
+ $this->manager->moveShare($share, $recipient);
+ }
+
+ public function testMoveShareGroup() {
+ $share = $this->manager->newShare();
+ $share->setShareType(\OCP\Share::SHARE_TYPE_GROUP);
+
+ $sharedWith = $this->getMock('\OCP\IGroup');
+ $share->setSharedWith($sharedWith);
+
+ $recipient = $this->getMock('\OCP\IUser');
+ $sharedWith->method('inGroup')->with($recipient)->willReturn(true);
+
+ $this->defaultProvider->method('move')->with($share, $recipient)->will($this->returnArgument(0));
+
+ $this->manager->moveShare($share, $recipient);
+ }
}
class DummyPassword {
diff --git a/tests/lib/systemtag/systemtagmanagertest.php b/tests/lib/systemtag/systemtagmanagertest.php
index 97c072f33f6..64220205ade 100644
--- a/tests/lib/systemtag/systemtagmanagertest.php
+++ b/tests/lib/systemtag/systemtagmanagertest.php
@@ -15,6 +15,7 @@ use OC\SystemTag\SystemTagObjectMapper;
use OCP\IDBConnection;
use OCP\SystemTag\ISystemTag;
use OCP\SystemTag\ISystemTagManager;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Test\TestCase;
/**
@@ -35,11 +36,23 @@ class SystemTagManagerTest extends TestCase {
*/
private $connection;
+ /**
+ * @var EventDispatcherInterface
+ */
+ private $dispatcher;
+
public function setUp() {
parent::setUp();
$this->connection = \OC::$server->getDatabaseConnection();
- $this->tagManager = new SystemTagManager($this->connection);
+
+ $this->dispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')
+ ->getMock();
+
+ $this->tagManager = new SystemTagManager(
+ $this->connection,
+ $this->dispatcher
+ );
$this->pruneTagsTables();
}
@@ -378,7 +391,7 @@ class SystemTagManagerTest extends TestCase {
$tag1 = $this->tagManager->createTag('one', true, false);
$tag2 = $this->tagManager->createTag('two', true, true);
- $tagMapper = new SystemTagObjectMapper($this->connection, $this->tagManager);
+ $tagMapper = new SystemTagObjectMapper($this->connection, $this->tagManager, $this->dispatcher);
$tagMapper->assignTags(1, 'testtype', $tag1->getId());
$tagMapper->assignTags(1, 'testtype', $tag2->getId());
diff --git a/tests/lib/systemtag/systemtagobjectmappertest.php b/tests/lib/systemtag/systemtagobjectmappertest.php
index 4ea80c216ed..5c8204f6a87 100644
--- a/tests/lib/systemtag/systemtagobjectmappertest.php
+++ b/tests/lib/systemtag/systemtagobjectmappertest.php
@@ -10,14 +10,15 @@
namespace Test\SystemTag;
+use OC\SystemTag\SystemTag;
use OC\SystemTag\SystemTagManager;
use OC\SystemTag\SystemTagObjectMapper;
-use \OCP\SystemTag\ISystemTag;
-use \OCP\SystemTag\ISystemTagManager;
-use \OCP\SystemTag\ISystemTagObjectMapper;
-use \OCP\SystemTag\TagNotFoundException;
-use \OCP\IDBConnection;
-use \OC\SystemTag\SystemTag;
+use OCP\IDBConnection;
+use OCP\SystemTag\ISystemTag;
+use OCP\SystemTag\ISystemTagManager;
+use OCP\SystemTag\ISystemTagObjectMapper;
+use OCP\SystemTag\TagNotFoundException;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Test\TestCase;
/**
@@ -44,6 +45,11 @@ class SystemTagObjectMapperTest extends TestCase {
private $connection;
/**
+ * @var EventDispatcherInterface
+ */
+ private $dispatcher;
+
+ /**
* @var ISystemTag
*/
private $tag1;
@@ -67,7 +73,14 @@ class SystemTagObjectMapperTest extends TestCase {
$this->tagManager = $this->getMockBuilder('OCP\SystemTag\ISystemTagManager')
->getMock();
- $this->tagMapper = new SystemTagObjectMapper($this->connection, $this->tagManager);
+ $this->dispatcher = $this->getMockBuilder('Symfony\Component\EventDispatcher\EventDispatcherInterface')
+ ->getMock();
+
+ $this->tagMapper = new SystemTagObjectMapper(
+ $this->connection,
+ $this->tagManager,
+ $this->dispatcher
+ );
$this->tag1 = new SystemTag(1, 'testtag1', false, false);
$this->tag2 = new SystemTag(2, 'testtag2', true, false);
diff --git a/tests/settings/controller/CheckSetupControllerTest.php b/tests/settings/controller/CheckSetupControllerTest.php
index 08d4135fa12..c22ddb2e120 100644
--- a/tests/settings/controller/CheckSetupControllerTest.php
+++ b/tests/settings/controller/CheckSetupControllerTest.php
@@ -326,10 +326,6 @@ class CheckSetupControllerTest extends TestCase {
$this->clientService->expects($this->once())
->method('newClient')
->will($this->returnValue($client));
-
- $this->util->expects($this->once())
- ->method('isHtaccessWorking')
- ->will($this->returnValue(true));
$this->urlGenerator->expects($this->at(0))
->method('linkToDocs')
->with('admin-performance')
@@ -347,7 +343,6 @@ class CheckSetupControllerTest extends TestCase {
$expected = new DataResponse(
[
'serverHasInternetConnection' => false,
- 'dataDirectoryProtected' => true,
'isMemcacheConfigured' => true,
'memcacheDocs' => 'http://doc.owncloud.org/server/go.php?to=admin-performance',
'isUrandomAvailable' => self::invokePrivate($this->checkSetupController, 'isUrandomAvailable'),
diff --git a/version.php b/version.php
index a82e4ad1c0f..0b7eb6f79d2 100644
--- a/version.php
+++ b/version.php
@@ -25,7 +25,7 @@
// We only can count up. The 4. digit is only for the internal patchlevel to trigger DB upgrades
// between betas, final and RCs. This is _not_ the public version number. Reset minor/patchlevel
// when updating major/minor version number.
-$OC_Version = array(9, 0, 0, 7);
+$OC_Version = array(9, 0, 0, 8);
// The human readable string
$OC_VersionString = '9.0 pre alpha';