summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/encryption/appinfo/app.php6
-rw-r--r--apps/encryption/appinfo/application.php21
-rw-r--r--apps/encryption/appinfo/routes.php5
-rw-r--r--apps/encryption/controller/recoverycontroller.php28
-rw-r--r--apps/encryption/hooks/contracts/ihook.php4
-rw-r--r--apps/encryption/hooks/userhooks.php6
-rw-r--r--apps/encryption/js/detect-migration.js33
-rw-r--r--apps/encryption/lib/crypto/crypt.php48
-rw-r--r--apps/encryption/lib/crypto/encryption.php34
-rw-r--r--apps/encryption/lib/exceptions/multikeydecryptexception.php20
-rw-r--r--apps/encryption/lib/exceptions/multikeyencryptexception.php20
-rw-r--r--apps/encryption/lib/exceptions/privatekeymissingexception.php8
-rw-r--r--apps/encryption/lib/exceptions/publickeymissingexception.php21
-rw-r--r--apps/encryption/lib/hookmanager.php9
-rw-r--r--apps/encryption/lib/keymanager.php28
-rw-r--r--apps/encryption/lib/recovery.php6
-rw-r--r--apps/encryption/lib/session.php31
-rw-r--r--apps/encryption/lib/users/setup.php35
-rw-r--r--apps/encryption/lib/util.php5
-rw-r--r--apps/encryption/settings/settings-admin.php23
-rw-r--r--apps/encryption/settings/settings-personal.php23
-rw-r--r--apps/encryption/tests/hooks/UserHooksTest.php217
-rw-r--r--apps/encryption/tests/lib/HookManagerTest.php4
-rw-r--r--apps/encryption/tests/lib/KeyManagerTest.php22
-rw-r--r--apps/encryption/tests/lib/RecoveryTest.php4
-rw-r--r--apps/encryption/tests/lib/SessionTest.php5
-rw-r--r--apps/encryption/tests/lib/UtilTest.php4
-rw-r--r--apps/encryption/tests/lib/crypto/encryptionTest.php26
-rw-r--r--apps/encryption/tests/lib/users/SetupTest.php4
-rw-r--r--apps/encryption_dummy/lib/dummymodule.php6
-rw-r--r--apps/files/appinfo/app.php7
-rw-r--r--apps/files/css/files.css21
-rw-r--r--apps/files/img/delete.pngbin0 -> 209 bytes
-rw-r--r--apps/files/img/delete.svg4
-rw-r--r--apps/files/img/external.pngbin0 -> 369 bytes
-rw-r--r--apps/files/img/external.svg4
-rw-r--r--apps/files/img/folder.pngbin0 -> 160 bytes
-rw-r--r--apps/files/img/folder.svg6
-rw-r--r--apps/files/img/public.pngbin0 -> 297 bytes
-rw-r--r--apps/files/img/public.svg7
-rw-r--r--apps/files/img/share.pngbin0 -> 365 bytes
-rw-r--r--apps/files/img/share.svg6
-rw-r--r--apps/files/img/star.pngbin0 -> 373 bytes
-rw-r--r--apps/files/img/star.svg6
-rw-r--r--apps/files/lib/activity.php88
-rw-r--r--apps/files/lib/activityhelper.php84
-rw-r--r--apps/files/templates/appnavigation.php11
-rw-r--r--apps/files/tests/activitytest.php297
-rw-r--r--apps/files/tests/ajax_rename.php11
-rw-r--r--apps/files_external/lib/sftp_key.php9
-rw-r--r--apps/files_external/service/globalstoragesservice.php11
-rw-r--r--apps/files_external/service/storagesservice.php6
-rw-r--r--apps/files_sharing/api/local.php43
-rw-r--r--apps/files_sharing/appinfo/app.php4
-rw-r--r--apps/files_sharing/js/settings-admin.js1
-rw-r--r--apps/files_sharing/lib/deleteorphanedsharesjob.php59
-rw-r--r--apps/files_sharing/lib/helper.php1
-rw-r--r--apps/files_sharing/lib/updater.php35
-rw-r--r--apps/files_sharing/templates/settings-admin.php3
-rw-r--r--apps/files_sharing/tests/deleteorphanedsharesjobtest.php162
-rw-r--r--apps/files_trashbin/tests/storage.php8
-rw-r--r--apps/user_ldap/ajax/getNewServerConfigPrefix.php15
-rw-r--r--apps/user_ldap/ajax/wizard.php23
-rw-r--r--apps/user_ldap/appinfo/version2
-rw-r--r--apps/user_ldap/css/settings.css62
-rw-r--r--apps/user_ldap/img/copy.pngbin0 -> 346 bytes
-rw-r--r--apps/user_ldap/img/copy.svg120
-rw-r--r--apps/user_ldap/js/experiencedAdmin.js92
-rw-r--r--apps/user_ldap/js/ldapFilter.js193
-rw-r--r--apps/user_ldap/js/settings.js1205
-rw-r--r--apps/user_ldap/js/wizard/configModel.js606
-rw-r--r--apps/user_ldap/js/wizard/controller.js56
-rw-r--r--apps/user_ldap/js/wizard/view.js437
-rw-r--r--apps/user_ldap/js/wizard/wizard.js80
-rw-r--r--apps/user_ldap/js/wizard/wizardDetectorAvailableAttributes.js59
-rw-r--r--apps/user_ldap/js/wizard/wizardDetectorBaseDN.js52
-rw-r--r--apps/user_ldap/js/wizard/wizardDetectorClearGroupMappings.js30
-rw-r--r--apps/user_ldap/js/wizard/wizardDetectorClearUserMappings.js30
-rw-r--r--apps/user_ldap/js/wizard/wizardDetectorEmailAttribute.js38
-rw-r--r--apps/user_ldap/js/wizard/wizardDetectorFeatureAbstract.js52
-rw-r--r--apps/user_ldap/js/wizard/wizardDetectorFilterGroup.js31
-rw-r--r--apps/user_ldap/js/wizard/wizardDetectorFilterLogin.js33
-rw-r--r--apps/user_ldap/js/wizard/wizardDetectorFilterUser.js32
-rw-r--r--apps/user_ldap/js/wizard/wizardDetectorGeneric.js117
-rw-r--r--apps/user_ldap/js/wizard/wizardDetectorGroupCount.js27
-rw-r--r--apps/user_ldap/js/wizard/wizardDetectorGroupObjectClasses.js29
-rw-r--r--apps/user_ldap/js/wizard/wizardDetectorGroupsForGroups.js29
-rw-r--r--apps/user_ldap/js/wizard/wizardDetectorGroupsForUsers.js29
-rw-r--r--apps/user_ldap/js/wizard/wizardDetectorPort.js44
-rw-r--r--apps/user_ldap/js/wizard/wizardDetectorQueue.js89
-rw-r--r--apps/user_ldap/js/wizard/wizardDetectorSimpleRequestAbstract.js44
-rw-r--r--apps/user_ldap/js/wizard/wizardDetectorTestAbstract.js63
-rw-r--r--apps/user_ldap/js/wizard/wizardDetectorTestBaseDN.js29
-rw-r--r--apps/user_ldap/js/wizard/wizardDetectorTestConfiguration.js31
-rw-r--r--apps/user_ldap/js/wizard/wizardDetectorTestLoginName.js30
-rw-r--r--apps/user_ldap/js/wizard/wizardDetectorUserCount.js26
-rw-r--r--apps/user_ldap/js/wizard/wizardDetectorUserDisplayNameAttribute.js39
-rw-r--r--apps/user_ldap/js/wizard/wizardDetectorUserGroupAssociation.js40
-rw-r--r--apps/user_ldap/js/wizard/wizardDetectorUserObjectClasses.js29
-rw-r--r--apps/user_ldap/js/wizard/wizardFilterOnType.js76
-rw-r--r--apps/user_ldap/js/wizard/wizardFilterOnTypeFactory.js27
-rw-r--r--apps/user_ldap/js/wizard/wizardObject.js60
-rw-r--r--apps/user_ldap/js/wizard/wizardTabAbstractFilter.js378
-rw-r--r--apps/user_ldap/js/wizard/wizardTabAdvanced.js330
-rw-r--r--apps/user_ldap/js/wizard/wizardTabElementary.js347
-rw-r--r--apps/user_ldap/js/wizard/wizardTabExpert.js130
-rw-r--r--apps/user_ldap/js/wizard/wizardTabGeneric.js547
-rw-r--r--apps/user_ldap/js/wizard/wizardTabGroupFilter.js124
-rw-r--r--apps/user_ldap/js/wizard/wizardTabLoginFilter.js238
-rw-r--r--apps/user_ldap/js/wizard/wizardTabUserFilter.js136
-rw-r--r--apps/user_ldap/lib/access.php27
-rw-r--r--apps/user_ldap/lib/configuration.php7
-rw-r--r--apps/user_ldap/lib/wizard.php146
-rw-r--r--apps/user_ldap/settings.php13
-rw-r--r--apps/user_ldap/templates/part.settingcontrols.php1
-rw-r--r--apps/user_ldap/templates/part.wizard-groupfilter.php42
-rw-r--r--apps/user_ldap/templates/part.wizard-loginfilter.php40
-rw-r--r--apps/user_ldap/templates/part.wizard-server.php31
-rw-r--r--apps/user_ldap/templates/part.wizard-userfilter.php53
-rw-r--r--apps/user_ldap/templates/settings.php56
-rw-r--r--apps/user_ldap/tests/user_ldap.php15
-rw-r--r--apps/user_ldap/user_ldap.php6
-rw-r--r--config/config.sample.php13
-rw-r--r--console.php13
-rw-r--r--core/ajax/share.php17
-rw-r--r--core/command/app/listapps.php46
-rw-r--r--core/command/base.php62
-rw-r--r--core/command/check.php41
-rw-r--r--core/command/status.php8
-rw-r--r--core/command/user/add.php41
-rw-r--r--core/command/user/resetpassword.php35
-rw-r--r--core/css/apps.css9
-rw-r--r--core/css/icons.css3
-rw-r--r--core/css/share.css9
-rw-r--r--core/css/styles.css33
-rw-r--r--core/img/actions/info-white.pngbin0 -> 324 bytes
-rw-r--r--core/img/actions/info-white.svg5
-rw-r--r--core/img/actions/info.pngbin325 -> 315 bytes
-rw-r--r--core/img/actions/info.svg2
-rw-r--r--core/img/actions/search.pngbin306 -> 293 bytes
-rw-r--r--core/img/actions/search.svg2
-rw-r--r--core/img/actions/settings.pngbin257 -> 256 bytes
-rw-r--r--core/img/actions/settings.svg6
-rw-r--r--core/img/actions/star.pngbin565 -> 496 bytes
-rw-r--r--core/img/actions/star.svg4
-rw-r--r--core/img/places/file.pngbin178 -> 174 bytes
-rw-r--r--core/img/places/file.svg2
-rw-r--r--core/img/places/home.pngbin195 -> 191 bytes
-rw-r--r--core/img/places/home.svg2
-rw-r--r--core/js/backgroundjobs.js2
-rw-r--r--core/js/config.php10
-rw-r--r--core/js/js.js6
-rw-r--r--core/js/maintenance-check.js4
-rw-r--r--core/js/share.js17
-rw-r--r--core/register_command.php1
-rw-r--r--core/templates/installation.php10
-rw-r--r--core/templates/layout.user.php2
-rw-r--r--lib/base.php51
-rw-r--r--lib/private/activitymanager.php65
-rw-r--r--lib/private/allconfig.php2
-rw-r--r--lib/private/app.php110
-rw-r--r--lib/private/app/appmanager.php2
-rw-r--r--lib/private/encryption/exceptions/decryptionfailedexception.php7
-rw-r--r--lib/private/encryption/exceptions/emptyencryptiondataexception.php7
-rw-r--r--lib/private/encryption/exceptions/encryptionfailedexception.php7
-rw-r--r--lib/private/encryption/exceptions/encryptionheaderkeyexistsexception.php27
-rw-r--r--lib/private/encryption/exceptions/encryptionheadertolargeexception.php7
-rw-r--r--lib/private/encryption/exceptions/modulealreadyexistsexception.php27
-rw-r--r--lib/private/encryption/exceptions/moduledoesnotexistsexception.php27
-rw-r--r--lib/private/encryption/exceptions/unknowncipherexception.php7
-rw-r--r--lib/private/encryption/file.php27
-rw-r--r--lib/private/encryption/keys/factory.php26
-rw-r--r--lib/private/encryption/keys/storage.php44
-rw-r--r--lib/private/encryption/manager.php27
-rw-r--r--lib/private/encryption/update.php27
-rw-r--r--lib/private/encryption/util.php27
-rw-r--r--lib/private/files/storage/dav.php170
-rw-r--r--lib/private/files/storage/local.php13
-rw-r--r--lib/private/files/storage/wrapper/encryption.php38
-rw-r--r--lib/private/files/stream/encryption.php28
-rw-r--r--lib/private/files/view.php4
-rw-r--r--lib/private/installer.php17
-rw-r--r--lib/private/ocsclient.php263
-rw-r--r--lib/private/preview.php363
-rw-r--r--lib/private/server.php24
-rw-r--r--lib/private/share/helper.php19
-rw-r--r--lib/private/share/share.php93
-rw-r--r--lib/private/tags.php2
-rw-r--r--lib/private/templatelayout.php2
-rw-r--r--lib/private/util.php13
-rw-r--r--lib/public/activity/imanager.php10
-rw-r--r--lib/public/app/iappmanager.php5
-rw-r--r--lib/public/encryption/exceptions/genericencryptionexception.php14
-rw-r--r--lib/public/encryption/iencryptionmodule.php30
-rw-r--r--lib/public/encryption/ifile.php27
-rw-r--r--lib/public/encryption/imanager.php28
-rw-r--r--lib/public/encryption/keys/istorage.php35
-rw-r--r--lib/public/iconfig.php2
-rw-r--r--lib/public/iservercontainer.php3
-rw-r--r--lib/public/share.php14
-rw-r--r--ocs/v1.php2
-rw-r--r--settings/application.php14
-rw-r--r--settings/apps.php42
-rw-r--r--settings/controller/appsettingscontroller.php75
-rw-r--r--settings/css/settings.css75
-rw-r--r--settings/js/apps.js41
-rw-r--r--settings/routes.php40
-rw-r--r--settings/templates/admin.php4
-rw-r--r--settings/templates/apps.php62
-rw-r--r--settings/templates/help.php14
-rw-r--r--settings/tests/js/appsSpec.js54
-rw-r--r--status.php1
-rw-r--r--tests/lib/OCSClientTest.php934
-rw-r--r--tests/lib/activitymanager.php103
-rw-r--r--tests/lib/encryption/keys/storage.php16
-rw-r--r--tests/lib/files/cache/updater.php8
-rw-r--r--tests/lib/files/cache/updaterlegacy.php10
-rw-r--r--tests/lib/files/cache/watcher.php10
-rw-r--r--tests/lib/files/etagtest.php17
-rw-r--r--tests/lib/files/filesystem.php12
-rw-r--r--tests/lib/files/node/integration.php12
-rw-r--r--tests/lib/files/storage/wrapper/encryption.php2
-rw-r--r--tests/lib/files/stream/encryption.php2
-rw-r--r--tests/lib/files/utils/scanner.php8
-rw-r--r--tests/lib/files/view.php12
-rw-r--r--tests/lib/preview.php92
-rw-r--r--tests/lib/share/share.php241
-rw-r--r--tests/lib/streamwrappers.php2
-rw-r--r--tests/lib/testcase.php4
-rw-r--r--tests/settings/controller/AppSettingsControllerTest.php231
230 files changed, 9886 insertions, 2773 deletions
diff --git a/apps/encryption/appinfo/app.php b/apps/encryption/appinfo/app.php
index 240a1726715..6bbf2113366 100644
--- a/apps/encryption/appinfo/app.php
+++ b/apps/encryption/appinfo/app.php
@@ -1,7 +1,9 @@
<?php
/**
- * @author Clark Tomlinson <clark@owncloud.com>
- * @since 2/19/15, 9:52 AM
+ * @author Björn Schießle <schiessle@owncloud.com>
+ * @author Clark Tomlinson <fallen013@gmail.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
diff --git a/apps/encryption/appinfo/application.php b/apps/encryption/appinfo/application.php
index 0d1bd0d6bed..34845ecf1e8 100644
--- a/apps/encryption/appinfo/application.php
+++ b/apps/encryption/appinfo/application.php
@@ -1,7 +1,10 @@
<?php
/**
- * @author Clark Tomlinson <clark@owncloud.com>
- * @since 3/11/15, 11:03 AM
+ * @author Björn Schießle <schiessle@owncloud.com>
+ * @author Clark Tomlinson <fallen013@gmail.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
@@ -47,7 +50,6 @@ class Application extends \OCP\AppFramework\App {
private $config;
/**
- * @param $appName
* @param array $urlParams
*/
public function __construct($urlParams = array()) {
@@ -57,9 +59,6 @@ class Application extends \OCP\AppFramework\App {
$this->registerServices();
}
- /**
- *
- */
public function registerHooks() {
if (!$this->config->getSystemValue('maintenance', false)) {
@@ -87,9 +86,6 @@ class Application extends \OCP\AppFramework\App {
}
}
- /**
- *
- */
public function registerEncryptionModule() {
$container = $this->getContainer();
$container->registerService('EncryptionModule', function (IAppContainer $c) {
@@ -102,9 +98,6 @@ class Application extends \OCP\AppFramework\App {
$this->encryptionManager->registerEncryptionModule($module);
}
- /**
- *
- */
public function registerServices() {
$container = $this->getContainer();
@@ -130,7 +123,6 @@ class Application extends \OCP\AppFramework\App {
);
});
-
$container->registerService('Recovery',
function (IAppContainer $c) {
$server = $c->getServer();
@@ -179,9 +171,6 @@ class Application extends \OCP\AppFramework\App {
}
- /**
- *
- */
public function registerSettings() {
// Register settings scripts
App::registerAdmin('encryption', 'settings/settings-admin');
diff --git a/apps/encryption/appinfo/routes.php b/apps/encryption/appinfo/routes.php
index d4867f5fdaa..0dab4a01b97 100644
--- a/apps/encryption/appinfo/routes.php
+++ b/apps/encryption/appinfo/routes.php
@@ -1,7 +1,8 @@
<?php
/**
- * @author Clark Tomlinson <clark@owncloud.com>
- * @since 2/19/15, 11:22 AM
+ * @author Björn Schießle <schiessle@owncloud.com>
+ * @author Clark Tomlinson <fallen013@gmail.com>
+ *
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
diff --git a/apps/encryption/controller/recoverycontroller.php b/apps/encryption/controller/recoverycontroller.php
index da55d81f63a..9c07bda62e4 100644
--- a/apps/encryption/controller/recoverycontroller.php
+++ b/apps/encryption/controller/recoverycontroller.php
@@ -1,7 +1,9 @@
<?php
/**
- * @author Clark Tomlinson <clark@owncloud.com>
- * @since 2/19/15, 11:25 AM
+ * @author Björn Schießle <schiessle@owncloud.com>
+ * @author Clark Tomlinson <fallen013@gmail.com>
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
@@ -27,7 +29,6 @@ use OCP\AppFramework\Controller;
use OCP\IConfig;
use OCP\IL10N;
use OCP\IRequest;
-use OCP\JSON;
use OCP\AppFramework\Http\DataResponse;
class RecoveryController extends Controller {
@@ -51,13 +52,23 @@ class RecoveryController extends Controller {
* @param IL10N $l10n
* @param Recovery $recovery
*/
- public function __construct($AppName, IRequest $request, IConfig $config, IL10N $l10n, Recovery $recovery) {
+ public function __construct($AppName,
+ IRequest $request,
+ IConfig $config,
+ IL10N $l10n,
+ Recovery $recovery) {
parent::__construct($AppName, $request);
$this->config = $config;
$this->l = $l10n;
$this->recovery = $recovery;
}
+ /**
+ * @param string $recoveryPassword
+ * @param string $confirmPassword
+ * @param string $adminEnableRecovery
+ * @return DataResponse
+ */
public function adminRecovery($recoveryPassword, $confirmPassword, $adminEnableRecovery) {
// Check if both passwords are the same
if (empty($recoveryPassword)) {
@@ -88,6 +99,12 @@ class RecoveryController extends Controller {
}
}
+ /**
+ * @param string $newPassword
+ * @param string $oldPassword
+ * @param string $confirmPassword
+ * @return DataResponse
+ */
public function changeRecoveryPassword($newPassword, $oldPassword, $confirmPassword) {
//check if both passwords are the same
if (empty($oldPassword)) {
@@ -132,6 +149,9 @@ class RecoveryController extends Controller {
/**
* @NoAdminRequired
+ *
+ * @param string $userEnableRecovery
+ * @return DataResponse
*/
public function userSetRecovery($userEnableRecovery) {
if ($userEnableRecovery === '0' || $userEnableRecovery === '1') {
diff --git a/apps/encryption/hooks/contracts/ihook.php b/apps/encryption/hooks/contracts/ihook.php
index 2cc01fd7c9b..d4f20700d78 100644
--- a/apps/encryption/hooks/contracts/ihook.php
+++ b/apps/encryption/hooks/contracts/ihook.php
@@ -1,7 +1,7 @@
<?php
/**
- * @author Clark Tomlinson <clark@owncloud.com>
- * @since 2/19/15, 10:03 AM
+ * @author Clark Tomlinson <fallen013@gmail.com>
+ *
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
diff --git a/apps/encryption/hooks/userhooks.php b/apps/encryption/hooks/userhooks.php
index 1ec0950d941..c39fa34108b 100644
--- a/apps/encryption/hooks/userhooks.php
+++ b/apps/encryption/hooks/userhooks.php
@@ -1,7 +1,9 @@
<?php
/**
- * @author Clark Tomlinson <clark@owncloud.com>
- * @since 2/19/15, 10:02 AM
+ * @author Björn Schießle <schiessle@owncloud.com>
+ * @author Clark Tomlinson <fallen013@gmail.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
diff --git a/apps/encryption/js/detect-migration.js b/apps/encryption/js/detect-migration.js
deleted file mode 100644
index f5627edf4e4..00000000000
--- a/apps/encryption/js/detect-migration.js
+++ /dev/null
@@ -1,33 +0,0 @@
-/**
- * Copyright (c) 2013
- * Bjoern Schiessle <schiessle@owncloud.com>
- * This file is licensed under the Affero General Public License version 3 or later.
- * See the COPYING-README file.
- */
-
-
-$(document).ready(function(){
- $('form[name="login"]').on('submit', function() {
- var user = $('#user').val();
- var password = $('#password').val();
- $.ajax({
- type: 'POST',
- url: OC.linkTo('files_encryption', 'ajax/getMigrationStatus.php'),
- dataType: 'json',
- data: {user: user, password: password},
- async: false,
- success: function(response) {
- if (response.data.migrationStatus === OC.Encryption.MIGRATION_OPEN) {
- var message = t('files_encryption', 'Initial encryption started... This can take some time. Please wait.');
- $('#messageText').text(message);
- $('#message').removeClass('hidden').addClass('update');
- } else if (response.data.migrationStatus === OC.Encryption.MIGRATION_IN_PROGRESS) {
- var message = t('files_encryption', 'Initial encryption running... Please try again later.');
- $('#messageText').text(message);
- $('#message').removeClass('hidden').addClass('update');
- }
- }
- });
- });
-
-});
diff --git a/apps/encryption/lib/crypto/crypt.php b/apps/encryption/lib/crypto/crypt.php
index c0b737a3daa..974e0038afc 100644
--- a/apps/encryption/lib/crypto/crypt.php
+++ b/apps/encryption/lib/crypto/crypt.php
@@ -1,7 +1,9 @@
<?php
/**
- * @author Clark Tomlinson <clark@owncloud.com>
- * @since 2/19/15, 1:42 PM
+ * @author Björn Schießle <schiessle@owncloud.com>
+ * @author Clark Tomlinson <fallen013@gmail.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
@@ -35,6 +37,8 @@ use OCP\IUserSession;
class Crypt {
const DEFAULT_CIPHER = 'AES-256-CFB';
+ // default cipher from old ownCloud versions
+ const LEGACY_CIPHER = 'AES-128-CFB';
const HEADER_START = 'HBEGIN';
const HEADER_END = 'HEND';
@@ -71,7 +75,7 @@ class Crypt {
$res = $this->getOpenSSLPKey();
if (!$res) {
- $log->error("Encryption Library could'nt generate users key-pair for {$this->user->getUID()}",
+ $log->error("Encryption Library couldn't generate users key-pair for {$this->user->getUID()}",
['app' => 'encryption']);
if (openssl_error_string()) {
@@ -90,7 +94,7 @@ class Crypt {
'privateKey' => $privateKey
];
}
- $log->error('Encryption library couldn\'t export users private key, please check your servers openSSL configuration.' . $this->user->getUID(),
+ $log->error('Encryption library couldn\'t export users private key, please check your servers OpenSSL configuration.' . $this->user->getUID(),
['app' => 'encryption']);
if (openssl_error_string()) {
$log->error('Encryption Library:' . openssl_error_string(),
@@ -147,6 +151,16 @@ class Crypt {
}
/**
+ * generate header for encrypted file
+ */
+ public function generateHeader() {
+ $cipher = $this->getCipher();
+ $header = self::HEADER_START . ':cipher:' . $cipher . ':' . self::HEADER_END;
+
+ return $header;
+ }
+
+ /**
* @param string $plainContent
* @param string $iv
* @param string $passPhrase
@@ -203,23 +217,28 @@ class Crypt {
}
/**
- * @param string $recoveryKey
+ * @param string $privateKey
* @param string $password
* @return bool|string
*/
- public function decryptPrivateKey($recoveryKey, $password) {
+ public function decryptPrivateKey($privateKey, $password) {
- $header = $this->parseHeader($recoveryKey);
- $cipher = $this->getCipher();
+ $header = $this->parseHeader($privateKey);
+
+ if (isset($header['cipher'])) {
+ $cipher = $header['cipher'];
+ } else {
+ $cipher = self::LEGACY_CIPHER;
+ }
// If we found a header we need to remove it from the key we want to decrypt
if (!empty($header)) {
- $recoveryKey = substr($recoveryKey,
- strpos($recoveryKey,
+ $privateKey = substr($privateKey,
+ strpos($privateKey,
self::HEADER_END) + strlen(self::HEADER_START));
}
- $plainKey = $this->symmetricDecryptFileContent($recoveryKey,
+ $plainKey = $this->symmetricDecryptFileContent($privateKey,
$password,
$cipher);
@@ -360,8 +379,11 @@ class Crypt {
}
/**
- * Generate a pseudo random 256-bit ASCII key, used as file key
+ * Generate a cryptographically secure pseudo-random base64 encoded 256-bit
+ * ASCII key, used as file key
+ *
* @return string
+ * @throws \Exception
*/
public static function generateFileKey() {
// Generate key
@@ -419,7 +441,7 @@ class Crypt {
}
/**
- * @param $plainContent
+ * @param string $plainContent
* @param array $keyFiles
* @return array
* @throws MultiKeyEncryptException
diff --git a/apps/encryption/lib/crypto/encryption.php b/apps/encryption/lib/crypto/encryption.php
index 7c633b7411f..13beda196ce 100644
--- a/apps/encryption/lib/crypto/encryption.php
+++ b/apps/encryption/lib/crypto/encryption.php
@@ -1,9 +1,24 @@
<?php
/**
- * @author Clark Tomlinson <fallen013@gmail.com>
- * @since 3/6/15, 2:28 PM
- * @link http:/www.clarkt.com
- * @copyright Clark Tomlinson © 2015
+ * @author Björn Schießle <schiessle@owncloud.com>
+ * @author Clark Tomlinson <fallen013@gmail.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
@@ -92,7 +107,7 @@ class Encryption implements IEncryptionModule {
* written to the header, in case of a write operation
* or if no additional data is needed return a empty array
*/
- public function begin($path, $user, $header, $accessList) {
+ public function begin($path, $user, array $header, array $accessList) {
if (isset($header['cipher'])) {
$this->cipher = $header['cipher'];
@@ -231,7 +246,7 @@ class Encryption implements IEncryptionModule {
* @param array $accessList who has access to the file contains the key 'users' and 'public'
* @return boolean
*/
- public function update($path, $uid, $accessList) {
+ public function update($path, $uid, array $accessList) {
$fileKey = $this->keyManager->getFileKey($path, $uid);
$publicKeys = array();
foreach ($accessList['users'] as $user) {
@@ -262,12 +277,11 @@ class Encryption implements IEncryptionModule {
}
if ($this->keyManager->recoveryKeyExists() &&
- $this->util->recoveryEnabled($this->user)) {
+ $this->util->isRecoveryEnabledForUser()) {
$publicKeys[$this->keyManager->getRecoveryKeyId()] = $this->keyManager->getRecoveryKey();
}
-
return $publicKeys;
}
@@ -314,6 +328,10 @@ class Encryption implements IEncryptionModule {
return 6126;
}
+ /**
+ * @param string $path
+ * @return string
+ */
protected function getPathToRealFile($path) {
$realPath = $path;
$parts = explode('/', $path);
diff --git a/apps/encryption/lib/exceptions/multikeydecryptexception.php b/apps/encryption/lib/exceptions/multikeydecryptexception.php
index 1466d35eda3..48b916ff1b8 100644
--- a/apps/encryption/lib/exceptions/multikeydecryptexception.php
+++ b/apps/encryption/lib/exceptions/multikeydecryptexception.php
@@ -1,5 +1,23 @@
<?php
-
+/**
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
namespace OCA\Encryption\Exceptions;
use OCP\Encryption\Exceptions\GenericEncryptionException;
diff --git a/apps/encryption/lib/exceptions/multikeyencryptexception.php b/apps/encryption/lib/exceptions/multikeyencryptexception.php
index daf528e2cf7..197e06adbf3 100644
--- a/apps/encryption/lib/exceptions/multikeyencryptexception.php
+++ b/apps/encryption/lib/exceptions/multikeyencryptexception.php
@@ -1,5 +1,23 @@
<?php
-
+/**
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
namespace OCA\Encryption\Exceptions;
use OCP\Encryption\Exceptions\GenericEncryptionException;
diff --git a/apps/encryption/lib/exceptions/privatekeymissingexception.php b/apps/encryption/lib/exceptions/privatekeymissingexception.php
index 50d75870b20..29db5a16641 100644
--- a/apps/encryption/lib/exceptions/privatekeymissingexception.php
+++ b/apps/encryption/lib/exceptions/privatekeymissingexception.php
@@ -1,7 +1,9 @@
<?php
- /**
- * @author Clark Tomlinson <clark@owncloud.com>
- * @since 2/25/15, 9:39 AM
+/**
+ * @author Björn Schießle <schiessle@owncloud.com>
+ * @author Clark Tomlinson <fallen013@gmail.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
diff --git a/apps/encryption/lib/exceptions/publickeymissingexception.php b/apps/encryption/lib/exceptions/publickeymissingexception.php
index 9638c28e427..078add0369a 100644
--- a/apps/encryption/lib/exceptions/publickeymissingexception.php
+++ b/apps/encryption/lib/exceptions/publickeymissingexception.php
@@ -1,6 +1,23 @@
<?php
-
-
+/**
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
namespace OCA\Encryption\Exceptions;
use OCP\Encryption\Exceptions\GenericEncryptionException;
diff --git a/apps/encryption/lib/hookmanager.php b/apps/encryption/lib/hookmanager.php
index 19ee142a622..4b885cd7f64 100644
--- a/apps/encryption/lib/hookmanager.php
+++ b/apps/encryption/lib/hookmanager.php
@@ -1,7 +1,9 @@
<?php
/**
- * @author Clark Tomlinson <clark@owncloud.com>
- * @since 2/19/15, 10:13 AM
+ * @author Björn Schießle <schiessle@owncloud.com>
+ * @author Clark Tomlinson <fallen013@gmail.com>
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
@@ -48,9 +50,6 @@ class HookManager {
return true;
}
- /**
- *
- */
public function fireHooks() {
foreach ($this->hookInstances as $instance) {
/**
diff --git a/apps/encryption/lib/keymanager.php b/apps/encryption/lib/keymanager.php
index 1f71a891e81..a280ea9bde3 100644
--- a/apps/encryption/lib/keymanager.php
+++ b/apps/encryption/lib/keymanager.php
@@ -1,5 +1,25 @@
<?php
-
+/**
+ * @author Björn Schießle <schiessle@owncloud.com>
+ * @author Clark Tomlinson <fallen013@gmail.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
namespace OCA\Encryption;
use OC\Encryption\Exceptions\DecryptionFailedException;
@@ -180,9 +200,10 @@ class KeyManager {
$encryptedKey = $this->crypt->symmetricEncryptFileContent($keyPair['privateKey'],
$password);
+ $header = $this->crypt->generateHeader();
if ($encryptedKey) {
- $this->setPrivateKey($uid, $encryptedKey);
+ $this->setPrivateKey($uid, $header . $encryptedKey);
return true;
}
return false;
@@ -199,9 +220,10 @@ class KeyManager {
$encryptedKey = $this->crypt->symmetricEncryptFileContent($keyPair['privateKey'],
$password);
+ $header = $this->crypt->generateHeader();
if ($encryptedKey) {
- $this->setSystemPrivateKey($this->getRecoveryKeyId(), $encryptedKey);
+ $this->setSystemPrivateKey($this->getRecoveryKeyId(), $header . $encryptedKey);
return true;
}
return false;
diff --git a/apps/encryption/lib/recovery.php b/apps/encryption/lib/recovery.php
index 34acdd0a6e3..5c1e91866a0 100644
--- a/apps/encryption/lib/recovery.php
+++ b/apps/encryption/lib/recovery.php
@@ -1,7 +1,8 @@
<?php
/**
- * @author Clark Tomlinson <clark@owncloud.com>
- * @since 2/19/15, 11:45 AM
+ * @author Björn Schießle <schiessle@owncloud.com>
+ * @author Clark Tomlinson <fallen013@gmail.com>
+ *
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
@@ -128,6 +129,7 @@ class Recovery {
*
* @param string $newPassword
* @param string $oldPassword
+ * @return bool
*/
public function changeRecoveryKeyPassword($newPassword, $oldPassword) {
$recoveryKey = $this->keyManager->getSystemPrivateKey($this->keyManager->getRecoveryKeyId());
diff --git a/apps/encryption/lib/session.php b/apps/encryption/lib/session.php
index e705611fa6e..85d2a7698ef 100644
--- a/apps/encryption/lib/session.php
+++ b/apps/encryption/lib/session.php
@@ -1,24 +1,24 @@
<?php
-
/**
- * ownCloud
- *
- * @copyright (C) 2015 ownCloud, Inc.
+ * @author Björn Schießle <schiessle@owncloud.com>
+ * @author Clark Tomlinson <fallen013@gmail.com>
+ * @author Lukas Reschke <lukas@owncloud.com>
*
- * @author Bjoern Schiessle <schiessle@owncloud.com>
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or any later version.
+ * This 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 library is distributed in the hope that it will be useful,
+ * 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.
+ * 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/>
*
- * 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/>.
*/
namespace OCA\Encryption;
@@ -34,6 +34,9 @@ class Session {
const INIT_EXECUTED = '1';
const INIT_SUCCESSFUL = '2';
+ /**
+ * @param ISession $session
+ */
public function __construct(ISession $session) {
$this->session = $session;
}
diff --git a/apps/encryption/lib/users/setup.php b/apps/encryption/lib/users/setup.php
index e80bf6003e6..2ec49b5c7fb 100644
--- a/apps/encryption/lib/users/setup.php
+++ b/apps/encryption/lib/users/setup.php
@@ -1,9 +1,23 @@
<?php
/**
- * @author Clark Tomlinson <fallen013@gmail.com>
- * @since 3/6/15, 11:36 AM
- * @link http:/www.clarkt.com
- * @copyright Clark Tomlinson © 2015
+ * @author Björn Schießle <schiessle@owncloud.com>
+ * @author Clark Tomlinson <fallen013@gmail.com>
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
@@ -40,7 +54,10 @@ class Setup {
* @param Crypt $crypt
* @param KeyManager $keyManager
*/
- public function __construct(ILogger $logger, IUserSession $userSession, Crypt $crypt, KeyManager $keyManager) {
+ public function __construct(ILogger $logger,
+ IUserSession $userSession,
+ Crypt $crypt,
+ KeyManager $keyManager) {
$this->logger = $logger;
$this->user = $userSession && $userSession->isLoggedIn() ? $userSession->getUser()->getUID() : false;
$this->crypt = $crypt;
@@ -48,8 +65,8 @@ class Setup {
}
/**
- * @param $uid userid
- * @param $password user password
+ * @param string $uid userid
+ * @param string $password user password
* @return bool
*/
public function setupUser($uid, $password) {
@@ -57,8 +74,8 @@ class Setup {
}
/**
- * @param $uid userid
- * @param $password user password
+ * @param string $uid userid
+ * @param string $password user password
* @return bool
*/
public function setupServerSide($uid, $password) {
diff --git a/apps/encryption/lib/util.php b/apps/encryption/lib/util.php
index 6b6b8b6b38c..04e04028caf 100644
--- a/apps/encryption/lib/util.php
+++ b/apps/encryption/lib/util.php
@@ -1,7 +1,8 @@
<?php
/**
- * @author Clark Tomlinson <clark@owncloud.com>
- * @since 3/17/15, 10:31 AM
+ * @author Björn Schießle <schiessle@owncloud.com>
+ * @author Clark Tomlinson <fallen013@gmail.com>
+ *
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
diff --git a/apps/encryption/settings/settings-admin.php b/apps/encryption/settings/settings-admin.php
index 36e9c532bbd..c7ac8c09c6b 100644
--- a/apps/encryption/settings/settings-admin.php
+++ b/apps/encryption/settings/settings-admin.php
@@ -1,9 +1,24 @@
<?php
/**
- * Copyright (c) 2015 Clark Tomlinson <clark@owncloud.com>
- * This file is licensed under the Affero General Public License version 3 or
- * later.
- * See the COPYING-README file.
+ * @author Björn Schießle <schiessle@owncloud.com>
+ * @author Clark Tomlinson <fallen013@gmail.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
*/
\OC_Util::checkAdminUser();
diff --git a/apps/encryption/settings/settings-personal.php b/apps/encryption/settings/settings-personal.php
index ec3d30f457d..abbe62af615 100644
--- a/apps/encryption/settings/settings-personal.php
+++ b/apps/encryption/settings/settings-personal.php
@@ -1,9 +1,24 @@
<?php
/**
- * Copyright (c) 2015 Clark Tomlinson <clark@owncloud.com>
- * This file is licensed under the Affero General Public License version 3 or
- * later.
- * See the COPYING-README file.
+ * @author Björn Schießle <schiessle@owncloud.com>
+ * @author Clark Tomlinson <fallen013@gmail.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
*/
$session = new \OCA\Encryption\Session(\OC::$server->getSession());
diff --git a/apps/encryption/tests/hooks/UserHooksTest.php b/apps/encryption/tests/hooks/UserHooksTest.php
new file mode 100644
index 00000000000..1d76e3ba1a2
--- /dev/null
+++ b/apps/encryption/tests/hooks/UserHooksTest.php
@@ -0,0 +1,217 @@
+<?php
+/**
+ * @author Clark Tomlinson <clark@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ */
+
+
+
+namespace OCA\Encryption\Tests\Hooks;
+
+
+use OCA\Encryption\Hooks\UserHooks;
+use Test\TestCase;
+
+class UserHooksTest extends TestCase {
+ /**
+ * @var \PHPUnit_Framework_MockObject_MockObject
+ */
+ private $utilMock;
+ /**
+ * @var \PHPUnit_Framework_MockObject_MockObject
+ */
+ private $recoveryMock;
+ /**
+ * @var \PHPUnit_Framework_MockObject_MockObject
+ */
+ private $sessionMock;
+ /**
+ * @var \PHPUnit_Framework_MockObject_MockObject
+ */
+ private $keyManagerMock;
+ /**
+ * @var \PHPUnit_Framework_MockObject_MockObject
+ */
+ private $userSetupMock;
+ /**
+ * @var \PHPUnit_Framework_MockObject_MockObject
+ */
+ private $userSessionMock;
+ /**
+ * @var \PHPUnit_Framework_MockObject_MockObject
+ */
+ private $cryptMock;
+ /**
+ * @var UserHooks
+ */
+ private $instance;
+
+ private $params = ['uid' => 'testUser', 'password' => 'password'];
+
+ public function testLogin() {
+ $this->userSetupMock->expects($this->exactly(2))
+ ->method('setupUser')
+ ->willReturnOnConsecutiveCalls(true, false);
+
+ $this->keyManagerMock->expects($this->once())
+ ->method('init')
+ ->with('testUser', 'password');
+
+ $this->assertNull($this->instance->login($this->params));
+ $this->assertFalse($this->instance->login($this->params));
+ }
+
+ public function testLogout() {
+ $this->sessionMock->expects($this->once())
+ ->method('clear');
+
+ $this->assertNull($this->instance->logout());
+ }
+
+ public function testPostCreateUser() {
+ $this->userSetupMock->expects($this->once())
+ ->method('setupUser');
+
+ $this->assertNull($this->instance->postCreateUser($this->params));
+ }
+
+ public function testPostDeleteUser() {
+ $this->keyManagerMock->expects($this->once())
+ ->method('deletePublicKey')
+ ->with('testUser');
+
+ $this->assertNull($this->instance->postDeleteUser($this->params));
+ }
+
+ public function testPreSetPassphrase() {
+ $this->userSessionMock->expects($this->once())
+ ->method('canChangePassword');
+
+ $this->assertNull($this->instance->preSetPassphrase($this->params));
+ }
+
+ public function testSetPassphrase() {
+ $this->sessionMock->expects($this->exactly(4))
+ ->method('getPrivateKey')
+ ->willReturnOnConsecutiveCalls(true, false);
+
+ $this->cryptMock->expects($this->exactly(4))
+ ->method('symmetricEncryptFileContent')
+ ->willReturn(true);
+
+ $this->keyManagerMock->expects($this->exactly(4))
+ ->method('setPrivateKey');
+
+ $this->assertNull($this->instance->setPassphrase($this->params));
+ $this->params['recoveryPassword'] = 'password';
+
+ $this->recoveryMock->expects($this->exactly(3))
+ ->method('isRecoveryEnabledForUser')
+ ->with('testUser')
+ ->willReturnOnConsecutiveCalls(true, false);
+
+
+ // Test first if statement
+ $this->assertNull($this->instance->setPassphrase($this->params));
+
+ // Test Second if conditional
+ $this->keyManagerMock->expects($this->exactly(2))
+ ->method('userHasKeys')
+ ->with('testUser')
+ ->willReturn(true);
+
+ $this->assertNull($this->instance->setPassphrase($this->params));
+
+ // Test third and final if condition
+ $this->utilMock->expects($this->once())
+ ->method('userHasFiles')
+ ->with('testUser')
+ ->willReturn(false);
+
+ $this->cryptMock->expects($this->once())
+ ->method('createKeyPair');
+
+ $this->keyManagerMock->expects($this->once())
+ ->method('setPrivateKey');
+
+ $this->recoveryMock->expects($this->once())
+ ->method('recoverUsersFiles')
+ ->with('password', 'testUser');
+
+ $this->assertNull($this->instance->setPassphrase($this->params));
+ }
+
+ public function testPostPasswordReset() {
+ $this->keyManagerMock->expects($this->once())
+ ->method('replaceUserKeys')
+ ->with('testUser');
+
+ $this->userSetupMock->expects($this->once())
+ ->method('setupServerSide')
+ ->with('testUser', 'password');
+
+ $this->assertNull($this->instance->postPasswordReset($this->params));
+ }
+
+ protected function setUp() {
+ parent::setUp();
+ $loggerMock = $this->getMock('OCP\ILogger');
+ $this->keyManagerMock = $this->getMockBuilder('OCA\Encryption\KeyManager')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->userSetupMock = $this->getMockBuilder('OCA\Encryption\Users\Setup')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->userSessionMock = $this->getMockBuilder('OCP\IUserSession')
+ ->disableOriginalConstructor()
+ ->setMethods([
+ 'isLoggedIn',
+ 'getUID',
+ 'login',
+ 'logout',
+ 'setUser',
+ 'getUser',
+ 'canChangePassword'
+ ])
+ ->getMock();
+
+ $this->userSessionMock->expects($this->any())->method('getUID')->will($this->returnValue('testUser'));
+
+ $this->userSessionMock->expects($this->any())
+ ->method($this->anything())
+ ->will($this->returnSelf());
+
+ $utilMock = $this->getMockBuilder('OCA\Encryption\Util')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $sessionMock = $this->getMockBuilder('OCA\Encryption\Session')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->cryptMock = $this->getMockBuilder('OCA\Encryption\Crypto\Crypt')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $recoveryMock = $this->getMockBuilder('OCA\Encryption\Recovery')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->sessionMock = $sessionMock;
+ $this->recoveryMock = $recoveryMock;
+ $this->utilMock = $utilMock;
+ $this->instance = new UserHooks($this->keyManagerMock,
+ $loggerMock,
+ $this->userSetupMock,
+ $this->userSessionMock,
+ $this->utilMock,
+ $this->sessionMock,
+ $this->cryptMock,
+ $this->recoveryMock
+ );
+
+ }
+
+}
diff --git a/apps/encryption/tests/lib/HookManagerTest.php b/apps/encryption/tests/lib/HookManagerTest.php
index 3c360ff3504..3da0bafb691 100644
--- a/apps/encryption/tests/lib/HookManagerTest.php
+++ b/apps/encryption/tests/lib/HookManagerTest.php
@@ -1,7 +1,7 @@
<?php
/**
- * @author Clark Tomlinson <clark@owncloud.com>
- * @since 3/31/15, 1:54 PM
+ * @author Clark Tomlinson <fallen013@gmail.com>
+ *
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
diff --git a/apps/encryption/tests/lib/KeyManagerTest.php b/apps/encryption/tests/lib/KeyManagerTest.php
index d12578bb8d2..1e51341a7e4 100644
--- a/apps/encryption/tests/lib/KeyManagerTest.php
+++ b/apps/encryption/tests/lib/KeyManagerTest.php
@@ -1,9 +1,23 @@
<?php
/**
- * @author Clark Tomlinson <fallen013@gmail.com>
- * @since 3/5/15, 10:53 AM
- * @link http:/www.clarkt.com
- * @copyright Clark Tomlinson © 2015
+ * @author Björn Schießle <schiessle@owncloud.com>
+ * @author Clark Tomlinson <fallen013@gmail.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
*
*/
diff --git a/apps/encryption/tests/lib/RecoveryTest.php b/apps/encryption/tests/lib/RecoveryTest.php
index 701762b56d6..b3fd403949c 100644
--- a/apps/encryption/tests/lib/RecoveryTest.php
+++ b/apps/encryption/tests/lib/RecoveryTest.php
@@ -1,7 +1,7 @@
<?php
/**
- * @author Clark Tomlinson <clark@owncloud.com>
- * @since 4/3/15, 9:57 AM
+ * @author Clark Tomlinson <fallen013@gmail.com>
+ *
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
diff --git a/apps/encryption/tests/lib/SessionTest.php b/apps/encryption/tests/lib/SessionTest.php
index f7e026808f0..e036c439939 100644
--- a/apps/encryption/tests/lib/SessionTest.php
+++ b/apps/encryption/tests/lib/SessionTest.php
@@ -1,7 +1,8 @@
<?php
/**
- * @author Clark Tomlinson <clark@owncloud.com>
- * @since 3/31/15, 10:19 AM
+ * @author Clark Tomlinson <fallen013@gmail.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
diff --git a/apps/encryption/tests/lib/UtilTest.php b/apps/encryption/tests/lib/UtilTest.php
index fa87a629f2f..5f086a8e475 100644
--- a/apps/encryption/tests/lib/UtilTest.php
+++ b/apps/encryption/tests/lib/UtilTest.php
@@ -1,7 +1,7 @@
<?php
/**
- * @author Clark Tomlinson <clark@owncloud.com>
- * @since 3/31/15, 3:49 PM
+ * @author Clark Tomlinson <fallen013@gmail.com>
+ *
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
diff --git a/apps/encryption/tests/lib/crypto/encryptionTest.php b/apps/encryption/tests/lib/crypto/encryptionTest.php
index 52a322463a9..9e14a70ebb0 100644
--- a/apps/encryption/tests/lib/crypto/encryptionTest.php
+++ b/apps/encryption/tests/lib/crypto/encryptionTest.php
@@ -1,24 +1,22 @@
<?php
-
/**
- * ownCloud
- *
- * @copyright (C) 2015 ownCloud, Inc.
+ * @author Björn Schießle <schiessle@owncloud.com>
*
- * @author Bjoern Schiessle <schiessle@owncloud.com>
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or any later version.
+ * This 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 library is distributed in the hope that it will be useful,
+ * 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.
+ * 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/>
*
- * 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/>.
*/
namespace OCA\Encryption\Tests\Crypto;
diff --git a/apps/encryption/tests/lib/users/SetupTest.php b/apps/encryption/tests/lib/users/SetupTest.php
index 6a66df3674b..354de26253e 100644
--- a/apps/encryption/tests/lib/users/SetupTest.php
+++ b/apps/encryption/tests/lib/users/SetupTest.php
@@ -1,7 +1,7 @@
<?php
/**
- * @author Clark Tomlinson <clark@owncloud.com>
- * @since 4/6/15, 11:50 AM
+ * @author Clark Tomlinson <fallen013@gmail.com>
+ *
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
diff --git a/apps/encryption_dummy/lib/dummymodule.php b/apps/encryption_dummy/lib/dummymodule.php
index 8cec9dfaf4c..b4dfe34a9bf 100644
--- a/apps/encryption_dummy/lib/dummymodule.php
+++ b/apps/encryption_dummy/lib/dummymodule.php
@@ -56,11 +56,11 @@ class DummyModule implements IEncryptionModule {
* @param array $header contains the header data read from the file
* @param array $accessList who has access to the file contains the key 'users' and 'public'
*
- * $return array $header contain data as key-value pairs which should be
+ * @return array $header contain data as key-value pairs which should be
* written to the header, in case of a write operation
* or if no additional data is needed return a empty array
*/
- public function begin($path, $user, $header, $accessList) {
+ public function begin($path, $user, array $header, array $accessList) {
return array();
}
@@ -141,7 +141,7 @@ class DummyModule implements IEncryptionModule {
* @param array $accessList who has access to the file contains the key 'users' and 'public'
* @return boolean
*/
- public function update($path, $uid, $accessList) {
+ public function update($path, $uid, array $accessList) {
return true;
}
}
diff --git a/apps/files/appinfo/app.php b/apps/files/appinfo/app.php
index e4cac7c7c14..c483ad31ec5 100644
--- a/apps/files/appinfo/app.php
+++ b/apps/files/appinfo/app.php
@@ -59,6 +59,11 @@ $templateManager->registerTemplate('application/vnd.oasis.opendocument.spreadshe
\OC::$server->getActivityManager()->registerExtension(function() {
return new \OCA\Files\Activity(
\OC::$server->query('L10NFactory'),
- \OC::$server->getURLGenerator()
+ \OC::$server->getURLGenerator(),
+ \OC::$server->getActivityManager(),
+ new \OCA\Files\ActivityHelper(
+ \OC::$server->getTagManager()
+ ),
+ \OC::$server->getConfig()
);
});
diff --git a/apps/files/css/files.css b/apps/files/css/files.css
index d34013db499..455ccae3f96 100644
--- a/apps/files/css/files.css
+++ b/apps/files/css/files.css
@@ -103,6 +103,27 @@
min-height: 100%;
}
+/* icons for sidebar */
+.nav-icon-files {
+ background-image: url('../img/folder.svg');
+}
+.nav-icon-favorites {
+ background-image: url('../img/star.svg');
+}
+.nav-icon-sharingin,
+.nav-icon-sharingout {
+ background-image: url('../img/share.svg');
+}
+.nav-icon-sharinglinks {
+ background-image: url('../img/public.svg');
+}
+.nav-icon-extstoragemounts {
+ background-image: url('../img/external.svg');
+}
+.nav-icon-trashbin {
+ background-image: url('../img/delete.svg');
+}
+
/* move Deleted Files to bottom of sidebar */
.nav-trashbin {
position: fixed !important;
diff --git a/apps/files/img/delete.png b/apps/files/img/delete.png
new file mode 100644
index 00000000000..b2ab8c5efef
--- /dev/null
+++ b/apps/files/img/delete.png
Binary files differ
diff --git a/apps/files/img/delete.svg b/apps/files/img/delete.svg
new file mode 100644
index 00000000000..b6dc2cc4173
--- /dev/null
+++ b/apps/files/img/delete.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="16" width="16" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <path opacity=".5" d="m6.5 1-0.5 1h-3c-0.554 0-1 0.446-1 1v1h12v-1c0-0.554-0.446-1-1-1h-3l-0.5-1zm-3.5 4 0.875 9c0.061 0.549 0.5729 1 1.125 1h6c0.55232 0 1.064-0.45102 1.125-1l0.875-9z" fill-rule="evenodd"/>
+</svg>
diff --git a/apps/files/img/external.png b/apps/files/img/external.png
new file mode 100644
index 00000000000..2ac5e9344c3
--- /dev/null
+++ b/apps/files/img/external.png
Binary files differ
diff --git a/apps/files/img/external.svg b/apps/files/img/external.svg
new file mode 100644
index 00000000000..d1940f2f1b3
--- /dev/null
+++ b/apps/files/img/external.svg
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="16" width="16" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <path opacity=".5" d="m7.4515 1.6186 2.3806 2.2573-3.5709 3.386 2.3806 2.2573 3.5709-3.386 2.3806 2.2573v-6.7725h-7.1422zm-4.7612 1.1286c-0.65945 0-1.1903 0.5034-1.1903 1.1286v9.029c0 0.6253 0.53085 1.1286 1.1903 1.1286h9.522c0.6594 0 1.1903-0.5034 1.1903-1.1286v-3.386l-1.19-1.1287v4.5146h-9.5217v-9.029h4.761l-1.1905-1.1287h-3.5707z"/>
+</svg>
diff --git a/apps/files/img/folder.png b/apps/files/img/folder.png
new file mode 100644
index 00000000000..ada4c4c2f88
--- /dev/null
+++ b/apps/files/img/folder.png
Binary files differ
diff --git a/apps/files/img/folder.svg b/apps/files/img/folder.svg
new file mode 100644
index 00000000000..1a97808443d
--- /dev/null
+++ b/apps/files/img/folder.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="16" width="16" version="1.0" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <g opacity=".5" fill-rule="evenodd" transform="matrix(.86667 0 0 .86667 -172.04 -864.43)">
+ <path d="m200.2 998.57c-0.28913 0-0.53125 0.24212-0.53125 0.53125v13.938c0 0.2985 0.23264 0.5312 0.53125 0.5312h15.091c0.2986 0 0.53125-0.2327 0.53125-0.5312l0.0004-8.1661c0-0.289-0.24212-0.5338-0.53125-0.5338h-12.161l-0.0004 6.349c0 0.2771-0.30237 0.5638-0.57937 0.5638s-0.57447-0.2867-0.57447-0.5638l0.0004-7.0055c0-0.2771 0.23357-0.4974 0.51057-0.4974h2.6507l8.3774 0.0003-0.0004-1.7029c0-0.3272-0.24549-0.6047-0.57258-0.6047h-7.5043v-1.7764c0-0.28915-0.23415-0.53125-0.52328-0.53125z" fill-rule="evenodd"/>
+ </g>
+</svg>
diff --git a/apps/files/img/public.png b/apps/files/img/public.png
new file mode 100644
index 00000000000..f49c5fb1ee5
--- /dev/null
+++ b/apps/files/img/public.png
Binary files differ
diff --git a/apps/files/img/public.svg b/apps/files/img/public.svg
new file mode 100644
index 00000000000..23ac51d8367
--- /dev/null
+++ b/apps/files/img/public.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="16" width="16" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <g opacity=".5" transform="matrix(.67042 -.67042 .67042 .67042 .62542 93.143)">
+ <path style="block-progression:tb;color:#000000;text-transform:none;text-indent:0" d="m69.5-61.5c-1.9217 0-3.5 1.5783-3.5 3.5 0 0.17425 0.0062 0.33232 0.03125 0.5h2.0625c-0.053-0.156-0.094-0.323-0.094-0.5 0-0.8483 0.6517-1.5 1.5-1.5h5c0.8483 0 1.5 0.6517 1.5 1.5s-0.6517 1.5-1.5 1.5h-1.6875c-0.28733 0.79501-0.78612 1.4793-1.4375 2h3.125c1.9217 0 3.5-1.5783 3.5-3.5s-1.5783-3.5-3.5-3.5h-5z"/>
+ <path style="block-progression:tb;color:#000000;text-transform:none;text-indent:0" d="m68.5-54.5c1.9217 0 3.5-1.5783 3.5-3.5 0-0.17425-0.0062-0.33232-0.03125-0.5h-2.0625c0.053 0.156 0.094 0.323 0.094 0.5 0 0.8483-0.6517 1.5-1.5 1.5h-5c-0.8483 0-1.5-0.6517-1.5-1.5s0.6517-1.5 1.5-1.5h1.6875c0.28733-0.79501 0.78612-1.4793 1.4375-2h-3.125c-1.9217 0-3.5 1.5783-3.5 3.5s1.5783 3.5 3.5 3.5h5z"/>
+ </g>
+</svg>
diff --git a/apps/files/img/share.png b/apps/files/img/share.png
new file mode 100644
index 00000000000..61c87e78b6c
--- /dev/null
+++ b/apps/files/img/share.png
Binary files differ
diff --git a/apps/files/img/share.svg b/apps/files/img/share.svg
new file mode 100644
index 00000000000..97f52f2e783
--- /dev/null
+++ b/apps/files/img/share.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="16" width="16" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <g opacity=".5" transform="translate(0 -1036.4)">
+ <path d="m12.228 1037.4c-1.3565 0-2.4592 1.0977-2.4592 2.4542 0 0.075 0.0084 0.1504 0.0149 0.2236l-4.7346 2.4145c-0.4291-0.3667-0.98611-0.5863-1.5947-0.5863-1.3565 0-2.4542 1.0977-2.4542 2.4543 0 1.3565 1.0977 2.4542 2.4542 2.4542 0.54607 0 1.0528-0.1755 1.4606-0.477l4.8637 2.4741c-0.0024 0.044-0.0099 0.089-0.0099 0.1342 0 1.3565 1.1027 2.4542 2.4592 2.4542s2.4542-1.0977 2.4542-2.4542-1.0977-2.4592-2.4542-2.4592c-0.63653 0-1.218 0.2437-1.6544 0.6409l-4.6953-2.4c0.01892-0.1228 0.03478-0.2494 0.03478-0.3775 0-0.072-0.0089-0.1437-0.0149-0.2137l4.7395-2.4145c0.42802 0.3627 0.98488 0.5813 1.5898 0.5813 1.3565 0 2.4542-1.1027 2.4542-2.4592s-1.0977-2.4542-2.4542-2.4542z"/>
+ </g>
+</svg>
diff --git a/apps/files/img/star.png b/apps/files/img/star.png
new file mode 100644
index 00000000000..6b0ec67ec1c
--- /dev/null
+++ b/apps/files/img/star.png
Binary files differ
diff --git a/apps/files/img/star.svg b/apps/files/img/star.svg
new file mode 100644
index 00000000000..fb3eef1e452
--- /dev/null
+++ b/apps/files/img/star.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="16" width="16" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <g opacity=".5" transform="matrix(.049689 0 0 .049689 -7.3558 -36.792)">
+ <path d="m330.36 858.43 43.111 108.06 117.64 9.2572-89.445 74.392 27.55 114.75-98.391-62.079-100.62 61.66 28.637-112.76-89.734-76.638 116.09-7.6094z" transform="translate(-21.071,-112.5)"/>
+ </g>
+</svg>
diff --git a/apps/files/lib/activity.php b/apps/files/lib/activity.php
index c376dc7b147..fff49ea4ea5 100644
--- a/apps/files/lib/activity.php
+++ b/apps/files/lib/activity.php
@@ -24,16 +24,20 @@ namespace OCA\Files;
use OC\L10N\Factory;
use OCP\Activity\IExtension;
+use OCP\Activity\IManager;
+use OCP\IConfig;
use OCP\IL10N;
use OCP\IURLGenerator;
class Activity implements IExtension {
const FILTER_FILES = 'files';
+ const FILTER_FAVORITES = 'files_favorites';
const TYPE_SHARE_CREATED = 'file_created';
const TYPE_SHARE_CHANGED = 'file_changed';
const TYPE_SHARE_DELETED = 'file_deleted';
const TYPE_SHARE_RESTORED = 'file_restored';
+ const TYPE_FAVORITES = 'files_favorites';
/** @var IL10N */
protected $l;
@@ -44,14 +48,29 @@ class Activity implements IExtension {
/** @var IURLGenerator */
protected $URLGenerator;
+ /** @var \OCP\Activity\IManager */
+ protected $activityManager;
+
+ /** @var \OCP\IConfig */
+ protected $config;
+
+ /** @var \OCA\Files\ActivityHelper */
+ protected $helper;
+
/**
* @param Factory $languageFactory
* @param IURLGenerator $URLGenerator
+ * @param IManager $activityManager
+ * @param ActivityHelper $helper
+ * @param IConfig $config
*/
- public function __construct(Factory $languageFactory, IURLGenerator $URLGenerator) {
+ public function __construct(Factory $languageFactory, IURLGenerator $URLGenerator, IManager $activityManager, ActivityHelper $helper, IConfig $config) {
$this->languageFactory = $languageFactory;
$this->URLGenerator = $URLGenerator;
$this->l = $this->getL10N();
+ $this->activityManager = $activityManager;
+ $this->helper = $helper;
+ $this->config = $config;
}
/**
@@ -74,6 +93,7 @@ class Activity implements IExtension {
return [
self::TYPE_SHARE_CREATED => (string) $l->t('A new file or folder has been <strong>created</strong>'),
self::TYPE_SHARE_CHANGED => (string) $l->t('A file or folder has been <strong>changed</strong>'),
+ self::TYPE_FAVORITES => (string) $l->t('Limit notifications about creation and changes to your <strong>favorite files</strong> <em>(Stream only)</em>'),
self::TYPE_SHARE_DELETED => (string) $l->t('A file or folder has been <strong>deleted</strong>'),
self::TYPE_SHARE_RESTORED => (string) $l->t('A file or folder has been <strong>restored</strong>'),
];
@@ -229,6 +249,13 @@ class Activity implements IExtension {
*/
public function getNavigation() {
return [
+ 'top' => [
+ self::FILTER_FAVORITES => [
+ 'id' => self::FILTER_FAVORITES,
+ 'name' => (string) $this->l->t('Favorites'),
+ 'url' => $this->URLGenerator->linkToRoute('activity.Activities.showList', ['filter' => self::FILTER_FAVORITES]),
+ ],
+ ],
'apps' => [
self::FILTER_FILES => [
'id' => self::FILTER_FILES,
@@ -236,7 +263,6 @@ class Activity implements IExtension {
'url' => $this->URLGenerator->linkToRoute('activity.Activities.showList', ['filter' => self::FILTER_FILES]),
],
],
- 'top' => [],
];
}
@@ -247,7 +273,7 @@ class Activity implements IExtension {
* @return boolean
*/
public function isFilterValid($filterValue) {
- return $filterValue === self::FILTER_FILES;
+ return $filterValue === self::FILTER_FILES || $filterValue === self::FILTER_FAVORITES;
}
/**
@@ -259,7 +285,7 @@ class Activity implements IExtension {
* @return array|false
*/
public function filterNotificationTypes($types, $filter) {
- if ($filter === self::FILTER_FILES) {
+ if ($filter === self::FILTER_FILES || $filter === self::FILTER_FAVORITES) {
return array_intersect([
self::TYPE_SHARE_CREATED,
self::TYPE_SHARE_CHANGED,
@@ -280,9 +306,63 @@ class Activity implements IExtension {
* @return array|false
*/
public function getQueryForFilter($filter) {
+ $user = $this->activityManager->getCurrentUserId();
+ // Display actions from all files
if ($filter === self::FILTER_FILES) {
return ['`app` = ?', ['files']];
}
+
+ if (!$user) {
+ // Remaining filters only work with a user/token
+ return false;
+ }
+
+ // Display actions from favorites only
+ if ($filter === self::FILTER_FAVORITES || in_array($filter, ['all', 'by', 'self']) && $this->userSettingFavoritesOnly($user)) {
+ try {
+ $favorites = $this->helper->getFavoriteFilePaths($user);
+ } catch (\RuntimeException $e) {
+ // Too many favorites, can not put them into one query anymore...
+ return ['`app` = ?', ['files']];
+ }
+
+ /*
+ * Display activities only, when they are not `type` create/change
+ * or `file` is a favorite or in a favorite folder
+ */
+ $parameters = $fileQueryList = [];
+ $parameters[] = 'files';
+
+ $fileQueryList[] = '(`type` <> ? AND `type` <> ?)';
+ $parameters[] = self::TYPE_SHARE_CREATED;
+ $parameters[] = self::TYPE_SHARE_CHANGED;
+
+ foreach ($favorites['items'] as $favorite) {
+ $fileQueryList[] = '`file` = ?';
+ $parameters[] = $favorite;
+ }
+ foreach ($favorites['folders'] as $favorite) {
+ $fileQueryList[] = '`file` LIKE ?';
+ $parameters[] = $favorite . '/%';
+ }
+
+ $parameters[] = 'files';
+
+ return [
+ ' CASE WHEN `app` = ? THEN (' . implode(' OR ', $fileQueryList) . ') ELSE `app` <> ? END ',
+ $parameters,
+ ];
+ }
return false;
}
+
+ /**
+ * Is the file actions favorite limitation enabled?
+ *
+ * @param string $user
+ * @return bool
+ */
+ protected function userSettingFavoritesOnly($user) {
+ return (bool) $this->config->getUserValue($user, 'activity', 'notify_stream_' . self::TYPE_FAVORITES, false);
+ }
}
diff --git a/apps/files/lib/activityhelper.php b/apps/files/lib/activityhelper.php
new file mode 100644
index 00000000000..f9ff722b1c2
--- /dev/null
+++ b/apps/files/lib/activityhelper.php
@@ -0,0 +1,84 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\Files;
+
+use OCP\Files\Folder;
+use OCP\ITagManager;
+
+class ActivityHelper {
+ /** If a user has a lot of favorites the query might get too slow and long */
+ const FAVORITE_LIMIT = 50;
+
+ /** @var \OCP\ITagManager */
+ protected $tagManager;
+
+ /**
+ * @param ITagManager $tagManager
+ */
+ public function __construct(ITagManager $tagManager) {
+ $this->tagManager = $tagManager;
+ }
+
+ /**
+ * Returns an array with the favorites
+ *
+ * @param string $user
+ * @return array
+ * @throws \RuntimeException when too many or no favorites where found
+ */
+ public function getFavoriteFilePaths($user) {
+ $tags = $this->tagManager->load('files', [], false, $user);
+ $favorites = $tags->getFavorites();
+
+ if (empty($favorites)) {
+ throw new \RuntimeException('No favorites', 1);
+ } else if (isset($favorites[self::FAVORITE_LIMIT])) {
+ throw new \RuntimeException('Too many favorites', 2);
+ }
+
+ // Can not DI because the user is not known on instantiation
+ $rootFolder = \OC::$server->getUserFolder($user);
+ $folders = $items = [];
+ foreach ($favorites as $favorite) {
+ $nodes = $rootFolder->getById($favorite);
+ if (!empty($nodes)) {
+ /** @var \OCP\Files\Node $node */
+ $node = array_shift($nodes);
+ $path = substr($node->getPath(), strlen($user . '/files/'));
+
+ $items[] = $path;
+ if ($node instanceof Folder) {
+ $folders[] = $path;
+ }
+ }
+ }
+
+ if (empty($items)) {
+ throw new \RuntimeException('No favorites', 1);
+ }
+
+ return [
+ 'items' => $items,
+ 'folders' => $folders,
+ ];
+ }
+}
diff --git a/apps/files/templates/appnavigation.php b/apps/files/templates/appnavigation.php
index e6237c7f485..f586b0ecb28 100644
--- a/apps/files/templates/appnavigation.php
+++ b/apps/files/templates/appnavigation.php
@@ -1,13 +1,18 @@
<div id="app-navigation">
- <ul>
+ <ul class="with-icon">
<?php foreach ($_['navigationItems'] as $item) { ?>
- <li data-id="<?php p($item['id']) ?>" class="nav-<?php p($item['id']) ?>"><a href="<?php p(isset($item['href']) ? $item['href'] : '#') ?>"><?php p($item['name']);?></a></li>
+ <li data-id="<?php p($item['id']) ?>" class="nav-<?php p($item['id']) ?>">
+ <a href="<?php p(isset($item['href']) ? $item['href'] : '#') ?>"
+ class="nav-icon-<?php p($item['id']) ?> svg">
+ <?php p($item['name']);?>
+ </a>
+ </li>
<?php } ?>
</ul>
<div id="app-settings">
<div id="app-settings-header">
<button class="settings-button" data-apps-slide-toggle="#app-settings-content">
- <span class="hidden-visually"><?php p($l->t('Settings'));?></span>
+ <span><?php p($l->t('Settings'));?></span>
</button>
</div>
<div id="app-settings-content">
diff --git a/apps/files/tests/activitytest.php b/apps/files/tests/activitytest.php
new file mode 100644
index 00000000000..1f8d6330e51
--- /dev/null
+++ b/apps/files/tests/activitytest.php
@@ -0,0 +1,297 @@
+<?php
+/**
+ * Copyright (c) 2015 Joas Schilling <nickvergessen@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or
+ * later.
+ * See the COPYING-README file.
+ *
+*/
+
+namespace OCA\Files\Tests;
+
+use OCA\Files\Activity;
+use Test\TestCase;
+
+class ActivityTest extends TestCase {
+
+ /** @var \OC\ActivityManager */
+ private $activityManager;
+
+ /** @var \PHPUnit_Framework_MockObject_MockObject */
+ protected $request;
+
+ /** @var \PHPUnit_Framework_MockObject_MockObject */
+ protected $session;
+
+ /** @var \PHPUnit_Framework_MockObject_MockObject */
+ protected $config;
+
+ /** @var \PHPUnit_Framework_MockObject_MockObject */
+ protected $activityHelper;
+
+ /** @var \OCA\Files\Activity */
+ protected $activityExtension;
+
+ protected function setUp() {
+ parent::setUp();
+
+ $this->request = $this->getMockBuilder('OCP\IRequest')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->session = $this->getMockBuilder('OCP\IUserSession')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->config = $this->getMockBuilder('OCP\IConfig')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->activityHelper = $this->getMockBuilder('OCA\Files\ActivityHelper')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->activityManager = new \OC\ActivityManager(
+ $this->request,
+ $this->session,
+ $this->config
+ );
+
+ $this->activityExtension = $activityExtension = new Activity(
+ new \OC\L10N\Factory(),
+ $this->getMockBuilder('OCP\IURLGenerator')->disableOriginalConstructor()->getMock(),
+ $this->activityManager,
+ $this->activityHelper,
+ $this->config
+ );
+
+ $this->activityManager->registerExtension(function() use ($activityExtension) {
+ return $activityExtension;
+ });
+ }
+
+ public function testNotificationTypes() {
+ $result = $this->activityExtension->getNotificationTypes('en');
+ $this->assertTrue(is_array($result), 'Asserting getNotificationTypes() returns an array');
+ $this->assertCount(5, $result);
+ $this->assertArrayHasKey(Activity::TYPE_SHARE_CREATED, $result);
+ $this->assertArrayHasKey(Activity::TYPE_SHARE_CHANGED, $result);
+ $this->assertArrayHasKey(Activity::TYPE_FAVORITES, $result);
+ $this->assertArrayHasKey(Activity::TYPE_SHARE_DELETED, $result);
+ $this->assertArrayHasKey(Activity::TYPE_SHARE_RESTORED, $result);
+ }
+
+ public function testDefaultTypes() {
+ $result = $this->activityExtension->getDefaultTypes('stream');
+ $this->assertTrue(is_array($result), 'Asserting getDefaultTypes(stream) returns an array');
+ $this->assertCount(4, $result);
+ $result = array_flip($result);
+ $this->assertArrayHasKey(Activity::TYPE_SHARE_CREATED, $result);
+ $this->assertArrayHasKey(Activity::TYPE_SHARE_CHANGED, $result);
+ $this->assertArrayNotHasKey(Activity::TYPE_FAVORITES, $result);
+ $this->assertArrayHasKey(Activity::TYPE_SHARE_DELETED, $result);
+ $this->assertArrayHasKey(Activity::TYPE_SHARE_RESTORED, $result);
+
+ $result = $this->activityExtension->getDefaultTypes('email');
+ $this->assertFalse($result, 'Asserting getDefaultTypes(email) returns false');
+ }
+
+ public function testTranslate() {
+ $this->assertFalse(
+ $this->activityExtension->translate('files_sharing', '', [], false, false, 'en'),
+ 'Asserting that no translations are set for files_sharing'
+ );
+ }
+
+ public function testGetSpecialParameterList() {
+ $this->assertFalse(
+ $this->activityExtension->getSpecialParameterList('files_sharing', ''),
+ 'Asserting that no special parameters are set for files_sharing'
+ );
+ }
+
+ public function typeIconData() {
+ return [
+ [Activity::TYPE_SHARE_CHANGED, 'icon-change'],
+ [Activity::TYPE_SHARE_CREATED, 'icon-add-color'],
+ [Activity::TYPE_SHARE_DELETED, 'icon-delete-color'],
+ [Activity::TYPE_SHARE_RESTORED, false],
+ [Activity::TYPE_FAVORITES, false],
+ ['unknown type', false],
+ ];
+ }
+
+ /**
+ * @dataProvider typeIconData
+ *
+ * @param string $type
+ * @param mixed $expected
+ */
+ public function testTypeIcon($type, $expected) {
+ $this->assertSame($expected, $this->activityExtension->getTypeIcon($type));
+ }
+
+ public function testGroupParameter() {
+ $this->assertFalse(
+ $this->activityExtension->getGroupParameter(['app' => 'files_sharing']),
+ 'Asserting that no group parameters are set for files_sharing'
+ );
+ }
+
+ public function testNavigation() {
+ $result = $this->activityExtension->getNavigation();
+ $this->assertCount(1, $result['top']);
+ $this->assertArrayHasKey(Activity::FILTER_FAVORITES, $result['top']);
+
+ $this->assertCount(1, $result['apps']);
+ $this->assertArrayHasKey(Activity::FILTER_FILES, $result['apps']);
+ }
+
+ public function testIsFilterValid() {
+ $this->assertTrue($this->activityExtension->isFilterValid(Activity::FILTER_FAVORITES));
+ $this->assertTrue($this->activityExtension->isFilterValid(Activity::FILTER_FILES));
+ $this->assertFalse($this->activityExtension->isFilterValid('unknown filter'));
+ }
+
+ public function filterNotificationTypesData() {
+ return [
+ [
+ Activity::FILTER_FILES,
+ [
+ 'NT0',
+ Activity::TYPE_SHARE_CREATED,
+ Activity::TYPE_SHARE_CHANGED,
+ Activity::TYPE_SHARE_DELETED,
+ Activity::TYPE_SHARE_RESTORED,
+ Activity::TYPE_FAVORITES,
+ ], [
+ Activity::TYPE_SHARE_CREATED,
+ Activity::TYPE_SHARE_CHANGED,
+ Activity::TYPE_SHARE_DELETED,
+ Activity::TYPE_SHARE_RESTORED,
+ ],
+ ],
+ [
+ Activity::FILTER_FILES,
+ [
+ 'NT0',
+ Activity::TYPE_SHARE_CREATED,
+ Activity::TYPE_FAVORITES,
+ ],
+ [
+ Activity::TYPE_SHARE_CREATED,
+ ],
+ ],
+ [
+ Activity::FILTER_FAVORITES,
+ [
+ 'NT0',
+ Activity::TYPE_SHARE_CREATED,
+ Activity::TYPE_SHARE_CHANGED,
+ Activity::TYPE_SHARE_DELETED,
+ Activity::TYPE_SHARE_RESTORED,
+ Activity::TYPE_FAVORITES,
+ ], [
+ Activity::TYPE_SHARE_CREATED,
+ Activity::TYPE_SHARE_CHANGED,
+ Activity::TYPE_SHARE_DELETED,
+ Activity::TYPE_SHARE_RESTORED,
+ ],
+ ],
+ [
+ 'unknown filter',
+ [
+ 'NT0',
+ Activity::TYPE_SHARE_CREATED,
+ Activity::TYPE_SHARE_CHANGED,
+ Activity::TYPE_SHARE_DELETED,
+ Activity::TYPE_SHARE_RESTORED,
+ Activity::TYPE_FAVORITES,
+ ],
+ false,
+ ],
+ ];
+ }
+
+ /**
+ * @dataProvider filterNotificationTypesData
+ *
+ * @param string $filter
+ * @param array $types
+ * @param mixed $expected
+ */
+ public function testFilterNotificationTypes($filter, $types, $expected) {
+ $result = $this->activityExtension->filterNotificationTypes($types, $filter);
+ $this->assertEquals($expected, $result);
+ }
+
+ public function queryForFilterData() {
+ return [
+ [
+ new \RuntimeException(),
+ '`app` = ?',
+ ['files']
+ ],
+ [
+ [
+ 'items' => [],
+ 'folders' => [],
+ ],
+ ' CASE WHEN `app` = ? THEN ((`type` <> ? AND `type` <> ?)) ELSE `app` <> ? END ',
+ ['files', Activity::TYPE_SHARE_CREATED, Activity::TYPE_SHARE_CHANGED, 'files']
+ ],
+ [
+ [
+ 'items' => ['file.txt', 'folder'],
+ 'folders' => ['folder'],
+ ],
+ ' CASE WHEN `app` = ? THEN ((`type` <> ? AND `type` <> ?) OR `file` = ? OR `file` = ? OR `file` LIKE ?) ELSE `app` <> ? END ',
+ ['files', Activity::TYPE_SHARE_CREATED, Activity::TYPE_SHARE_CHANGED, 'file.txt', 'folder', 'folder/%', 'files']
+ ],
+ ];
+ }
+
+ /**
+ * @dataProvider queryForFilterData
+ *
+ * @param mixed $will
+ * @param string $query
+ * @param array $parameters
+ */
+ public function testQueryForFilter($will, $query, $parameters) {
+ $this->mockUserSession('test');
+
+ $this->config->expects($this->any())
+ ->method('getUserValue')
+ ->willReturnMap([
+ ['test', 'activity', 'notify_stream_' . Activity::TYPE_FAVORITES, false, true],
+ ]);
+ if (is_array($will)) {
+ $this->activityHelper->expects($this->any())
+ ->method('getFavoriteFilePaths')
+ ->with('test')
+ ->willReturn($will);
+ } else {
+ $this->activityHelper->expects($this->any())
+ ->method('getFavoriteFilePaths')
+ ->with('test')
+ ->willThrowException($will);
+ }
+
+ $result = $this->activityExtension->getQueryForFilter('all');
+ $this->assertEquals([$query, $parameters], $result);
+ }
+
+ protected function mockUserSession($user) {
+ $mockUser = $this->getMockBuilder('\OCP\IUser')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $mockUser->expects($this->any())
+ ->method('getUID')
+ ->willReturn($user);
+
+ $this->session->expects($this->any())
+ ->method('isLoggedIn')
+ ->willReturn(true);
+ $this->session->expects($this->any())
+ ->method('getUser')
+ ->willReturn($mockUser);
+ }
+}
diff --git a/apps/files/tests/ajax_rename.php b/apps/files/tests/ajax_rename.php
index 789177bb353..34e7f5085dd 100644
--- a/apps/files/tests/ajax_rename.php
+++ b/apps/files/tests/ajax_rename.php
@@ -38,21 +38,15 @@ class Test_OC_Files_App_Rename extends \Test\TestCase {
*/
private $files;
- private $originalStorage;
-
protected function setUp() {
parent::setUp();
- $this->originalStorage = \OC\Files\Filesystem::getStorage('/');
-
// mock OC_L10n
if (!self::$user) {
self::$user = uniqid();
}
\OC_User::createUser(self::$user, 'password');
- \OC_User::setUserId(self::$user);
-
- \OC\Files\Filesystem::init(self::$user, '/' . self::$user . '/files');
+ $this->loginAsUser(self::$user);
$l10nMock = $this->getMock('\OC_L10N', array('t'), array(), '', false);
$l10nMock->expects($this->any())
@@ -72,9 +66,8 @@ class Test_OC_Files_App_Rename extends \Test\TestCase {
protected function tearDown() {
$result = \OC_User::deleteUser(self::$user);
$this->assertTrue($result);
- \OC\Files\Filesystem::tearDown();
- \OC\Files\Filesystem::mount($this->originalStorage, array(), '/');
+ $this->logout();
parent::tearDown();
}
diff --git a/apps/files_external/lib/sftp_key.php b/apps/files_external/lib/sftp_key.php
index c460d81b8f0..d9bcadb9eb7 100644
--- a/apps/files_external/lib/sftp_key.php
+++ b/apps/files_external/lib/sftp_key.php
@@ -135,11 +135,16 @@ class SFTP_Key extends \OC\Files\Storage\SFTP {
}
public function test() {
- if (empty($this->getHost())) {
+
+ // FIXME: Use as expression in empty once PHP 5.4 support is dropped
+ $host = $this->getHost();
+ if (empty($host)) {
\OC::$server->getLogger()->warning('Hostname has not been specified');
return false;
}
- if (empty($this->getUser())) {
+ // FIXME: Use as expression in empty once PHP 5.4 support is dropped
+ $user = $this->getUser();
+ if (empty($user)) {
\OC::$server->getLogger()->warning('Username has not been specified');
return false;
}
diff --git a/apps/files_external/service/globalstoragesservice.php b/apps/files_external/service/globalstoragesservice.php
index 011730390b0..7df0f73f652 100644
--- a/apps/files_external/service/globalstoragesservice.php
+++ b/apps/files_external/service/globalstoragesservice.php
@@ -101,6 +101,7 @@ class GlobalStoragesService extends StoragesService {
* @param string $signal signal to trigger
*/
protected function triggerHooks(StorageConfig $storage, $signal) {
+ // FIXME: Use as expression in empty once PHP 5.4 support is dropped
$applicableUsers = $storage->getApplicableUsers();
$applicableGroups = $storage->getApplicableGroups();
if (empty($applicableUsers) && empty($applicableGroups)) {
@@ -149,8 +150,11 @@ class GlobalStoragesService extends StoragesService {
$groupAdditions = array_diff($newStorage->getApplicableGroups(), $oldStorage->getApplicableGroups());
$groupDeletions = array_diff($oldStorage->getApplicableGroups(), $newStorage->getApplicableGroups());
+ // FIXME: Use as expression in empty once PHP 5.4 support is dropped
// if no applicable were set, raise a signal for "all"
- if (empty($oldStorage->getApplicableUsers()) && empty($oldStorage->getApplicableGroups())) {
+ $oldApplicableUsers = $oldStorage->getApplicableUsers();
+ $oldApplicableGroups = $oldStorage->getApplicableGroups();
+ if (empty($oldApplicableUsers) && empty($oldApplicableGroups)) {
$this->triggerApplicableHooks(
Filesystem::signal_delete_mount,
$oldStorage->getMountPoint(),
@@ -191,8 +195,11 @@ class GlobalStoragesService extends StoragesService {
$groupAdditions
);
+ // FIXME: Use as expression in empty once PHP 5.4 support is dropped
// if no applicable, raise a signal for "all"
- if (empty($newStorage->getApplicableUsers()) && empty($newStorage->getApplicableGroups())) {
+ $newApplicableUsers = $newStorage->getApplicableUsers();
+ $newApplicableGroups = $newStorage->getApplicableGroups();
+ if (empty($newApplicableUsers) && empty($newApplicableGroups)) {
$this->triggerApplicableHooks(
Filesystem::signal_create_mount,
$newStorage->getMountPoint(),
diff --git a/apps/files_external/service/storagesservice.php b/apps/files_external/service/storagesservice.php
index 399a56677bf..51eb4abcc00 100644
--- a/apps/files_external/service/storagesservice.php
+++ b/apps/files_external/service/storagesservice.php
@@ -227,8 +227,10 @@ abstract class StoragesService {
if (!is_null($storageConfig->getPriority())) {
$options['priority'] = $storageConfig->getPriority();
}
- if (!empty($storageConfig->getMountOptions())) {
- $options['mountOptions'] = $storageConfig->getMountOptions();
+
+ $mountOptions = $storageConfig->getMountOptions();
+ if (!empty($mountOptions)) {
+ $options['mountOptions'] = $mountOptions;
}
$mountPoints[$mountType][$applicable][$rootMountPoint] = $options;
diff --git a/apps/files_sharing/api/local.php b/apps/files_sharing/api/local.php
index 571982d153b..1a5edbfd070 100644
--- a/apps/files_sharing/api/local.php
+++ b/apps/files_sharing/api/local.php
@@ -343,7 +343,7 @@ class Local {
if(isset($params['_put']['permissions'])) {
return self::updatePermissions($share, $params);
} elseif (isset($params['_put']['password'])) {
- return self::updatePassword($share, $params);
+ return self::updatePassword($params['id'], (int)$share['share_type'], $params['_put']['password']);
} elseif (isset($params['_put']['publicUpload'])) {
return self::updatePublicUpload($share, $params);
} elseif (isset($params['_put']['expireDate'])) {
@@ -457,47 +457,22 @@ class Local {
/**
* update password for public link share
- * @param array $share information about the share
- * @param array $params 'password'
+ * @param int $shareId
+ * @param int $shareType
+ * @param string $password
* @return \OC_OCS_Result
*/
- private static function updatePassword($share, $params) {
-
- $itemSource = $share['item_source'];
- $itemType = $share['item_type'];
-
- if( (int)$share['share_type'] !== \OCP\Share::SHARE_TYPE_LINK) {
+ private static function updatePassword($shareId, $shareType, $password) {
+ if($shareType !== \OCP\Share::SHARE_TYPE_LINK) {
return new \OC_OCS_Result(null, 400, "password protection is only supported for public shares");
}
- $shareWith = isset($params['_put']['password']) ? $params['_put']['password'] : null;
-
- if($shareWith === '') {
- $shareWith = null;
- }
-
- $items = \OCP\Share::getItemShared($itemType, $itemSource);
-
- $checkExists = false;
- foreach ($items as $item) {
- if($item['share_type'] === \OCP\Share::SHARE_TYPE_LINK) {
- $checkExists = true;
- $permissions = $item['permissions'];
- }
- }
-
- if (!$checkExists) {
- return new \OC_OCS_Result(null, 404, "share doesn't exists, can't change password");
+ if($password === '') {
+ $password = null;
}
try {
- $result = \OCP\Share::shareItem(
- $itemType,
- $itemSource,
- \OCP\Share::SHARE_TYPE_LINK,
- $shareWith,
- $permissions
- );
+ $result = \OCP\Share::setPassword($shareId, $password);
} catch (\Exception $e) {
return new \OC_OCS_Result(null, 403, $e->getMessage());
}
diff --git a/apps/files_sharing/appinfo/app.php b/apps/files_sharing/appinfo/app.php
index a222973fc34..d009fbca3b9 100644
--- a/apps/files_sharing/appinfo/app.php
+++ b/apps/files_sharing/appinfo/app.php
@@ -51,6 +51,10 @@ OCP\Share::registerBackend('folder', 'OC_Share_Backend_Folder', 'file');
OCP\Util::addScript('files_sharing', 'share');
OCP\Util::addScript('files_sharing', 'external');
+// FIXME: registering a job here will cause additional useless SQL queries
+// when the route is not cron.php, needs a better way
+\OC::$server->getJobList()->add('OCA\Files_sharing\Lib\DeleteOrphanedSharesJob');
+
\OC::$server->getActivityManager()->registerExtension(function() {
return new \OCA\Files_Sharing\Activity(
\OC::$server->query('L10NFactory'),
diff --git a/apps/files_sharing/js/settings-admin.js b/apps/files_sharing/js/settings-admin.js
index 257c864b04f..95578bff548 100644
--- a/apps/files_sharing/js/settings-admin.js
+++ b/apps/files_sharing/js/settings-admin.js
@@ -8,4 +8,5 @@ $(document).ready(function() {
OC.AppConfig.setValue('files_sharing', $(this).attr('name'), value);
});
+ $('.section .icon-info').tipsy({gravity: 'w'});
});
diff --git a/apps/files_sharing/lib/deleteorphanedsharesjob.php b/apps/files_sharing/lib/deleteorphanedsharesjob.php
new file mode 100644
index 00000000000..f39078b778f
--- /dev/null
+++ b/apps/files_sharing/lib/deleteorphanedsharesjob.php
@@ -0,0 +1,59 @@
+<?php
+/**
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\Files_sharing\Lib;
+
+use Doctrine\DBAL\Platforms\SqlitePlatform;
+use OCP\IDBConnection;
+use OC\BackgroundJob\TimedJob;
+
+/**
+ * Delete all share entries that have no matching entries in the file cache table.
+ */
+class DeleteOrphanedSharesJob extends TimedJob {
+
+ /**
+ * Default interval in minutes
+ *
+ * @var int $defaultIntervalMin
+ **/
+ protected $defaultIntervalMin = 15;
+
+ /**
+ * Makes the background job do its work
+ *
+ * @param array $argument unused argument
+ */
+ public function run($argument) {
+ $connection = \OC::$server->getDatabaseConnection();
+ $logger = \OC::$server->getLogger();
+
+ $sql =
+ 'DELETE FROM `*PREFIX*share` ' .
+ 'WHERE `item_type` in (\'file\', \'folder\') ' .
+ 'AND NOT EXISTS (SELECT `fileid` FROM `*PREFIX*filecache` WHERE `file_source` = `fileid`)';
+
+ $deletedEntries = $connection->executeUpdate($sql);
+ $logger->info("$deletedEntries orphaned share(s) deleted", ['app' => 'DeleteOrphanedSharesJob']);
+ }
+
+}
diff --git a/apps/files_sharing/lib/helper.php b/apps/files_sharing/lib/helper.php
index 3f1de7233ae..5b5525e244f 100644
--- a/apps/files_sharing/lib/helper.php
+++ b/apps/files_sharing/lib/helper.php
@@ -33,7 +33,6 @@ class Helper {
\OCP\Util::connectHook('OC_Filesystem', 'setup', '\OC\Files\Storage\Shared', 'setup');
\OCP\Util::connectHook('OC_Filesystem', 'setup', '\OCA\Files_Sharing\External\Manager', 'setup');
\OCP\Util::connectHook('OC_Filesystem', 'post_write', '\OC\Files\Cache\Shared_Updater', 'writeHook');
- \OCP\Util::connectHook('OC_Filesystem', 'post_delete', '\OC\Files\Cache\Shared_Updater', 'postDeleteHook');
\OCP\Util::connectHook('OC_Filesystem', 'delete', '\OC\Files\Cache\Shared_Updater', 'deleteHook');
\OCP\Util::connectHook('OC_Filesystem', 'post_rename', '\OC\Files\Cache\Shared_Updater', 'renameHook');
\OCP\Util::connectHook('OC_Filesystem', 'post_delete', '\OCA\Files_Sharing\Hooks', 'unshareChildren');
diff --git a/apps/files_sharing/lib/updater.php b/apps/files_sharing/lib/updater.php
index d748d5555f6..322c031f2f1 100644
--- a/apps/files_sharing/lib/updater.php
+++ b/apps/files_sharing/lib/updater.php
@@ -27,9 +27,6 @@ namespace OC\Files\Cache;
class Shared_Updater {
- // shares which can be removed from oc_share after the delete operation was successful
- static private $toRemove = array();
-
/**
* walk up the users file tree and update the etags
* @param string $user
@@ -82,25 +79,6 @@ class Shared_Updater {
}
/**
- * remove all shares for a given file if the file was deleted
- *
- * @param string $path
- */
- private static function removeShare($path) {
- $fileSource = self::$toRemove[$path];
-
- if (!\OC\Files\Filesystem::file_exists($path)) {
- $query = \OC_DB::prepare('DELETE FROM `*PREFIX*share` WHERE `file_source`=?');
- try {
- \OC_DB::executeAudited($query, array($fileSource));
- } catch (\Exception $e) {
- \OCP\Util::writeLog('files_sharing', "can't remove share: " . $e->getMessage(), \OCP\Util::WARN);
- }
- }
- unset(self::$toRemove[$path]);
- }
-
- /**
* @param array $params
*/
static public function writeHook($params) {
@@ -122,19 +100,6 @@ class Shared_Updater {
static public function deleteHook($params) {
$path = $params['path'];
self::correctFolders($path);
-
- $fileInfo = \OC\Files\Filesystem::getFileInfo($path);
-
- // mark file as deleted so that we can clean up the share table if
- // the file was deleted successfully
- self::$toRemove[$path] = $fileInfo['fileid'];
- }
-
- /**
- * @param array $params
- */
- static public function postDeleteHook($params) {
- self::removeShare($params['path']);
}
/**
diff --git a/apps/files_sharing/templates/settings-admin.php b/apps/files_sharing/templates/settings-admin.php
index bd803517597..376f2b71aee 100644
--- a/apps/files_sharing/templates/settings-admin.php
+++ b/apps/files_sharing/templates/settings-admin.php
@@ -4,6 +4,9 @@
?>
<div id="fileSharingSettings">
<h3><?php p($l->t('Federated Cloud Sharing'));?></h3>
+ <a target="_blank" class="icon-info svg"
+ title="<?php p($l->t('Open documentation'));?>"
+ href="<?php p(link_to_docs('admin-sharing-federated')); ?>"></a>
<p>
<input type="checkbox" name="outgoing_server2server_share_enabled" id="outgoingServer2serverShareEnabled"
diff --git a/apps/files_sharing/tests/deleteorphanedsharesjobtest.php b/apps/files_sharing/tests/deleteorphanedsharesjobtest.php
new file mode 100644
index 00000000000..20f3bcd5ebd
--- /dev/null
+++ b/apps/files_sharing/tests/deleteorphanedsharesjobtest.php
@@ -0,0 +1,162 @@
+<?php
+/**
+ * @author Morris Jobke <hey@morrisjobke.de>
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace Test\BackgroundJob;
+
+use OCA\Files_sharing\Lib\DeleteOrphanedSharesJob;
+
+class DeleteOrphanedSharesJobTest extends \Test\TestCase {
+
+ /**
+ * @var bool
+ */
+ private static $trashBinStatus;
+
+ /**
+ * @var DeleteOrphanedSharesJob
+ */
+ private $job;
+
+ /**
+ * @var \OCP\IDBConnection
+ */
+ private $connection;
+
+ /**
+ * @var string
+ */
+ private $user1;
+
+ /**
+ * @var string
+ */
+ private $user2;
+
+ public static function setUpBeforeClass() {
+ $appManager = \OC::$server->getAppManager();
+ self::$trashBinStatus = $appManager->isEnabledForUser('files_trashbin');
+ $appManager->disableApp('files_trashbin');
+
+ // just in case...
+ \OC\Files\Filesystem::getLoader()->removeStorageWrapper('oc_trashbin');
+ }
+
+ public static function tearDownAfterClass() {
+ if (self::$trashBinStatus) {
+ \OC::$server->getAppManager()->enableApp('files_trashbin');
+ }
+ }
+
+ protected function setup() {
+ parent::setUp();
+
+ $this->connection = \OC::$server->getDatabaseConnection();
+
+ $this->user1 = $this->getUniqueID('user1_');
+ $this->user2 = $this->getUniqueID('user2_');
+
+ $userManager = \OC::$server->getUserManager();
+ $userManager->createUser($this->user1, 'pass');
+ $userManager->createUser($this->user2, 'pass');
+
+ \OC::registerShareHooks();
+
+ $this->job = new DeleteOrphanedSharesJob();
+ }
+
+ protected function tearDown() {
+ $this->connection->executeUpdate('DELETE FROM `*PREFIX*share` WHERE `item_type` in (\'file\', \'folder\')');
+
+ $userManager = \OC::$server->getUserManager();
+ $user1 = $userManager->get($this->user1);
+ if($user1) {
+ $user1->delete();
+ }
+ $user2 = $userManager->get($this->user2);
+ if($user2) {
+ $user2->delete();
+ }
+
+ $this->logout();
+
+ parent::tearDown();
+ }
+
+ private function getShares() {
+ $shares = [];
+ $result = $this->connection->executeQuery('SELECT * FROM `*PREFIX*share`');
+ while ($row = $result->fetch()) {
+ $shares[] = $row;
+ }
+ $result->closeCursor();
+ return $shares;
+ }
+
+ /**
+ * Test clearing orphaned shares
+ */
+ public function testClearShares() {
+ $this->loginAsUser($this->user1);
+
+ $view = new \OC\Files\View('/' . $this->user1 . '/');
+ $view->mkdir('files/test');
+ $view->mkdir('files/test/sub');
+
+ $fileInfo = $view->getFileInfo('files/test/sub');
+ $fileId = $fileInfo->getId();
+
+ $this->assertTrue(
+ \OCP\Share::shareItem('folder', $fileId, \OCP\Share::SHARE_TYPE_USER, $this->user2, \OCP\Constants::PERMISSION_READ),
+ 'Failed asserting that user 1 successfully shared "test/sub" with user 2.'
+ );
+
+ $this->assertCount(1, $this->getShares());
+
+ $this->job->run([]);
+
+ $this->assertCount(1, $this->getShares(), 'Linked shares not deleted');
+
+ $view->unlink('files/test');
+
+ $this->job->run([]);
+
+ $this->assertCount(0, $this->getShares(), 'Orphaned shares deleted');
+ }
+
+ public function testKeepNonFileShares() {
+ $this->loginAsUser($this->user1);
+
+ \OCP\Share::registerBackend('test', 'Test_Share_Backend');
+
+ $this->assertTrue(
+ \OCP\Share::shareItem('test', 'test.txt', \OCP\Share::SHARE_TYPE_USER, $this->user2, \OCP\Constants::PERMISSION_READ),
+ 'Failed asserting that user 1 successfully shared something with user 2.'
+ );
+
+ $this->assertCount(1, $this->getShares());
+
+ $this->job->run([]);
+
+ $this->assertCount(1, $this->getShares(), 'Non-file shares kept');
+ }
+}
+
diff --git a/apps/files_trashbin/tests/storage.php b/apps/files_trashbin/tests/storage.php
index f1ac055d335..d5bd7c318d3 100644
--- a/apps/files_trashbin/tests/storage.php
+++ b/apps/files_trashbin/tests/storage.php
@@ -35,11 +35,6 @@ class Storage extends \Test\TestCase {
private $user;
/**
- * @var \OC\Files\Storage\Storage
- **/
- private $originalStorage;
-
- /**
* @var \OC\Files\View
*/
private $rootView;
@@ -61,8 +56,6 @@ class Storage extends \Test\TestCase {
// this will setup the FS
$this->loginAsUser($this->user);
- $this->originalStorage = \OC\Files\Filesystem::getStorage('/');
-
\OCA\Files_Trashbin\Storage::setupStorage();
$this->rootView = new \OC\Files\View('/');
@@ -73,7 +66,6 @@ class Storage extends \Test\TestCase {
protected function tearDown() {
\OC\Files\Filesystem::getLoader()->removeStorageWrapper('oc_trashbin');
- \OC\Files\Filesystem::mount($this->originalStorage, array(), '/');
$this->logout();
\OC_User::deleteUser($this->user);
\OC_Hook::clear();
diff --git a/apps/user_ldap/ajax/getNewServerConfigPrefix.php b/apps/user_ldap/ajax/getNewServerConfigPrefix.php
index 7d1434a8fb4..d0135917886 100644
--- a/apps/user_ldap/ajax/getNewServerConfigPrefix.php
+++ b/apps/user_ldap/ajax/getNewServerConfigPrefix.php
@@ -32,4 +32,17 @@ sort($serverConnections);
$lk = array_pop($serverConnections);
$ln = intval(str_replace('s', '', $lk));
$nk = 's'.str_pad($ln+1, 2, '0', STR_PAD_LEFT);
-OCP\JSON::success(array('configPrefix' => $nk));
+
+$resultData = array('configPrefix' => $nk);
+
+if(isset($_POST['copyConfig'])) {
+ $originalConfig = new \OCA\user_ldap\lib\Configuration($_POST['copyConfig']);
+ $newConfig = new \OCA\user_ldap\lib\Configuration($nk, false);
+ $newConfig->setConfiguration($originalConfig->getConfiguration());
+ $newConfig->saveConfiguration();
+} else {
+ $configuration = new \OCA\user_ldap\lib\Configuration($nk, false);
+ $resultData['defaults'] = $configuration->getDefaults();
+}
+
+OCP\JSON::success($resultData);
diff --git a/apps/user_ldap/ajax/wizard.php b/apps/user_ldap/ajax/wizard.php
index ab521b5bf0e..267b9568a28 100644
--- a/apps/user_ldap/ajax/wizard.php
+++ b/apps/user_ldap/ajax/wizard.php
@@ -72,13 +72,11 @@ switch($action) {
case 'determineGroupsForGroups':
case 'determineAttributes':
case 'getUserListFilter':
- case 'getLoginFilterMode':
case 'getUserLoginFilter':
- case 'getUserFilterMode':
case 'getGroupFilter':
- case 'getGroupFilterMode':
case 'countUsers':
case 'countGroups':
+ case 'countInBaseDN':
try {
$result = $wizard->$action();
if($result !== false) {
@@ -93,6 +91,23 @@ switch($action) {
exit;
break;
+ case 'testLoginName': {
+ try {
+ $loginName = $_POST['ldap_test_loginname'];
+ $result = $wizard->$action($loginName);
+ if($result !== false) {
+ OCP\JSON::success($result->getResultArray());
+ exit;
+ }
+ } catch (\Exception $e) {
+ \OCP\JSON::error(array('message' => $e->getMessage()));
+ exit;
+ }
+ \OCP\JSON::error();
+ exit;
+ break;
+ }
+
case 'save':
$key = isset($_POST['cfgkey']) ? $_POST['cfgkey'] : false;
$val = isset($_POST['cfgval']) ? $_POST['cfgval'] : null;
@@ -115,6 +130,6 @@ switch($action) {
OCP\JSON::success();
break;
default:
- //TODO: return 4xx error
+ \OCP\JSON::error(array('message' => $l->t('Action does not exist')));
break;
}
diff --git a/apps/user_ldap/appinfo/version b/apps/user_ldap/appinfo/version
index 8f0916f768f..a918a2aa18d 100644
--- a/apps/user_ldap/appinfo/version
+++ b/apps/user_ldap/appinfo/version
@@ -1 +1 @@
-0.5.0
+0.6.0
diff --git a/apps/user_ldap/css/settings.css b/apps/user_ldap/css/settings.css
index 8f339451c64..b351f9ae2af 100644
--- a/apps/user_ldap/css/settings.css
+++ b/apps/user_ldap/css/settings.css
@@ -1,6 +1,6 @@
.table {
display: table;
- width: 60%;
+ width: 85%;
}
.tablerow {
@@ -21,10 +21,18 @@
margin-left: 3px;
}
+.ldapIconCopy {
+ background-image: url('../img/copy.svg');
+}
+
.invisible {
visibility: hidden;
}
+.forceHidden {
+ display: none !important;
+}
+
.ldapSettingsTabs {
float: right !important;
}
@@ -49,13 +57,16 @@
}
#ldapWizard1 .hostPortCombinator div span {
- width: 7%;
- display: table-cell;
+ width: 14.5%;
+ display: inline-block;
text-align: right;
}
#ldapWizard1 .host {
- width: 96.5% !important;
+ width: 100%;
+ margin-left: 0;
+ margin-right: 0;
+ border: 0;
}
.tableCellInput {
@@ -77,7 +88,7 @@
color: #FF3B3B;
}
-.wizSpinner {
+.ldapSpinner {
height: 15px;
margin: 5px;
}
@@ -104,10 +115,51 @@
width: auto;
}
+.ldapManyGroupsSupport span {
+ display: inline-block;
+ vertical-align: top;
+ height: 150px;
+}
+
+.ldapManyGroupsSupport span button {
+ margin-top: 35px;
+}
+
+.ldapManyGroupsSearch {
+ width: 425px !important;
+}
+
+.ldapGroupList {
+ height: 150px;
+ width: 200px;
+}
+
#ldap fieldset input, #ldap fieldset textarea {
width: 60%;
}
+#ldap fieldset textarea ~ button {
+ vertical-align: text-bottom;
+}
+
+input.ldapVerifyInput {
+ width: 150px !important;
+}
+
+.ldapInputColElement {
+ width: 35%;
+ display: inline-block;
+ padding-left: 10px;
+}
+
+.ldapToggle {
+ text-decoration: underline;
+}
+
+span.ldapInputColElement {
+ margin-top: 9px;
+}
+
#ldap fieldset p input[type=checkbox] {
vertical-align: bottom;
}
diff --git a/apps/user_ldap/img/copy.png b/apps/user_ldap/img/copy.png
new file mode 100644
index 00000000000..283d627a5a7
--- /dev/null
+++ b/apps/user_ldap/img/copy.png
Binary files differ
diff --git a/apps/user_ldap/img/copy.svg b/apps/user_ldap/img/copy.svg
new file mode 100644
index 00000000000..2e19d8066e3
--- /dev/null
+++ b/apps/user_ldap/img/copy.svg
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:ns1="http://sozi.baierouge.fr"
+ xmlns:cc="http://web.resource.org/cc/"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ id="svg1"
+ sodipodi:docname="copy.svg"
+ viewBox="0 0 60 60"
+ sodipodi:version="0.32"
+ _SVGFile__filename="oldscale/actions/copy.svg"
+ version="1.0"
+ y="0"
+ x="0"
+ inkscape:version="0.40"
+ sodipodi:docbase="/home/danny/work/flat/SVG/mono/scalable/actions"
+ >
+ <sodipodi:namedview
+ id="base"
+ bordercolor="#666666"
+ inkscape:pageshadow="2"
+ inkscape:window-y="0"
+ pagecolor="#ffffff"
+ inkscape:window-height="699"
+ inkscape:zoom="4.9119411"
+ inkscape:window-x="0"
+ borderopacity="1.0"
+ inkscape:current-layer="svg1"
+ inkscape:cx="60.290892"
+ inkscape:cy="24.030855"
+ inkscape:window-width="1024"
+ inkscape:pageopacity="0.0"
+ />
+ <path
+ id="path1716"
+ style="stroke-linejoin:round;stroke:#ffffff;stroke-width:8.3605;fill:none"
+ transform="matrix(.97183 0 0 .97183 .87037 .23733)"
+ d="m7.513 4.5767c-1.3709 0-2.4745 1.1036-2.4745 2.4745v31.564c0 1.371 1.1036 2.474 2.4745 2.474h29.783c1.371 0 2.474-1.103 2.474-2.474v-31.564c0-1.3707-1.103-2.4743-2.474-2.4743h-29.783z"
+ />
+ <path
+ id="rect1101"
+ style="stroke-linejoin:round;fill-rule:evenodd;stroke:#000000;stroke-width:3.2156;fill:#ffffff"
+ transform="matrix(.97183 0 0 .97183 .87037 .23733)"
+ d="m7.513 4.5767c-1.3709 0-2.4745 1.1036-2.4745 2.4745v31.564c0 1.371 1.1036 2.474 2.4745 2.474h29.783c1.371 0 2.474-1.103 2.474-2.474v-31.564c0-1.3707-1.103-2.4743-2.474-2.4743h-29.783z"
+ />
+ <path
+ id="path1094"
+ style="stroke-linejoin:round;stroke:#ffffff;stroke-width:8.3605;fill:none"
+ transform="matrix(.97183 0 0 .97183 .87037 .23733)"
+ d="m22.652 20.161c-1.37 0-2.474 1.104-2.474 2.475v31.564c0 1.371 1.104 2.474 2.474 2.474h29.783c1.371 0 2.475-1.103 2.475-2.474v-31.564c0-1.371-1.104-2.475-2.475-2.475h-29.783z"
+ />
+ <path
+ id="rect1111"
+ style="stroke-linejoin:round;fill-rule:evenodd;stroke:#000000;stroke-width:3.2156;fill:#ffffff"
+ transform="matrix(.97183 0 0 .97183 .87037 .23733)"
+ d="m22.652 20.161c-1.37 0-2.474 1.104-2.474 2.475v31.564c0 1.371 1.104 2.474 2.474 2.474h29.783c1.371 0 2.475-1.103 2.475-2.474v-31.564c0-1.371-1.104-2.475-2.475-2.475h-29.783z"
+ />
+ <path
+ id="path1718"
+ style="stroke-linejoin:round;stroke:#ffffff;stroke-linecap:round;stroke-width:8.125;fill:none"
+ d="m12.457 21.599c2.325 20.529 20.15 19.15 21.296 19.043v6.139l8.981-8.889-8.981-8.87v6.066c-1.348 0.159-13.941 1.422-21.296-13.489z"
+ />
+ <path
+ id="path1574"
+ style="stroke-linejoin:round;fill-rule:evenodd;stroke:#000000;stroke-linecap:round;stroke-width:3.125;fill:#000000"
+ d="m12.457 21.599c2.325 20.529 20.15 19.15 21.296 19.043v6.139l8.981-8.889-8.981-8.87v6.066c-1.348 0.159-13.941 1.422-21.296-13.489z"
+ />
+ <metadata
+ >
+ <rdf:RDF
+ >
+ <cc:Work
+ >
+ <dc:format
+ >image/svg+xml</dc:format
+ >
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage"
+ />
+ <cc:license
+ rdf:resource="http://creativecommons.org/licenses/publicdomain/"
+ />
+ <dc:publisher
+ >
+ <cc:Agent
+ rdf:about="http://openclipart.org/"
+ >
+ <dc:title
+ >Openclipart</dc:title
+ >
+ </cc:Agent
+ >
+ </dc:publisher
+ >
+ </cc:Work
+ >
+ <cc:License
+ rdf:about="http://creativecommons.org/licenses/publicdomain/"
+ >
+ <cc:permits
+ rdf:resource="http://creativecommons.org/ns#Reproduction"
+ />
+ <cc:permits
+ rdf:resource="http://creativecommons.org/ns#Distribution"
+ />
+ <cc:permits
+ rdf:resource="http://creativecommons.org/ns#DerivativeWorks"
+ />
+ </cc:License
+ >
+ </rdf:RDF
+ >
+ </metadata
+ >
+</svg
+>
diff --git a/apps/user_ldap/js/experiencedAdmin.js b/apps/user_ldap/js/experiencedAdmin.js
deleted file mode 100644
index 7dc5a4e503d..00000000000
--- a/apps/user_ldap/js/experiencedAdmin.js
+++ /dev/null
@@ -1,92 +0,0 @@
-/**
- * Copyright (c) 2014, Arthur Schiwon <blizzz@owncloud.com>
- * This file is licensed under the Affero General Public License version 3 or later.
- * See the COPYING-README file.
- */
-
-/* global LdapWizard */
-
-/**
- * controls behaviour depend on whether the admin is experienced in LDAP or not.
- *
- * @class
- * @param {object} wizard the LDAP Wizard object
- * @param {boolean} initialState whether the admin is experienced or not
- */
-function ExperiencedAdmin(wizard, initialState) {
- this.wizard = wizard;
- this._isExperienced = initialState;
- if(this._isExperienced) {
- this.hideEntryCounters();
- }
-}
-
-
-/**
- * toggles whether the admin is an experienced one or not
- *
- * @param {boolean} isExperienced whether the admin is experienced or not
- */
-ExperiencedAdmin.prototype.setExperienced = function(isExperienced) {
- this._isExperienced = isExperienced;
- if(this._isExperienced) {
- this.enableRawMode();
- this.hideEntryCounters();
- } else {
- this.showEntryCounters();
- }
-};
-
-/**
-* answers whether the admin is an experienced one or not
-*
-* @return {boolean} whether the admin is experienced or not
-*/
-ExperiencedAdmin.prototype.isExperienced = function() {
- return this._isExperienced;
-};
-
-/**
- * switches all LDAP filters from Assisted to Raw mode.
- */
-ExperiencedAdmin.prototype.enableRawMode = function() {
- LdapWizard._save({id: 'ldapGroupFilterMode'}, LdapWizard.filterModeRaw);
- LdapWizard._save({id: 'ldapUserFilterMode' }, LdapWizard.filterModeRaw);
- LdapWizard._save({id: 'ldapLoginFilterMode'}, LdapWizard.filterModeRaw);
-};
-
-ExperiencedAdmin.prototype.updateUserTab = function(mode) {
- this._updateTab(mode, $('#ldap_user_count'));
-};
-
-ExperiencedAdmin.prototype.updateGroupTab = function(mode) {
- this._updateTab(mode, $('#ldap_group_count'));
-};
-
-ExperiencedAdmin.prototype._updateTab = function(mode, $countEl) {
- if(mode === LdapWizard.filterModeAssisted) {
- $countEl.removeClass('hidden');
- } else if(!this._isExperienced) {
- $countEl.removeClass('hidden');
- } else {
- $countEl.addClass('hidden');
- }
-};
-
-/**
- * hide user and group counters, they will be displayed on demand only
- */
-ExperiencedAdmin.prototype.hideEntryCounters = function() {
- $('#ldap_user_count').addClass('hidden');
- $('#ldap_group_count').addClass('hidden');
- $('.ldapGetEntryCount').removeClass('hidden');
-};
-
-/**
-* shows user and group counters, they will be displayed on demand only
-*/
-ExperiencedAdmin.prototype.showEntryCounters = function() {
- $('#ldap_user_count').removeClass('hidden');
- $('#ldap_group_count').removeClass('hidden');
- $('.ldapGetEntryCount').addClass('hidden');
-};
diff --git a/apps/user_ldap/js/ldapFilter.js b/apps/user_ldap/js/ldapFilter.js
deleted file mode 100644
index dc65858217d..00000000000
--- a/apps/user_ldap/js/ldapFilter.js
+++ /dev/null
@@ -1,193 +0,0 @@
-/* global LdapWizard */
-
-function LdapFilter(target, determineModeCallback) {
- this.locked = true;
- this.target = false;
- this.mode = LdapWizard.filterModeAssisted;
- this.lazyRunCompose = false;
- this.determineModeCallback = determineModeCallback;
- this.foundFeatures = false;
- this.activated = false;
- this.countPending = false;
-
- if( target === 'User' ||
- target === 'Login' ||
- target === 'Group') {
- this.target = target;
- }
-}
-
-LdapFilter.prototype.activate = function() {
- if(this.activated) {
- // might be necessary, if configuration changes happened.
- this.findFeatures();
- return;
- }
- this.activated = true;
-
- this.determineMode();
-};
-
-LdapFilter.prototype.compose = function(updateCount) {
- var action;
-
- if(updateCount === true) {
- this.countPending = updateCount;
- }
-
- if(this.locked) {
- this.lazyRunCompose = true;
- return false;
- }
-
- if(this.mode === LdapWizard.filterModeRaw) {
- //Raw filter editing, i.e. user defined filter, don't compose
- return;
- }
-
- if(this.target === 'User') {
- action = 'getUserListFilter';
- } else if(this.target === 'Login') {
- action = 'getUserLoginFilter';
- } else if(this.target === 'Group') {
- action = 'getGroupFilter';
- }
-
- var param = 'action='+action+
- '&ldap_serverconfig_chooser='+
- encodeURIComponent($('#ldap_serverconfig_chooser').val());
-
- var filter = this;
-
- LdapWizard.ajax(param,
- function(result) {
- filter.afterComposeSuccess(result);
- },
- function () {
- filter.countPending = false;
- console.log('LDAP Wizard: could not compose filter. '+
- 'Please check owncloud.log');
- }
- );
-};
-
-/**
- * this function is triggered after LDAP filters have been composed successfully
- * @param {object} result returned by the ajax call
- */
-LdapFilter.prototype.afterComposeSuccess = function(result) {
- LdapWizard.applyChanges(result);
- if(this.countPending) {
- this.countPending = false;
- this.updateCount();
- }
-};
-
-LdapFilter.prototype.determineMode = function() {
- var param = 'action=get'+encodeURIComponent(this.target)+'FilterMode'+
- '&ldap_serverconfig_chooser='+
- encodeURIComponent($('#ldap_serverconfig_chooser').val());
-
- var filter = this;
- LdapWizard.ajax(param,
- function(result) {
- var property = 'ldap' + filter.target + 'FilterMode';
- filter.mode = parseInt(result.changes[property], 10);
- var rawContainerIsInvisible =
- $('#raw'+filter.target+'FilterContainer').hasClass('invisible');
- if ( filter.mode === LdapWizard.filterModeRaw
- && rawContainerIsInvisible
- ) {
- LdapWizard['toggleRaw'+filter.target+'Filter']();
- } else if ( filter.mode === LdapWizard.filterModeAssisted
- && !rawContainerIsInvisible
- ) {
- LdapWizard['toggleRaw'+filter.target+'Filter']();
- } else {
- console.log('LDAP Wizard determineMode: returned mode was »' +
- filter.mode + '« of type ' + typeof filter.mode);
- }
- filter.unlock();
- filter.determineModeCallback(filter.mode);
- },
- function () {
- //on error case get back to default i.e. Assisted
- if(!$('#raw'+filter.target+'FilterContainer').hasClass('invisible')) {
- LdapWizard['toggleRaw'+filter.target+'Filter']();
- filter.mode = LdapWizard.filterModeAssisted;
- }
- filter.unlock();
- filter.determineModeCallback(filter.mode);
- }
- );
-};
-
-LdapFilter.prototype.setMode = function(mode) {
- if(mode === LdapWizard.filterModeAssisted || mode === LdapWizard.filterModeRaw) {
- this.mode = mode;
- }
-};
-
-LdapFilter.prototype.getMode = function() {
- return this.mode;
-};
-
-LdapFilter.prototype.unlock = function() {
- this.locked = false;
- if(this.lazyRunCompose) {
- this.lazyRunCompose = false;
- this.compose();
- }
-};
-
-/**
- * resets this.foundFeatures so that LDAP queries can be fired again to retrieve
- * objectClasses, groups, etc.
- */
-LdapFilter.prototype.reAllowFeatureLookup = function () {
- this.foundFeatures = false;
-};
-
-LdapFilter.prototype.findFeatures = function() {
- if(!this.foundFeatures && !this.locked && this.mode === LdapWizard.filterModeAssisted) {
- this.foundFeatures = true;
- var objcEl, avgrEl;
- if(this.target === 'User') {
- objcEl = 'ldap_userfilter_objectclass';
- avgrEl = 'ldap_userfilter_groups';
- } else if (this.target === 'Group') {
- objcEl = 'ldap_groupfilter_objectclass';
- avgrEl = 'ldap_groupfilter_groups';
- } else if (this.target === 'Login') {
- LdapWizard.findAttributes();
- return;
- } else {
- return false;
- }
- LdapWizard.findObjectClasses(objcEl, this.target);
- LdapWizard.findAvailableGroups(avgrEl, this.target + "s");
- }
-};
-
-/**
- * this function is triggered before user and group counts are executed
- * resolving the passed status variable will fire up counting
- */
-LdapFilter.prototype.beforeUpdateCount = function() {
- var status = $.Deferred();
- LdapWizard.runDetectors(this.target, function() {
- status.resolve();
- });
- return status;
-};
-
-LdapFilter.prototype.updateCount = function(doneCallback) {
- var filter = this;
- $.when(this.beforeUpdateCount()).done(function() {
- if(filter.target === 'User') {
- LdapWizard.countUsers(doneCallback);
- } else if (filter.target === 'Group') {
- LdapWizard.countGroups(doneCallback);
- }
- });
-};
diff --git a/apps/user_ldap/js/settings.js b/apps/user_ldap/js/settings.js
deleted file mode 100644
index 768d62a18d1..00000000000
--- a/apps/user_ldap/js/settings.js
+++ /dev/null
@@ -1,1205 +0,0 @@
-var LdapConfiguration = {
- refreshConfig: function() {
- if($('#ldap_serverconfig_chooser option').length < 2) {
- LdapConfiguration.addConfiguration(true);
- return;
- }
- $.post(
- OC.filePath('user_ldap','ajax','getConfiguration.php'),
- $('#ldap_serverconfig_chooser').serialize(),
- function (result) {
- if(result.status === 'success') {
- $.each(result.configuration, function(configkey, configvalue) {
- elementID = '#'+configkey;
-
- //deal with Checkboxes
- if($(elementID).is('input[type=checkbox]')) {
- if(parseInt(configvalue, 10) === 1) {
- $(elementID).attr('checked', 'checked');
- } else {
- $(elementID).removeAttr('checked');
- }
- return;
- }
-
- //On Textareas, Multi-Line Settings come as array
- if($(elementID).is('textarea') && $.isArray(configvalue)) {
- configvalue = configvalue.join("\n");
- }
-
- // assign the value
- $('#'+configkey).val(configvalue);
- });
- LdapWizard.init();
- }
- }
- );
- },
-
- resetDefaults: function() {
- $('#ldap').find('input[type=text], input[type=number], input[type=password], textarea, select').each(function() {
- if($(this).attr('id') === 'ldap_serverconfig_chooser') {
- return;
- }
- $(this).val($(this).attr('data-default'));
- });
- $('#ldap').find('input[type=checkbox]').each(function() {
- if($(this).attr('data-default') === 1) {
- $(this).attr('checked', 'checked');
- } else {
- $(this).removeAttr('checked');
- }
- });
- },
-
- deleteConfiguration: function() {
- $.post(
- OC.filePath('user_ldap','ajax','deleteConfiguration.php'),
- $('#ldap_serverconfig_chooser').serialize(),
- function (result) {
- if(result.status === 'success') {
- $('#ldap_serverconfig_chooser option:selected').remove();
- $('#ldap_serverconfig_chooser option:first').select();
- LdapConfiguration.refreshConfig();
- } else {
- OC.dialogs.alert(
- result.message,
- t('user_ldap', 'Deletion failed')
- );
- }
- }
- );
- },
-
- addConfiguration: function(doNotAsk) {
- $.post(
- OC.filePath('user_ldap','ajax','getNewServerConfigPrefix.php'),
- function (result) {
- if(result.status === 'success') {
- if(doNotAsk) {
- LdapConfiguration.resetDefaults();
- } else {
- OC.dialogs.confirm(
- t('user_ldap', 'Take over settings from recent server configuration?'),
- t('user_ldap', 'Keep settings?'),
- function(keep) {
- if(!keep) {
- LdapConfiguration.resetDefaults();
- }
- }
- );
- }
- $('#ldap_serverconfig_chooser option:selected').removeAttr('selected');
- var html = '<option value="'+result.configPrefix+'" selected="selected">'+t('user_ldap','{nthServer}. Server', {nthServer: $('#ldap_serverconfig_chooser option').length})+'</option>';
- $('#ldap_serverconfig_chooser option:last').before(html);
- LdapWizard.init();
- } else {
- OC.dialogs.alert(
- result.message,
- t('user_ldap', 'Cannot add server configuration')
- );
- }
- }
- );
- },
-
- testConfiguration: function(onSuccess, onError) {
- $.post(
- OC.filePath('user_ldap','ajax','testConfiguration.php'),
- $('#ldap').serialize(),
- function (result) {
- if (result.status === 'success') {
- onSuccess(result);
- } else {
- onError(result);
- }
- }
- );
- },
-
- clearMappings: function(mappingSubject) {
- $.post(
- OC.filePath('user_ldap','ajax','clearMappings.php'),
- 'ldap_clear_mapping='+encodeURIComponent(mappingSubject),
- function(result) {
- if(result.status === 'success') {
- OC.dialogs.info(
- t('user_ldap', 'mappings cleared'),
- t('user_ldap', 'Success')
- );
- } else {
- OC.dialogs.alert(
- result.message,
- t('user_ldap', 'Error')
- );
- }
- }
- );
- }
-};
-
-var LdapWizard = {
- checkPortInfoShown: false,
- saveBlacklist: {},
- userFilterGroupSelectState: 'enable',
- spinner: '<img class="wizSpinner" src="'+ OC.imagePath('core', 'loading.gif') +'">',
- filterModeAssisted: 0,
- filterModeRaw: 1,
- userFilter: false,
- loginFilter: false,
- groupFilter: false,
- ajaxRequests: {},
- lastTestSuccessful: true,
-
- ajax: function(param, fnOnSuccess, fnOnError, reqID) {
- if(!_.isUndefined(reqID)) {
- if(LdapWizard.ajaxRequests.hasOwnProperty(reqID)) {
- console.log('aborting ' + reqID);
- console.log(param);
- LdapWizard.ajaxRequests[reqID].abort();
- }
- }
- var request = $.post(
- OC.filePath('user_ldap','ajax','wizard.php'),
- param,
- function(result) {
- if(result.status === 'success') {
- fnOnSuccess(result);
- } else {
- fnOnError(result);
- }
- }
- );
- if(!_.isUndefined(reqID)) {
- LdapWizard.ajaxRequests[reqID] = request;
- }
- return request;
- },
-
- applyChanges: function (result) {
- for (var id in result.changes) {
- LdapWizard.blacklistAdd(id);
- if(id.indexOf('count') > 0) {
- $('#'+id).text(result.changes[id]);
- } else {
- $('#'+id).val(result.changes[id]);
- }
- }
- LdapWizard.functionalityCheck();
-
- if($('#ldapSettings').tabs('option', 'active') == 0) {
- LdapWizard.basicStatusCheck();
- }
- },
-
- enableTabs: function() {
- //do not use this function directly, use basicStatusCheck instead.
- if(LdapWizard.saveProcesses === 0) {
- $('.ldap_action_continue').removeAttr('disabled');
- $('.ldap_action_back').removeAttr('disabled');
- $('#ldapSettings').tabs('option', 'disabled', []);
- }
- },
-
- disableTabs: function() {
- $('.ldap_action_continue').attr('disabled', 'disabled');
- $('.ldap_action_back').attr('disabled', 'disabled');
- $('#ldapSettings').tabs('option', 'disabled', [1, 2, 3, 4, 5]);
- },
-
- basicStatusCheck: function() {
- //criteria to continue from the first tab
- // - host, port, user filter, agent dn, password, base dn
- var host = $('#ldap_host').val();
- var port = $('#ldap_port').val();
- var agent = $('#ldap_dn').val();
- var pwd = $('#ldap_agent_password').val();
- var base = $('#ldap_base').val();
-
- if((host && port && base) && ((!agent && !pwd) || (agent && pwd))) {
- LdapWizard.enableTabs();
- } else {
- LdapWizard.disableTabs();
- }
- },
-
-
- blacklistAdd: function(id) {
- var obj = $('#' + id);
- if(!(obj[0].hasOwnProperty('multiple') && obj[0]['multiple'] === true)) {
- //no need to blacklist multiselect
- LdapWizard.saveBlacklist[id] = true;
- return true;
- }
- return false;
- },
-
- blacklistRemove: function(id) {
- if(LdapWizard.saveBlacklist.hasOwnProperty(id)) {
- delete LdapWizard.saveBlacklist[id];
- return true;
- }
- return false;
- },
-
- checkBaseDN: function() {
- var host = $('#ldap_host').val();
- var port = $('#ldap_port').val();
- var user = $('#ldap_dn').val();
- var pass = $('#ldap_agent_password').val();
-
- //FIXME: determine base dn with anonymous access
- if(host && port && user && pass) {
- var param = 'action=guessBaseDN'+
- '&ldap_serverconfig_chooser='+
- encodeURIComponent($('#ldap_serverconfig_chooser').val());
-
- LdapWizard.showSpinner('#ldap_base');
- $('#ldap_base').prop('disabled', 'disabled');
- LdapWizard.ajax(param,
- function(result) {
- LdapWizard.applyChanges(result);
- LdapWizard.hideSpinner('#ldap_base');
- if($('#ldap_base').val()) {
- LdapWizard.hideInfoBox();
- }
- $('#ldap_base').prop('disabled', false);
- },
- function (result) {
- LdapWizard.hideSpinner('#ldap_base');
- LdapWizard.showInfoBox(t('user_ldap', 'Please specify a Base DN'));
- LdapWizard.showInfoBox(t('user_ldap', 'Could not determine Base DN'));
- $('#ldap_base').prop('disabled', false);
- },
- 'guessBaseDN'
- );
- }
- },
-
- checkPort: function() {
- var host = $('#ldap_host').val();
- var port = $('#ldap_port').val();
-
- if(host && !port) {
- var param = 'action=guessPortAndTLS'+
- '&ldap_serverconfig_chooser='+
- encodeURIComponent($('#ldap_serverconfig_chooser').val());
-
- LdapWizard.showSpinner('#ldap_port');
- $('#ldap_port').prop('disabled', 'disabled');
- LdapWizard.ajax(param,
- function(result) {
- LdapWizard.applyChanges(result);
- LdapWizard.hideSpinner('#ldap_port');
- if($('#ldap_port').val()) {
- LdapWizard.checkBaseDN();
- $('#ldap_port').prop('disabled', false);
- LdapWizard.hideInfoBox();
- }
- },
- function (result) {
- LdapWizard.hideSpinner('#ldap_port');
- $('#ldap_port').prop('disabled', false);
- LdapWizard.showInfoBox(t('user_ldap', 'Please specify the port'));
- },
- 'guessPortAndTLS'
- );
- }
- },
-
- controlBack: function() {
- var curTabIndex = $('#ldapSettings').tabs('option', 'active');
- if(curTabIndex == 0) {
- return;
- }
- $('#ldapSettings').tabs('option', 'active', curTabIndex - 1);
- LdapWizard.controlUpdate(curTabIndex - 1);
- },
-
- controlContinue: function() {
- var curTabIndex = $('#ldapSettings').tabs('option', 'active');
- if(curTabIndex == 3) {
- return;
- }
- $('#ldapSettings').tabs('option', 'active', 1 + curTabIndex);
- LdapWizard.controlUpdate(curTabIndex + 1);
- },
-
- controlUpdate: function(nextTabIndex) {
- if(nextTabIndex == 0) {
- $('.ldap_action_back').addClass('invisible');
- $('.ldap_action_continue').removeClass('invisible');
- } else
- if(nextTabIndex == 1) {
- $('.ldap_action_back').removeClass('invisible');
- $('.ldap_action_continue').removeClass('invisible');
- } else
- if(nextTabIndex == 2) {
- $('.ldap_action_continue').removeClass('invisible');
- $('.ldap_action_back').removeClass('invisible');
- } else
- if(nextTabIndex == 3) {
- //now last tab
- $('.ldap_action_back').removeClass('invisible');
- $('.ldap_action_continue').addClass('invisible');
- }
- },
-
- _countThings: function(method, spinnerID, doneCallback) {
- var param = 'action='+method+
- '&ldap_serverconfig_chooser='+
- encodeURIComponent($('#ldap_serverconfig_chooser').val());
-
- LdapWizard.showSpinner(spinnerID);
- LdapWizard.ajax(param,
- function(result) {
- LdapWizard.applyChanges(result);
- LdapWizard.hideSpinner(spinnerID);
- if(!_.isUndefined(doneCallback)) {
- doneCallback(method);
- }
- },
- function (result) {
- OC.Notification.showTemporary('Counting the entries failed with: ' + result.message);
- LdapWizard.hideSpinner(spinnerID);
- if(!_.isUndefined(doneCallback)) {
- doneCallback(method);
- }
- },
- method
- );
- },
-
- countGroups: function(doneCallback) {
- var groupFilter = $('#ldap_group_filter').val();
- if(!_.isEmpty(groupFilter)) {
- LdapWizard._countThings('countGroups', '#ldap_group_count', doneCallback);
- }
- },
-
- countUsers: function(doneCallback) {
- var userFilter = $('#ldap_userlist_filter').val();
- if(!_.isEmpty(userFilter)) {
- LdapWizard._countThings('countUsers', '#ldap_user_count', doneCallback);
- }
- },
-
- /**
- * called after detectors have run
- * @callback runDetectorsCallback
- */
-
- /**
- * runs detectors to determine appropriate attributes, e.g. displayName
- * @param {string} type either "User" or "Group"
- * @param {runDetectorsCallback} triggered after all detectors have completed
- */
- runDetectors: function(type, callback) {
- if(type === 'Group') {
- $.when(LdapWizard.detectGroupMemberAssoc())
- .then(callback, callback);
- if( LdapWizard.admin.isExperienced
- && !(LdapWizard.detectorsRunInXPMode & LdapWizard.groupDetectors)) {
- LdapWizard.detectorsRunInXPMode += LdapWizard.groupDetectors;
- }
- } else if(type === 'User') {
- var req1 = LdapWizard.detectUserDisplayNameAttribute();
- var req2 = LdapWizard.detectEmailAttribute();
- $.when(req1, req2)
- .then(callback, callback);
- if( LdapWizard.admin.isExperienced
- && !(LdapWizard.detectorsRunInXPMode & LdapWizard.userDetectors)) {
- LdapWizard.detectorsRunInXPMode += LdapWizard.userDetectors;
- }
- }
- },
-
- /**
- * runs detector to find out a fitting user display name attribute
- */
- detectUserDisplayNameAttribute: function() {
- var param = 'action=detectUserDisplayNameAttribute' +
- '&ldap_serverconfig_chooser='+
- encodeURIComponent($('#ldap_serverconfig_chooser').val());
-
- //runs in the background, no callbacks necessary
- return LdapWizard.ajax(param, LdapWizard.applyChanges, function(){}, 'detectUserDisplayNameAttribute');
- },
-
- detectEmailAttribute: function() {
- var param = 'action=detectEmailAttribute'+
- '&ldap_serverconfig_chooser='+
- encodeURIComponent($('#ldap_serverconfig_chooser').val());
- //runs in the background, no callbacks necessary
- return LdapWizard.ajax(param, LdapWizard.applyChanges, function(){}, 'detectEmailAttribute');
- },
-
- detectGroupMemberAssoc: function() {
- param = 'action=determineGroupMemberAssoc'+
- '&ldap_serverconfig_chooser='+
- encodeURIComponent($('#ldap_serverconfig_chooser').val());
-
- return LdapWizard.ajax(param,
- function(result) {
- //pure background story
- },
- function (result) {
- // error handling
- },
- 'determineGroupMemberAssoc'
- );
- },
-
- findAttributes: function() {
- param = 'action=determineAttributes'+
- '&ldap_serverconfig_chooser='+
- encodeURIComponent($('#ldap_serverconfig_chooser').val());
-
- LdapWizard.showSpinner('#ldap_loginfilter_attributes');
- LdapWizard.ajax(param,
- function(result) {
- $('#ldap_loginfilter_attributes').find('option').remove();
- for (var i in result.options['ldap_loginfilter_attributes']) {
- //FIXME: move HTML into template
- var attr = result.options['ldap_loginfilter_attributes'][i];
- $('#ldap_loginfilter_attributes').append(
- "<option value='"+attr+"'>"+attr+"</option>");
- }
- LdapWizard.hideSpinner('#ldap_loginfilter_attributes');
- LdapWizard.applyChanges(result);
- $('#ldap_loginfilter_attributes').multiselect('refresh');
- if($('#rawLoginFilterContainer').hasClass('invisible')) {
- $('#ldap_loginfilter_attributes').multiselect('enable');
- }
- LdapWizard.postInitLoginFilter();
- },
- function (result) {
- //deactivate if no attributes found
- $('#ldap_loginfilter_attributes').multiselect(
- {noneSelectedText : 'No attributes found'});
- $('#ldap_loginfilter_attributes').multiselect('disable');
- LdapWizard.hideSpinner('#ldap_loginfilter_attributes');
- },
- 'determineAttributes'
- );
- },
-
- findAvailableGroups: function(multisel, type) {
- if(type !== 'Users' && type !== 'Groups') {
- return false;
- }
- param = 'action=determineGroupsFor'+encodeURIComponent(type)+
- '&ldap_serverconfig_chooser='+
- encodeURIComponent($('#ldap_serverconfig_chooser').val());
-
- LdapWizard.showSpinner('#'+multisel);
- LdapWizard.ajax(param,
- function(result) {
- $('#'+multisel).find('option').remove();
- for (var i in result.options[multisel]) {
- //FIXME: move HTML into template
- objc = result.options[multisel][i];
- $('#'+multisel).append("<option value='"+objc+"'>"+objc+"</option>");
- }
- LdapWizard.hideSpinner('#'+multisel);
- LdapWizard.applyChanges(result);
- $('#'+multisel).multiselect('refresh');
- part = type.slice(0, -1);
- if($('#raw' + part + 'FilterContainer').hasClass('invisible')) {
- //enable only when raw filter editing is not turned on
- $('#'+multisel).multiselect('enable');
- }
- if(type === 'Users') {
- //required for initial save
- filter = $('#ldap_userlist_filter').val();
- if(!filter) {
- LdapWizard.saveMultiSelect(multisel,
- $('#'+multisel).multiselect("getChecked"));
- }
- LdapWizard.userFilterAvailableGroupsHasRun = true;
- LdapWizard.postInitUserFilter();
- }
- },
- function (result) {
- LdapWizard.hideSpinner('#'+multisel);
- $('#'+multisel).multiselect('disable');
- if(type === 'Users') {
- LdapWizard.userFilterAvailableGroupsHasRun = true;
- LdapWizard.postInitUserFilter();
- }
- },
- 'findAvailableGroupsFor' + type
- );
- },
-
- findObjectClasses: function(multisel, type) {
- if(type !== 'User' && type !== 'Group') {
- return false;
- }
- var param = 'action=determine'+encodeURIComponent(type)+'ObjectClasses'+
- '&ldap_serverconfig_chooser='+
- encodeURIComponent($('#ldap_serverconfig_chooser').val());
-
- LdapWizard.showSpinner('#'+multisel);
- LdapWizard.ajax(param,
- function(result) {
- $('#'+multisel).find('option').remove();
- for (var i in result.options[multisel]) {
- //FIXME: move HTML into template
- objc = result.options[multisel][i];
- $('#'+multisel).append("<option value='"+objc+"'>"+objc+"</option>");
- }
- LdapWizard.hideSpinner('#'+multisel);
- LdapWizard.applyChanges(result);
- $('#'+multisel).multiselect('refresh');
- if(type === 'User') {
- //required for initial save
- filter = $('#ldap_userlist_filter').val();
- if(!filter) {
- LdapWizard.saveMultiSelect(multisel,
- $('#'+multisel).multiselect("getChecked"));
- }
- LdapWizard.userFilterObjectClassesHasRun = true;
- LdapWizard.postInitUserFilter();
- }
- },
- function (result) {
- LdapWizard.hideSpinner('#'+multisel);
- if(type === 'User') {
- LdapWizard.userFilterObjectClassesHasRun = true;
- LdapWizard.postInitUserFilter();
- }
- //TODO: error handling
- },
- 'determine' + type + 'ObjectClasses'
- );
- },
-
- functionalityCheck: function() {
- //criteria to enable the connection:
- // - host, port, basedn, user filter, login filter
- var host = $('#ldap_host').val();
- var port = $('#ldap_port').val();
- var base = $('#ldap_base').val();
- var userfilter = $('#ldap_userlist_filter').val();
- var loginfilter = $('#ldap_login_filter').val();
-
- //FIXME: activates a manually deactivated configuration.
- if(host && port && base && userfilter && loginfilter) {
- LdapWizard.updateStatusIndicator(true);
- if($('#ldap_configuration_active').is(':checked')) {
- return;
- }
- if(!LdapWizard.isConfigurationActiveControlLocked) {
- //avoids a manually deactivated connection will be activated
- //upon opening the admin page
- $('#ldap_configuration_active').prop('checked', true);
- LdapWizard.save($('#ldap_configuration_active')[0]);
- }
- } else {
- if($('#ldap_configuration_active').is(':checked')) {
- $('#ldap_configuration_active').prop('checked', false);
- LdapWizard.save($('#ldap_configuration_active')[0]);
- }
- LdapWizard.updateStatusIndicator(false);
- }
- },
-
- hideInfoBox: function() {
- if(LdapWizard.checkInfoShown) {
- $('#ldapWizard1 .ldapWizardInfo').addClass('invisible');
- LdapWizard.checkInfoShown = false;
- }
- },
-
- hideSpinner: function(id) {
- $(id+' + .wizSpinner').remove();
- $(id + " + button").css('display', 'inline');
- },
-
- isConfigurationActiveControlLocked: true,
- detectorsRunInXPMode: 0,
- userDetectors: 1,
- groupDetectors: 2,
-
- init: function() {
- LdapWizard.detectorsRunInXPMode = 0;
- LdapWizard.instantiateFilters();
- LdapWizard.admin.setExperienced($('#ldap_experienced_admin').is(':checked'));
- LdapWizard.lastTestSuccessful = true;
- LdapWizard.basicStatusCheck();
- LdapWizard.functionalityCheck();
- LdapWizard.isConfigurationActiveControlLocked = false;
- },
-
- initGroupFilter: function() {
- LdapWizard.groupFilter.activate();
- },
-
- /** init login filter tab section **/
-
- initLoginFilter: function() {
- LdapWizard.loginFilter.activate();
- },
-
- postInitLoginFilter: function() {
- if($('#rawLoginFilterContainer').hasClass('invisible')) {
- LdapWizard.loginFilter.compose();
- }
- },
-
- /** end of init user filter tab section **/
-
- initMultiSelect: function(object, id, caption) {
- object.multiselect({
- header: false,
- selectedList: 9,
- noneSelectedText: caption,
- click: function(event, ui) {
- LdapWizard.saveMultiSelect(id,
- $('#'+id).multiselect("getChecked"));
- }
- });
- },
-
- hideTestSpinner:function (countMethod) {
- var selector;
- if(countMethod === 'countUsers') {
- selector = '#rawUserFilterContainer .ldapGetEntryCount';
- } else {
- selector = '#rawGroupFilterContainer .ldapGetEntryCount';
- }
- LdapWizard.hideSpinner(selector);
- },
-
- /** init user filter tab section **/
-
- instantiateFilters: function() {
- delete LdapWizard.userFilter;
- LdapWizard.userFilter = new LdapFilter('User', function(mode) {
- if( !LdapWizard.admin.isExperienced()
- || mode === LdapWizard.filterModeAssisted) {
- LdapWizard.userFilter.updateCount();
- }
- LdapWizard.userFilter.findFeatures();
- });
- $('#rawUserFilterContainer .ldapGetEntryCount').click(function(event) {
- event.preventDefault();
- $('#ldap_user_count').text('');
- LdapWizard.showSpinner('#rawUserFilterContainer .ldapGetEntryCount');
- LdapWizard.userFilter.updateCount(LdapWizard.hideTestSpinner);
- $('#ldap_user_count').removeClass('hidden');
- });
-
- delete LdapWizard.loginFilter;
- LdapWizard.loginFilter = new LdapFilter('Login', function(mode) {
- LdapWizard.loginFilter.findFeatures();
- });
-
- delete LdapWizard.groupFilter;
- LdapWizard.groupFilter = new LdapFilter('Group', function(mode) {
- if( !LdapWizard.admin.isExperienced()
- || mode === LdapWizard.filterModeAssisted) {
- LdapWizard.groupFilter.updateCount();
- }
- LdapWizard.groupFilter.findFeatures();
- });
- $('#rawGroupFilterContainer .ldapGetEntryCount').click(function(event) {
- event.preventDefault();
- $('#ldap_group_count').text('');
- LdapWizard.showSpinner('#rawGroupFilterContainer .ldapGetEntryCount');
- LdapWizard.groupFilter.updateCount(LdapWizard.hideTestSpinner);
- $('#ldap_group_count').removeClass('hidden');
- });
- },
-
- userFilterObjectClassesHasRun: false,
- userFilterAvailableGroupsHasRun: false,
-
- initUserFilter: function() {
- LdapWizard.userFilterObjectClassesHasRun = false;
- LdapWizard.userFilterAvailableGroupsHasRun = false;
- LdapWizard.userFilter.activate();
- },
-
- postInitUserFilter: function() {
- if(LdapWizard.userFilterObjectClassesHasRun &&
- LdapWizard.userFilterAvailableGroupsHasRun) {
- LdapWizard.userFilter.compose();
- }
- },
-
- /** end of init user filter tab section **/
-
- onTabChange: function(event, ui) {
- if(LdapWizard.saveProcesses > 0) {
- //do not allow to switch tabs as long as a save process is active
- return false;
- }
- var newTabIndex = 0;
- if(ui.newTab[0].id === '#ldapWizard2') {
- LdapWizard.initUserFilter();
- newTabIndex = 1;
- } else if(ui.newTab[0].id === '#ldapWizard3') {
- LdapWizard.initLoginFilter();
- newTabIndex = 2;
- } else if(ui.newTab[0].id === '#ldapWizard4') {
- LdapWizard.initGroupFilter();
- newTabIndex = 3;
- }
-
- var curTabIndex = $('#ldapSettings').tabs('option', 'active');
- if(curTabIndex >= 0 && curTabIndex <= 3) {
- LdapWizard.controlUpdate(newTabIndex);
- //run detectors in XP mode, when "Test Filter" button has not been
- //clicked in order to make sure that email, displayname, member-
- //group association attributes are properly set.
- if( curTabIndex === 1
- && LdapWizard.admin.isExperienced
- && !(LdapWizard.detecorsRunInXPMode & LdapWizard.userDetectors)
- ) {
- LdapWizard.runDetectors('User', function(){});
- } else if( curTabIndex === 3
- && LdapWizard.admin.isExperienced
- && !(LdapWizard.detecorsRunInXPMode & LdapWizard.groupDetectors)
- ) {
- LdapWizard.runDetectors('Group', function(){});
- }
- }
- },
-
- /**
- * allows UserFilter, LoginFilter and GroupFilter to lookup objectClasses
- * and similar again. This should be called after essential changes, e.g.
- * Host or BaseDN changes, or positive functionality check
- *
- */
- allowFilterFeatureSearch: function () {
- LdapWizard.userFilter.reAllowFeatureLookup();
- LdapWizard.loginFilter.reAllowFeatureLookup();
- LdapWizard.groupFilter.reAllowFeatureLookup();
- },
-
- processChanges: function (triggerObj) {
- LdapWizard.hideInfoBox();
-
- if(triggerObj.id === 'ldap_host'
- || triggerObj.id === 'ldap_port'
- || triggerObj.id === 'ldap_dn'
- || triggerObj.id === 'ldap_agent_password') {
- LdapWizard.checkPort();
- if($('#ldap_port').val()) {
- //if Port is already set, check BaseDN
- LdapWizard.checkBaseDN();
- LdapWizard.allowFilterFeatureSearch();
- }
- }
-
- if(triggerObj.id === 'ldap_loginfilter_username'
- || triggerObj.id === 'ldap_loginfilter_email') {
- LdapWizard.loginFilter.compose();
- } else if (!LdapWizard.admin.isExperienced()) {
- if(triggerObj.id === 'ldap_userlist_filter') {
- LdapWizard.userFilter.updateCount();
- } else if (triggerObj.id === 'ldap_group_filter') {
- LdapWizard.groupFilter.updateCount();
- }
- }
-
- if($('#ldapSettings').tabs('option', 'active') == 0) {
- LdapWizard.basicStatusCheck();
- LdapWizard.functionalityCheck();
- }
- },
-
- save: function(inputObj) {
- if(LdapWizard.blacklistRemove(inputObj.id)) {
- return;
- }
- if($(inputObj).is('input[type=checkbox]')
- && !$(inputObj).is(':checked')) {
- val = 0;
- } else {
- val = $(inputObj).val();
- }
- LdapWizard._save(inputObj, val);
- },
-
- /**
- * updates user or group count on multiSelect close. Resets the event
- * function subsequently.
- *
- * @param {LdapFilter} filter
- * @param {Object} $multiSelectObj
- */
- onMultiSelectClose: function(filter, $multiSelectObj) {
- filter.updateCount();
- $multiSelectObj.multiselect({close: function(){}});
- },
-
- saveMultiSelect: function(originalObj, resultObj) {
- var values = '';
- for(var i = 0; i < resultObj.length; i++) {
- values = values + "\n" + resultObj[i].value;
- }
- LdapWizard._save($('#'+originalObj)[0], $.trim(values));
- var $multiSelectObj = $('#'+originalObj);
- var updateCount = !$multiSelectObj.multiselect("isOpen");
- var applyUpdateOnCloseToFilter;
- if(originalObj === 'ldap_userfilter_objectclass'
- || originalObj === 'ldap_userfilter_groups') {
- LdapWizard.userFilter.compose(updateCount);
- if(!updateCount) {
- applyUpdateOnCloseToFilter = LdapWizard.userFilter;
- }
- //when user filter is changed afterwards, login filter needs to
- //be adjusted, too
- if(!LdapWizard.loginFilter) {
- LdapWizard.initLoginFilter();
- }
- LdapWizard.loginFilter.compose();
- } else if(originalObj === 'ldap_loginfilter_attributes') {
- LdapWizard.loginFilter.compose();
- } else if(originalObj === 'ldap_groupfilter_objectclass'
- || originalObj === 'ldap_groupfilter_groups') {
- LdapWizard.groupFilter.compose(updateCount);
- if(!updateCount) {
- applyUpdateOnCloseToFilter = LdapWizard.groupFilter;
- }
- }
-
- if(applyUpdateOnCloseToFilter instanceof LdapFilter) {
- $multiSelectObj.multiselect({
- close: function () {
- LdapWizard.onMultiSelectClose(
- applyUpdateOnCloseToFilter, $multiSelectObj);
- }
- });
- }
- },
-
- saveProcesses: 0,
- _save: function(object, value) {
- $('#ldap .ldap_saving').removeClass('hidden');
- LdapWizard.saveProcesses += 1;
- $('#ldap *').addClass('save-cursor');
- param = 'cfgkey='+encodeURIComponent(object.id)+
- '&cfgval='+encodeURIComponent(value)+
- '&action=save'+
- '&ldap_serverconfig_chooser='+$('#ldap_serverconfig_chooser').val();
-
- $.post(
- OC.filePath('user_ldap','ajax','wizard.php'),
- param,
- function(result) {
- LdapWizard.saveProcesses -= 1;
- if(LdapWizard.saveProcesses === 0) {
- $('#ldap .ldap_saving').addClass('hidden');
- $('#ldap *').removeClass('save-cursor');
- }
- if(result.status === 'success') {
- LdapWizard.processChanges(object);
- } else {
- console.log('Could not save value for ' + object.id);
- }
- }
- );
- },
-
- showInfoBox: function(text) {
- $('#ldapWizard1 .ldapWizardInfo').text(text);
- $('#ldapWizard1 .ldapWizardInfo').removeClass('invisible');
- LdapWizard.checkInfoShown = true;
- },
-
- showSpinner: function(id) {
- if($(id + ' + .wizSpinner').length == 0) {
- $(LdapWizard.spinner).insertAfter($(id));
- $(id + " + img + button").css('display', 'none');
- }
- },
-
- toggleRawFilter: function(container, moc, mg, stateVar, modeKey) {
- var isUser = moc.indexOf('user') >= 0;
- var filter = isUser ? LdapWizard.userFilter : LdapWizard.groupFilter;
- //moc = multiselect objectclass
- //mg = mutliselect groups
- if($(container).hasClass('invisible')) {
- filter.setMode(LdapWizard.filterModeRaw);
- $(container).removeClass('invisible');
- $(moc).multiselect('disable');
- if($(mg).multiselect().attr('disabled') === 'disabled') {
- LdapWizard[stateVar] = 'disable';
- } else {
- LdapWizard[stateVar] = 'enable';
- }
- $(mg).multiselect('disable');
- LdapWizard._save({ id: modeKey }, LdapWizard.filterModeRaw);
- } else {
- filter.setMode(LdapWizard.filterModeAssisted);
- filter.findFeatures();
- $(container).addClass('invisible');
- $(mg).multiselect(LdapWizard[stateVar]);
- $(moc).multiselect('enable');
- LdapWizard._save({ id: modeKey }, LdapWizard.filterModeAssisted);
- if(isUser) {
- LdapWizard.blacklistRemove('ldap_userlist_filter');
- LdapWizard.userFilter.compose(true);
- } else {
- LdapWizard.blacklistRemove('ldap_group_filter');
- LdapWizard.groupFilter.compose(true);
- }
- }
- },
-
- onToggleRawFilterConfirmation: function(currentMode, isRawVisible, callback) {
- if( !LdapWizard.admin.isExperienced()
- || currentMode === LdapWizard.filterModeAssisted
- || (LdapWizard.admin.isExperienced() && !isRawVisible)
- ) {
- return callback(true);
- }
-
- var confirmed = OCdialogs.confirm(
- 'Switching the mode will enable automatic LDAP queries. Depending on your LDAP size they may take a while. Do you still want to switch the mode?',
- 'Mode switch',
- callback
- );
- },
-
- toggleRawGroupFilter: function() {
- LdapWizard.onToggleRawFilterConfirmation(
- LdapWizard.groupFilter.getMode(),
- !$('#rawGroupFilterContainer').hasClass('invisible'),
- function(confirmed) {
- if(confirmed !== true) {
- return;
- }
-
- LdapWizard.blacklistRemove('ldap_group_filter');
- LdapWizard.toggleRawFilter('#rawGroupFilterContainer',
- '#ldap_groupfilter_objectclass',
- '#ldap_groupfilter_groups',
- 'groupFilterGroupSelectState',
- 'ldapGroupFilterMode'
- );
- LdapWizard.admin.updateGroupTab(LdapWizard.groupFilter.getMode());
- }
- );
- },
-
- toggleRawLoginFilter: function() {
- LdapWizard.onToggleRawFilterConfirmation(
- LdapWizard.loginFilter.getMode(),
- !$('#rawLoginFilterContainer').hasClass('invisible'),
- function(confirmed) {
- if(confirmed !== true) {
- return;
- }
-
- LdapWizard.blacklistRemove('ldap_login_filter');
- container = '#rawLoginFilterContainer';
- if($(container).hasClass('invisible')) {
- $(container).removeClass('invisible');
- action = 'disable';
- property = 'disabled';
- mode = LdapWizard.filterModeRaw;
- } else {
- $(container).addClass('invisible');
- action = 'enable';
- property = false;
- mode = LdapWizard.filterModeAssisted;
- }
- LdapWizard.loginFilter.setMode(mode);
- LdapWizard.loginFilter.findFeatures();
- $('#ldap_loginfilter_attributes').multiselect(action);
- $('#ldap_loginfilter_email').prop('disabled', property);
- $('#ldap_loginfilter_username').prop('disabled', property);
- LdapWizard._save({ id: 'ldapLoginFilterMode' }, mode);
- if(action === 'enable') {
- LdapWizard.loginFilter.compose();
- }
- }
- );
- },
-
- toggleRawUserFilter: function() {
- LdapWizard.onToggleRawFilterConfirmation(
- LdapWizard.userFilter.getMode(),
- !$('#rawUserFilterContainer').hasClass('invisible'),
- function(confirmed) {
- if(confirmed === true) {
- LdapWizard.blacklistRemove('ldap_userlist_filter');
- LdapWizard.toggleRawFilter('#rawUserFilterContainer',
- '#ldap_userfilter_objectclass',
- '#ldap_userfilter_groups',
- 'userFilterGroupSelectState',
- 'ldapUserFilterMode'
- );
- LdapWizard.admin.updateUserTab(LdapWizard.userFilter.getMode());
- }
- }
- );
- },
-
- updateStatusIndicator: function(isComplete) {
- if(isComplete) {
- LdapConfiguration.testConfiguration(
- //onSuccess
- function(result) {
- $('.ldap_config_state_indicator').text(t('user_ldap',
- 'Configuration OK'
- ));
- $('.ldap_config_state_indicator').addClass('ldap_grey');
- $('.ldap_config_state_indicator_sign').removeClass('error');
- $('.ldap_config_state_indicator_sign').addClass('success');
- if(!LdapWizard.lastTestSuccessful) {
- LdapWizard.lastTestSuccessful = true;
- LdapWizard.allowFilterFeatureSearch();
- }
- },
- //onError
- function(result) {
- $('.ldap_config_state_indicator').text(t('user_ldap',
- 'Configuration incorrect'
- ));
- $('.ldap_config_state_indicator').removeClass('ldap_grey');
- $('.ldap_config_state_indicator_sign').addClass('error');
- $('.ldap_config_state_indicator_sign').removeClass('success');
- LdapWizard.lastTestSuccessful = false;
- }
- );
- } else {
- $('.ldap_config_state_indicator').text(t('user_ldap',
- 'Configuration incomplete'
- ));
- $('.ldap_config_state_indicator').removeClass('ldap_grey');
- $('.ldap_config_state_indicator_sign').removeClass('error');
- $('.ldap_config_state_indicator_sign').removeClass('success');
- }
- }
-};
-
-$(document).ready(function() {
- $('#ldapAdvancedAccordion').accordion({ heightStyle: 'content', animate: 'easeInOutCirc'});
- $('#ldapSettings').tabs({ beforeActivate: LdapWizard.onTabChange });
- $('.ldap_submit').button();
- $('.ldap_action_test_connection').button();
- $('#ldap_action_delete_configuration').button();
- LdapWizard.initMultiSelect($('#ldap_userfilter_groups'),
- 'ldap_userfilter_groups',
- t('user_ldap', 'Select groups'));
- LdapWizard.initMultiSelect($('#ldap_userfilter_objectclass'),
- 'ldap_userfilter_objectclass',
- t('user_ldap', 'Select object classes'));
- LdapWizard.initMultiSelect($('#ldap_loginfilter_attributes'),
- 'ldap_loginfilter_attributes',
- t('user_ldap', 'Select attributes'));
- LdapWizard.initMultiSelect($('#ldap_groupfilter_groups'),
- 'ldap_groupfilter_groups',
- t('user_ldap', 'Select groups'));
- LdapWizard.initMultiSelect($('#ldap_groupfilter_objectclass'),
- 'ldap_groupfilter_objectclass',
- t('user_ldap', 'Select object classes'));
-
- $('.lwautosave').change(function() { LdapWizard.save(this); });
- $('#toggleRawUserFilter').click(LdapWizard.toggleRawUserFilter);
- $('#toggleRawGroupFilter').click(LdapWizard.toggleRawGroupFilter);
- $('#toggleRawLoginFilter').click(LdapWizard.toggleRawLoginFilter);
- LdapConfiguration.refreshConfig();
- $('.ldap_action_continue').click(function(event) {
- event.preventDefault();
- LdapWizard.controlContinue();
- });
- $('.ldap_action_back').click(function(event) {
- event.preventDefault();
- LdapWizard.controlBack();
- });
- $('.ldap_action_test_connection').click(function(event){
- event.preventDefault();
- LdapConfiguration.testConfiguration(
- //onSuccess
- function(result) {
- OC.dialogs.alert(
- result.message,
- t('user_ldap', 'Connection test succeeded')
- );
- },
- //onError
- function(result) {
- OC.dialogs.alert(
- result.message,
- t('user_ldap', 'Connection test failed')
- );
- }
- );
- });
-
- $('#ldap_action_delete_configuration').click(function(event) {
- event.preventDefault();
- OC.dialogs.confirm(
- t('user_ldap', 'Do you really want to delete the current Server Configuration?'),
- t('user_ldap', 'Confirm Deletion'),
- function(deleteConfiguration) {
- if(deleteConfiguration) {
- LdapConfiguration.deleteConfiguration();
- }
- }
- );
- });
-
- $('.ldap_submit').click(function(event) {
- event.preventDefault();
- $.post(
- OC.filePath('user_ldap','ajax','setConfiguration.php'),
- $('#ldap').serialize(),
- function (result) {
- bgcolor = $('.ldap_submit').css('background');
- if (result.status === 'success') {
- //the dealing with colors is a but ugly, but the jQuery version in use has issues with rgba colors
- $('.ldap_submit').css('background', '#fff');
- $('.ldap_submit').effect('highlight', {'color':'#A8FA87'}, 5000, function() {
- $('.ldap_submit').css('background', bgcolor);
- });
- //update the Label in the config chooser
- caption = $('#ldap_serverconfig_chooser option:selected:first').text();
- pretext = '. Server: ';
- caption = caption.slice(0, caption.indexOf(pretext) + pretext.length);
- caption = caption + $('#ldap_host').val();
- $('#ldap_serverconfig_chooser option:selected:first').text(caption);
-
- } else {
- $('.ldap_submit').css('background', '#fff');
- $('.ldap_submit').effect('highlight', {'color':'#E97'}, 5000, function() {
- $('.ldap_submit').css('background', bgcolor);
- });
- }
- }
- );
- });
-
- $('#ldap_action_clear_user_mappings').click(function(event) {
- event.preventDefault();
- LdapConfiguration.clearMappings('user');
- });
-
- $('#ldap_action_clear_group_mappings').click(function(event) {
- event.preventDefault();
- LdapConfiguration.clearMappings('group');
- });
-
- $('#ldap_serverconfig_chooser').change(function(event) {
- value = $('#ldap_serverconfig_chooser option:selected:first').attr('value');
- if(value === 'NEW') {
- LdapConfiguration.addConfiguration(false);
- } else {
- LdapConfiguration.refreshConfig();
- }
- });
-
- expAdminCB = $('#ldap_experienced_admin');
- LdapWizard.admin = new ExperiencedAdmin(LdapWizard, expAdminCB.is(':checked'));
- expAdminCB.change(function() {
- LdapWizard.admin.setExperienced($(this).is(':checked'));
- });
-});
diff --git a/apps/user_ldap/js/wizard/configModel.js b/apps/user_ldap/js/wizard/configModel.js
new file mode 100644
index 00000000000..c3f1e85b592
--- /dev/null
+++ b/apps/user_ldap/js/wizard/configModel.js
@@ -0,0 +1,606 @@
+/**
+ * Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+OCA = OCA || {};
+
+(function() {
+
+ /**
+ * @classdesc this class represents a server configuration. It communicates
+ * with the ownCloud server to ensure to always have the up to date LDAP
+ * configuration. It sends various events that views can listen to and
+ * provides methods so they can modify the configuration based upon user
+ * input. This model is also extended by so-called "detectors" who let the
+ * ownCloud server try to auto-detect settings and manipulate the
+ * configuration as well.
+ *
+ * @constructor
+ */
+ var ConfigModel = function() {};
+
+ ConfigModel.prototype = {
+ /** @constant {number} */
+ FILTER_MODE_ASSISTED: 0,
+ /** @constant {number} */
+ FILTER_MODE_RAW: 1,
+
+ /**
+ * initializes the instance. Always call it after creating the instance.
+ *
+ * @param {OCA.LDAP.Wizard.WizardDetectorQueue} detectorQueue
+ */
+ init: function (detectorQueue) {
+ /** @type {object} holds the configuration in key-value-pairs */
+ this.configuration = {};
+ /** @type {object} holds the subscribers that listen to the events */
+ this.subscribers = {};
+ /** @type {Array} holds registered detectors */
+ this.detectors = [];
+ /** @type {boolean} whether a configuration is currently loading */
+ this.loadingConfig = false;
+
+ if(detectorQueue instanceof OCA.LDAP.Wizard.WizardDetectorQueue) {
+ /** @type {OCA.LDAP.Wizard.WizardDetectorQueue} */
+ this.detectorQueue = detectorQueue;
+ }
+ },
+
+ /**
+ * loads a specified configuration
+ *
+ * @param {string} [configID] - the configuration id (or prefix)
+ */
+ load: function (configID) {
+ if(this.loadingConfig) {
+ return;
+ }
+ this._resetDetectorQueue();
+
+ this.configID = configID;
+ var url = OC.generateUrl('apps/user_ldap/ajax/getConfiguration.php');
+ var params = OC.buildQueryString({ldap_serverconfig_chooser: configID});
+ this.loadingConfig = true;
+ var model = this;
+ $.post(url, params, function (result) { model._processLoadConfig(model, result) });
+ },
+
+ /**
+ * creates a new LDAP configuration
+ *
+ * @param {boolean} [copyCurrent] - if true, the current configuration
+ * is copied, otherwise a blank one is created.
+ */
+ newConfig: function(copyCurrent) {
+ this._resetDetectorQueue();
+
+ var url = OC.generateUrl('apps/user_ldap/ajax/getNewServerConfigPrefix.php');
+ var params = {};
+ if(copyCurrent === true) {
+ params['copyConfig'] = this.configID;
+ }
+ params = OC.buildQueryString(params);
+ var model = this;
+ copyCurrent = _.isUndefined(copyCurrent) ? false : copyCurrent;
+ $.post(url, params, function (result) { model._processNewConfigPrefix(model, result, copyCurrent) });
+ },
+
+ /**
+ * deletes the current configuration. This method will not ask for
+ * confirmation, if desired it needs to be ensured by the caller.
+ *
+ * @param {string} [configID] - the configuration id (or prefix)
+ */
+ deleteConfig: function(configID) {
+ var url = OC.generateUrl('apps/user_ldap/ajax/deleteConfiguration.php');
+ var params = OC.buildQueryString({ldap_serverconfig_chooser: configID});
+ var model = this;
+ $.post(url, params, function (result) { model._processDeleteConfig(model, result, configID) });
+ },
+
+ /**
+ * @callback wizardCallBack
+ * @param {ConfigModel} [model]
+ * @param {OCA.LDAP.Wizard.WizardDetectorGeneric} [detector]
+ * @param {object} [result] - response from the ajax request
+ */
+
+ /**
+ * calls an AJAX endpoint at ownCloud. This method should be called by
+ * detectors only!
+ *
+ * @param {string} [params] - as return by OC.buildQueryString
+ * @param {wizardCallBack} [callback]
+ * @param {OCA.LDAP.Wizard.WizardDetectorGeneric} [detector]
+ * @returns {jqXHR}
+ */
+ callWizard: function(params, callback, detector) {
+ return this.callAjax('wizard.php', params, callback, detector);
+ },
+
+ /**
+ * calls an AJAX endpoint at ownCloud. This method should be called by
+ * detectors only!
+ *
+ * @param {string} destination - the desired end point
+ * @param {string} [params] - as return by OC.buildQueryString
+ * @param {wizardCallBack} [callback]
+ * @param {OCA.LDAP.Wizard.WizardDetectorGeneric} [detector]
+ * @returns {jqXHR}
+ */
+ callAjax: function(destination, params, callback, detector) {
+ var url = OC.generateUrl('apps/user_ldap/ajax/' + destination);
+ var model = this;
+ return $.post(url, params, function (result) {
+ callback(model, detector,result);
+ });
+ },
+
+ /**
+ * setRequested Event
+ *
+ * @event ConfigModel#setRequested
+ * @type{object} - empty
+ */
+
+ /**
+ * modifies a configuration key. If a provided configuration key does
+ * not exist or the provided value equals the current setting, false is
+ * returned. Otherwise ownCloud server will be called to save the new
+ * value, an event will notify when this is done. True is returned when
+ * the request is sent, however it does not mean whether saving was
+ * successful or not.
+ *
+ * This method is supposed to be called by views, after the user did a
+ * change which needs to be saved.
+ *
+ * @param {string} [key]
+ * @param {string|number} [value]
+ * @returns {boolean}
+ * @fires {ConfigModel#setRequested}
+ */
+ set: function(key, value) {
+ if(_.isUndefined(this.configuration[key])) {
+ console.warn('will not save undefined key: ' + key);
+ return false;
+ }
+ if(this.configuration[key] === value) {
+ return false;
+ }
+ this._broadcast('setRequested', {});
+ var url = OC.generateUrl('apps/user_ldap/ajax/wizard.php');
+ var objParams = {
+ ldap_serverconfig_chooser: this.configID,
+ action: 'save',
+ cfgkey: key,
+ cfgval: value
+ };
+ var strParams = OC.buildQueryString(objParams);
+ var model = this;
+ $.post(url, strParams, function(result) { model._processSetResult(model, result, objParams) });
+ return true;
+ },
+
+ /**
+ * configUpdated Event
+ *
+ * object property is a key-value-pair of the configuration key as index
+ * and its value.
+ *
+ * @event ConfigModel#configUpdated
+ * @type{object}
+ */
+
+ /**
+ * updates the model's configuration data. This should be called only,
+ * when a new configuration value was received from the ownCloud server.
+ * This is typically done by detectors, but never by views.
+ *
+ * Cancels with false if old and new values already match.
+ *
+ * @param {string} [key]
+ * @param {string} [value]
+ * @returns {boolean}
+ * @fires ConfigModel#configUpdated
+ */
+ update: function(key, value) {
+ if(this.configuration[key] === value) {
+ return false;
+ }
+ if(!_.isUndefined(this.configuration[key])) {
+ // don't write e.g. count values to the configuration
+ // they don't go as feature, yet
+ this.configuration[key] = value;
+ }
+ var configPart = {};
+ configPart[key] = value;
+ this._broadcast('configUpdated', configPart);
+ },
+
+ /**
+ * @typedef {object} FeaturePayload
+ * @property {string} feature
+ * @property {Array} data
+ */
+
+ /**
+ * informs about a detected LDAP "feature" (wider sense). For examples,
+ * the detected object classes for users or groups
+ *
+ * @param {FeaturePayload} payload
+ */
+ inform: function(payload) {
+ this._broadcast('receivedLdapFeature', payload);
+ },
+
+ /**
+ * @typedef {object} ErrorPayload
+ * @property {string} message
+ * @property {string} relatedKey
+ */
+
+ /**
+ * broadcasts an error message, if a wizard reply ended up in an error.
+ * To be called by detectors.
+ *
+ * @param {ErrorPayload} payload
+ */
+ gotServerError: function(payload) {
+ this._broadcast('serverError', payload);
+ },
+
+ /**
+ * detectionStarted Event
+ *
+ * @event ConfigModel#detectionStarted
+ * @type{string} - the target configuration key that is being
+ * auto-detected
+ */
+
+ /**
+ * lets the model broadcast the info that a detector starts to run
+ *
+ * supposed to be called by detectors only
+ *
+ * @param {string} [key]
+ * @fires ConfigModel#detectionStarted
+ */
+ notifyAboutDetectionStart: function(key) {
+ this._broadcast('detectionStarted', key);
+ },
+
+ /**
+ * detectionCompleted Event
+ *
+ * @event ConfigModel#detectionCompleted
+ * @type{string} - the target configuration key that was
+ * auto-detected
+ */
+
+ /**
+ * lets the model broadcast the info that a detector run was completed
+ *
+ * supposed to be called by detectors only
+ *
+ * @param {string} [key]
+ * @fires ConfigModel#detectionCompleted
+ */
+ notifyAboutDetectionCompletion: function(key) {
+ this._broadcast('detectionCompleted', key);
+ },
+
+ /**
+ * @callback listenerCallback
+ * @param {OCA.LDAP.Wizard.WizardTabGeneric|OCA.LDAP.Wizard.WizardView} [view]
+ * @param {object} [params]
+ */
+
+ /**
+ * registers a listener to an event
+ *
+ * the idea is that only views listen.
+ *
+ * @param {string} [name] - the event name
+ * @param {listenerCallback} [fn]
+ * @param {OCA.LDAP.Wizard.WizardTabGeneric|OCA.LDAP.Wizard.WizardView} [context]
+ */
+ on: function(name, fn, context) {
+ if(_.isUndefined(this.subscribers[name])) {
+ this.subscribers[name] = [];
+ }
+ this.subscribers[name].push({fn: fn, context: context});
+ },
+
+ /**
+ * starts a configuration test on the ownCloud server
+ */
+ requestConfigurationTest: function() {
+ var url = OC.generateUrl('apps/user_ldap/ajax/testConfiguration.php');
+ var params = OC.buildQueryString(this.configuration);
+ var model = this;
+ $.post(url, params, function(result) { model._processTestResult(model, result) });
+ //TODO: make sure only one test is running at a time
+ },
+
+ /**
+ * the view may request a call to the wizard, for instance to fetch
+ * object classes or groups
+ *
+ * @param {string} featureKey
+ * @param {Object} [additionalParams]
+ */
+ requestWizard: function(featureKey, additionalParams) {
+ var model = this;
+ var detectorCount = this.detectors.length;
+ var found = false;
+ for(var i = 0; i < detectorCount; i++) {
+ if(this.detectors[i].runsOnFeatureRequest(featureKey)) {
+ found = true;
+ (function (detector) {
+ model.detectorQueue.add(function() {
+ return detector.run(model, model.configID, additionalParams);
+ });
+ })(model.detectors[i]);
+ }
+ }
+ if(!found) {
+ console.warn('No detector found for feature ' + featureKey);
+ }
+ },
+
+ /**
+ * resets the detector queue
+ *
+ * @private
+ */
+ _resetDetectorQueue: function() {
+ if(!_.isUndefined(this.detectorQueue)) {
+ this.detectorQueue.reset();
+ }
+ },
+
+ /**
+ * detectors can be registered herewith
+ *
+ * @param {OCA.LDAP.Wizard.WizardDetectorGeneric} [detector]
+ */
+ registerDetector: function(detector) {
+ if(detector instanceof OCA.LDAP.Wizard.WizardDetectorGeneric) {
+ this.detectors.push(detector);
+ }
+ },
+
+ /**
+ * emits an event
+ *
+ * @param {string} [name] - the event name
+ * @param {*} [params]
+ * @private
+ */
+ _broadcast: function(name, params) {
+ if(_.isUndefined(this.subscribers[name])) {
+ return;
+ }
+ var subscribers = this.subscribers[name];
+ var subscriberCount = subscribers.length;
+ for(var i = 0; i < subscriberCount; i++) {
+ if(_.isUndefined(subscribers[i]['fn'])) {
+ console.warn('callback method is not defined. Event ' + name);
+ continue;
+ }
+ subscribers[i]['fn'](subscribers[i]['context'], params);
+ }
+ },
+
+ /**
+ * ConfigModel#configLoaded Event
+ *
+ * @event ConfigModel#configLoaded
+ * @type {object} - LDAP configuration as key-value-pairs
+ */
+
+ /**
+ * @typedef {object} ConfigLoadResponse
+ * @property {string} [status]
+ * @property {object} [configuration] - only present if status equals 'success'
+ */
+
+ /**
+ * processes the ajax response of a configuration load request
+ *
+ * @param {ConfigModel} [model]
+ * @param {ConfigLoadResponse} [result]
+ * @fires ConfigModel#configLoaded
+ * @private
+ */
+ _processLoadConfig: function(model, result) {
+ model.configuration = {};
+ if(result['status'] === 'success') {
+ $.each(result['configuration'], function(key, value) {
+ model.configuration[key] = value;
+ });
+ }
+ model.loadingConfig = false;
+ model._broadcast('configLoaded', model.configuration);
+ },
+
+ /**
+ * @typedef {object} ConfigSetPayload
+ * @property {boolean} [isSuccess]
+ * @property {string} [key]
+ * @property {string} [value]
+ * @property {string} [errorMessage]
+ */
+
+ /**
+ * ConfigModel#setCompleted Event
+ *
+ * @event ConfigModel#setCompleted
+ * @type {ConfigSetPayload}
+ */
+
+ /**
+ * @typedef {object} ConfigSetResponse
+ * @property {string} [status]
+ * @property {object} [message] - might be present only in error cases
+ */
+
+ /**
+ * processes the ajax response of a configuration key set request
+ *
+ * @param {ConfigModel} [model]
+ * @param {ConfigSetResponse} [result]
+ * @param {object} [params] - the original changeSet
+ * @fires ConfigModel#configLoaded
+ * @private
+ */
+ _processSetResult: function(model, result, params) {
+ var isSuccess = (result['status'] === 'success');
+ if(isSuccess) {
+ model.configuration[params.cfgkey] = params.cfgval;
+ }
+ var payload = {
+ isSuccess: isSuccess,
+ key: params.cfgkey,
+ value: model.configuration[params.cfgkey],
+ errorMessage: _.isUndefined(result['message']) ? '' : result['message']
+ };
+ model._broadcast('setCompleted', payload);
+
+ // let detectors run
+ // NOTE: detector's changes will not result in new _processSetResult
+ // calls, … in case they interfere it is because of this ;)
+ if(_.isUndefined(model.detectorQueue)) {
+ console.warn("DetectorQueue was not set, detectors will not be fired");
+ return;
+ }
+ var detectorCount = model.detectors.length;
+ for(var i = 0; i < detectorCount; i++) {
+ if(model.detectors[i].triggersOn(params.cfgkey)) {
+ (function (detector) {
+ model.detectorQueue.add(function() {
+ return detector.run(model, model.configID);
+ });
+ })(model.detectors[i]);
+ }
+ }
+ },
+
+ /**
+ * @typedef {object} ConfigTestPayload
+ * @property {boolean} [isSuccess]
+ */
+
+ /**
+ * ConfigModel#configurationTested Event
+ *
+ * @event ConfigModel#configurationTested
+ * @type {ConfigTestPayload}
+ */
+
+ /**
+ * @typedef {object} StatusResponse
+ * @property {string} [status]
+ */
+
+ /**
+ * processes the ajax response of a configuration test request
+ *
+ * @param {ConfigModel} [model]
+ * @param {StatusResponse} [result]
+ * @fires ConfigModel#configurationTested
+ * @private
+ */
+ _processTestResult: function(model, result) {
+ var payload = {
+ isSuccess: (result['status'] === 'success')
+ };
+ model._broadcast('configurationTested', payload);
+ },
+
+ /**
+ * @typedef {object} BasicConfigPayload
+ * @property {boolean} [isSuccess]
+ * @property {string} [configPrefix] - the new config ID
+ * @property {string} [errorMessage]
+ */
+
+ /**
+ * ConfigModel#newConfiguration Event
+ *
+ * @event ConfigModel#newConfiguration
+ * @type {BasicConfigPayload}
+ */
+
+ /**
+ * @typedef {object} NewConfigResponse
+ * @property {string} [status]
+ * @property {string} [configPrefix]
+ * @property {object} [defaults] - default configuration values
+ * @property {string} [message] - might only appear with status being
+ * not 'success'
+ */
+
+ /**
+ * processes the ajax response of a new configuration request
+ *
+ * @param {ConfigModel} [model]
+ * @param {NewConfigResponse} [result]
+ * @param {boolean} [copyCurrent]
+ * @fires ConfigModel#newConfiguration
+ * @fires ConfigModel#configLoaded
+ * @private
+ */
+ _processNewConfigPrefix: function(model, result, copyCurrent) {
+ var isSuccess = (result['status'] === 'success');
+ var payload = {
+ isSuccess: isSuccess,
+ configPrefix: result['configPrefix'],
+ errorMessage: _.isUndefined(result['message']) ? '' : result['message']
+ };
+ model._broadcast('newConfiguration', payload);
+
+ if(isSuccess) {
+ this.configID = result['configPrefix'];
+ if(!copyCurrent) {
+ model.configuration = {};
+ $.each(result['defaults'], function(key, value) {
+ model.configuration[key] = value;
+ });
+ // view / tabs need to update with new blank config
+ model._broadcast('configLoaded', model.configuration);
+ }
+ }
+ },
+
+ /**
+ * ConfigModel#deleteConfiguration Event
+ *
+ * @event ConfigModel#deleteConfiguration
+ * @type {BasicConfigPayload}
+ */
+
+ /**
+ * processes the ajax response of a delete configuration request
+ *
+ * @param {ConfigModel} [model]
+ * @param {StatusResponse} [result]
+ * @param {string} [configID]
+ * @fires ConfigModel#deleteConfiguration
+ * @private
+ */
+ _processDeleteConfig: function(model, result, configID) {
+ var isSuccess = (result['status'] === 'success');
+ var payload = {
+ isSuccess: isSuccess,
+ configPrefix: configID,
+ errorMessage: _.isUndefined(result['message']) ? '' : result['message']
+ };
+ model._broadcast('deleteConfiguration', payload);
+ }
+ };
+
+ OCA.LDAP.Wizard.ConfigModel = ConfigModel;
+})();
diff --git a/apps/user_ldap/js/wizard/controller.js b/apps/user_ldap/js/wizard/controller.js
new file mode 100644
index 00000000000..7c1f0d5d818
--- /dev/null
+++ b/apps/user_ldap/js/wizard/controller.js
@@ -0,0 +1,56 @@
+/**
+ * Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+OCA = OCA || {};
+OCA.LDAP = {};
+OCA.LDAP.Wizard = {};
+
+(function(){
+
+ /**
+ * @classdesc minimalistic controller that basically makes the view render
+ *
+ * @constructor
+ */
+ var WizardController = function() {};
+
+ WizardController.prototype = {
+ /**
+ * initializes the instance. Always call it after creating the instance.
+ */
+ init: function() {
+ this.view = false;
+ this.configModel = false;
+ },
+
+ /**
+ * sets the model instance
+ *
+ * @param {OCA.LDAP.Wizard.ConfigModel} [model]
+ */
+ setModel: function(model) {
+ this.configModel = model;
+ },
+
+ /**
+ * sets the view instance
+ *
+ * @param {OCA.LDAP.Wizard.WizardView} [view]
+ */
+ setView: function(view) {
+ this.view = view;
+ },
+
+ /**
+ * makes the view render i.e. ready to be used
+ */
+ run: function() {
+ this.view.render();
+ }
+ };
+
+ OCA.LDAP.Wizard.Controller = WizardController;
+})();
diff --git a/apps/user_ldap/js/wizard/view.js b/apps/user_ldap/js/wizard/view.js
new file mode 100644
index 00000000000..7743c277d61
--- /dev/null
+++ b/apps/user_ldap/js/wizard/view.js
@@ -0,0 +1,437 @@
+/**
+ * Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+OCA = OCA || {};
+
+(function() {
+
+ /**
+ * @classdesc main view class. It takes care of tab-unrelated control
+ * elements (status bar, control buttons) and does or requests configuration
+ * checks. It also manages the separate tab views.
+ *
+ * @constructor
+ */
+ var WizardView = function() {};
+
+ WizardView.prototype = {
+ /** @constant {number} */
+ STATUS_ERROR: 0,
+ /** @constant {number} */
+ STATUS_INCOMPLETE: 1,
+ /** @constant {number} */
+ STATUS_SUCCESS: 2,
+
+ /**
+ * initializes the instance. Always call it after creating the instance.
+ */
+ init: function () {
+ this.tabs = {};
+ this.tabs.server = new OCA.LDAP.Wizard.WizardTabElementary();
+ this.$settings = $('#ldapSettings');
+ this.$saveSpinners = $('.ldap_saving');
+ this.saveProcesses = 0;
+ _.bindAll(this, 'onTabChange', 'onTestButtonClick');
+ },
+
+ /**
+ * applies click events to the forward and backword buttons
+ */
+ initControls: function() {
+ var view = this;
+ $('.ldap_action_continue').click(function(event) {
+ event.preventDefault();
+ view._controlContinue(view);
+ });
+
+ $('.ldap_action_back').click(function(event) {
+ event.preventDefault();
+ view._controlBack(view);
+ });
+
+ $('.ldap_action_test_connection').click(this.onTestButtonClick);
+ },
+
+ /**
+ * registers a tab
+ *
+ * @param {OCA.LDAP.Wizard.WizardTabGeneric} tabView
+ * @param {string} index
+ * @returns {boolean}
+ */
+ registerTab: function(tabView, index) {
+ if( _.isUndefined(this.tabs[index])
+ && tabView instanceof OCA.LDAP.Wizard.WizardTabGeneric
+ ) {
+ this.tabs[index] = tabView;
+ this.tabs[index].setModel(this.configModel);
+ return true;
+ }
+ return false;
+ },
+
+ /**
+ * checks certain config values for completeness and depending on them
+ * enables or disables non-elementary tabs.
+ */
+ basicStatusCheck: function(view) {
+ var host = view.configModel.configuration.ldap_host;
+ var port = view.configModel.configuration.ldap_port;
+ var base = view.configModel.configuration.ldap_base;
+ var agent = view.configModel.configuration.ldap_dn;
+ var pwd = view.configModel.configuration.ldap_agent_password;
+
+ if((host && port && base) && ((!agent && !pwd) || (agent && pwd))) {
+ view.enableTabs();
+ } else {
+ view.disableTabs();
+ }
+ },
+
+ /**
+ * if the configuration is sufficient the model is being request to
+ * perform a configuration test. Otherwise, the status indicator is
+ * being updated with the status "incomplete"
+ */
+ functionalityCheck: function() {
+ // this method should be called only if necessary, because it may
+ // cause an LDAP request!
+ var host = this.configModel.configuration.ldap_host;
+ var port = this.configModel.configuration.ldap_port;
+ var base = this.configModel.configuration.ldap_base;
+ var userFilter = this.configModel.configuration.ldap_userlist_filter;
+ var loginFilter = this.configModel.configuration.ldap_login_filter;
+
+ if(host && port && base && userFilter && loginFilter) {
+ this.configModel.requestConfigurationTest();
+ } else {
+ this._updateStatusIndicator(this.STATUS_INCOMPLETE);
+ }
+ },
+
+ /**
+ * will request a functionality check if one of the related configuration
+ * settings was changed.
+ *
+ * @param {ConfigSetPayload|Object} [changeSet]
+ */
+ considerFunctionalityCheck: function(changeSet) {
+ var testTriggers = [
+ 'ldap_host', 'ldap_port', 'ldap_dn', 'ldap_agent_password',
+ 'ldap_base', 'ldap_userlist_filter', 'ldap_login_filter'
+ ];
+ for(var key in changeSet) {
+ if($.inArray(key, testTriggers) >= 0) {
+ this.functionalityCheck();
+ return;
+ }
+ }
+ },
+
+ /**
+ * keeps number of running save processes and shows a spinner if
+ * necessary
+ *
+ * @param {WizardView} [view]
+ * @listens ConfigModel#setRequested
+ */
+ onSetRequested: function(view) {
+ view.saveProcesses += 1;
+ if(view.saveProcesses === 1) {
+ view.showSaveSpinner();
+ }
+ },
+
+ /**
+ * keeps number of running save processes and hides the spinner if
+ * necessary. Also triggers checks, to adjust tabs state and status bar.
+ *
+ * @param {WizardView} [view]
+ * @param {ConfigSetPayload} [result]
+ * @listens ConfigModel#setCompleted
+ */
+ onSetRequestDone: function(view, result) {
+ if(view.saveProcesses > 0) {
+ view.saveProcesses -= 1;
+ if(view.saveProcesses === 0) {
+ view.hideSaveSpinner();
+ }
+ }
+
+ view.basicStatusCheck(view);
+ var param = {};
+ param[result.key] = 1;
+ view.considerFunctionalityCheck(param);
+ },
+
+ /**
+ * updates the status indicator based on the configuration test result
+ *
+ * @param {WizardView} [view]
+ * @param {ConfigTestPayload} [result]
+ * @listens ConfigModel#configurationTested
+ */
+ onTestCompleted: function(view, result) {
+ if(result.isSuccess) {
+ view._updateStatusIndicator(view.STATUS_SUCCESS);
+ } else {
+ view._updateStatusIndicator(view.STATUS_ERROR);
+ }
+ },
+
+ /**
+ * triggers initial checks upon configuration loading to update status
+ * controls
+ *
+ * @param {WizardView} [view]
+ * @listens ConfigModel#configLoaded
+ */
+ onConfigLoaded: function(view) {
+ view.basicStatusCheck(view);
+ view.functionalityCheck();
+ },
+
+ /**
+ * reacts on attempts to switch to a different tab
+ *
+ * @param {object} event
+ * @param {object} ui
+ * @returns {boolean}
+ */
+ onTabChange: function(event, ui) {
+ if(this.saveProcesses > 0) {
+ return false;
+ }
+
+ var newTabID = ui.newTab[0].id;
+ if(newTabID === '#ldapWizard1') {
+ newTabID = 'server';
+ }
+ var oldTabID = ui.oldTab[0].id;
+ if(oldTabID === '#ldapWizard1') {
+ oldTabID = 'server';
+ }
+ if(!_.isUndefined(this.tabs[newTabID])) {
+ this.tabs[newTabID].isActive = true;
+ this.tabs[newTabID].onActivate();
+ } else {
+ console.warn('Unreferenced activated tab ' + newTabID);
+ }
+ if(!_.isUndefined(this.tabs[oldTabID])) {
+ this.tabs[oldTabID].isActive = false;
+ } else {
+ console.warn('Unreferenced left tab ' + oldTabID);
+ }
+
+ if(!_.isUndefined(this.tabs[newTabID])) {
+ this._controlUpdate(this.tabs[newTabID].tabIndex);
+ }
+ },
+
+ /**
+ * triggers checks upon configuration updates to keep status controls
+ * up to date
+ *
+ * @param {WizardView} [view]
+ * @param {object} [changeSet]
+ * @listens ConfigModel#configUpdated
+ */
+ onConfigUpdated: function(view, changeSet) {
+ view.basicStatusCheck(view);
+ view.considerFunctionalityCheck(changeSet);
+ },
+
+ /**
+ * requests a configuration test
+ */
+ onTestButtonClick: function() {
+ this.configModel.requestWizard('ldap_action_test_connection', this.configModel.configuration);
+ },
+
+ /**
+ * sets the model instance and registers event listeners
+ *
+ * @param {OCA.LDAP.Wizard.ConfigModel} [configModel]
+ */
+ setModel: function(configModel) {
+ /** @type {OCA.LDAP.Wizard.ConfigModel} */
+ this.configModel = configModel;
+ for(var i in this.tabs) {
+ this.tabs[i].setModel(configModel);
+ }
+
+ // make sure this is definitely run after tabs did their work, order is important here
+ // for now this works, because tabs are supposed to register their listeners in their
+ // setModel() method.
+ // alternative: make Elementary Tab a Publisher as well.
+ this.configModel.on('configLoaded', this.onConfigLoaded, this);
+ this.configModel.on('configUpdated', this.onConfigUpdated, this);
+ this.configModel.on('setRequested', this.onSetRequested, this);
+ this.configModel.on('setCompleted', this.onSetRequestDone, this);
+ this.configModel.on('configurationTested', this.onTestCompleted, this);
+ },
+
+ /**
+ * enables tab and navigation buttons
+ */
+ enableTabs: function() {
+ //do not use this function directly, use basicStatusCheck instead.
+ if(this.saveProcesses === 0) {
+ $('.ldap_action_continue').removeAttr('disabled');
+ $('.ldap_action_back').removeAttr('disabled');
+ this.$settings.tabs('option', 'disabled', []);
+ }
+ },
+
+ /**
+ * disables tab and navigation buttons
+ */
+ disableTabs: function() {
+ $('.ldap_action_continue').attr('disabled', 'disabled');
+ $('.ldap_action_back').attr('disabled', 'disabled');
+ this.$settings.tabs('option', 'disabled', [1, 2, 3, 4, 5]);
+ },
+
+ /**
+ * shows a save spinner
+ */
+ showSaveSpinner: function() {
+ this.$saveSpinners.removeClass('hidden');
+ $('#ldap *').addClass('save-cursor');
+ },
+
+ /**
+ * hides the save spinner
+ */
+ hideSaveSpinner: function() {
+ this.$saveSpinners.addClass('hidden');
+ $('#ldap *').removeClass('save-cursor');
+ },
+
+ /**
+ * performs a config load request to the model
+ *
+ * @param {string} [configID]
+ * @private
+ */
+ _requestConfig: function(configID) {
+ this.configModel.load(configID);
+ },
+
+ /**
+ * bootstraps the visual appearance and event listeners, as well as the
+ * first config
+ */
+ render: function () {
+ $('#ldapAdvancedAccordion').accordion({ heightStyle: 'content', animate: 'easeInOutCirc'});
+ this.$settings.tabs({});
+ $('.ldap_submit').button();
+ $('.ldap_action_test_connection').button();
+ $('#ldapSettings').tabs({ beforeActivate: this.onTabChange });
+
+ this.initControls();
+ this.disableTabs();
+
+ this._requestConfig(this.tabs.server.getConfigID());
+ },
+
+ /**
+ * updates the status indicator / bar
+ *
+ * @param {number} [state]
+ * @private
+ */
+ _updateStatusIndicator: function(state) {
+ var $indicator = $('.ldap_config_state_indicator');
+ var $indicatorLight = $('.ldap_config_state_indicator_sign');
+
+ switch(state) {
+ case this.STATUS_ERROR:
+ $indicator.text(t('user_ldap',
+ 'Configuration incorrect'
+ ));
+ $indicator.removeClass('ldap_grey');
+ $indicatorLight.addClass('error');
+ $indicatorLight.removeClass('success');
+ break;
+ case this.STATUS_INCOMPLETE:
+ $indicator.text(t('user_ldap',
+ 'Configuration incomplete'
+ ));
+ $indicator.removeClass('ldap_grey');
+ $indicatorLight.removeClass('error');
+ $indicatorLight.removeClass('success');
+ break;
+ case this.STATUS_SUCCESS:
+ $indicator.text(t('user_ldap', 'Configuration OK'));
+ $indicator.addClass('ldap_grey');
+ $indicatorLight.removeClass('error');
+ $indicatorLight.addClass('success');
+ if(!this.tabs.server.isActive) {
+ this.configModel.set('ldap_configuration_active', 1);
+ }
+ break;
+ }
+ },
+
+ /**
+ * handles a click on the Back button
+ *
+ * @param {WizardView} [view]
+ * @private
+ */
+ _controlBack: function(view) {
+ var curTabIndex = view.$settings.tabs('option', 'active');
+ if(curTabIndex == 0) {
+ return;
+ }
+ view.$settings.tabs('option', 'active', curTabIndex - 1);
+ view._controlUpdate(curTabIndex - 1);
+ },
+
+ /**
+ * handles a click on the Continue button
+ *
+ * @param {WizardView} [view]
+ * @private
+ */
+ _controlContinue: function(view) {
+ var curTabIndex = view.$settings.tabs('option', 'active');
+ if(curTabIndex == 3) {
+ return;
+ }
+ view.$settings.tabs('option', 'active', 1 + curTabIndex);
+ view._controlUpdate(curTabIndex + 1);
+ },
+
+ /**
+ * updates the controls (navigation buttons)
+ *
+ * @param {number} [nextTabIndex] - index of the tab being switched to
+ * @private
+ */
+ _controlUpdate: function(nextTabIndex) {
+ if(nextTabIndex == 0) {
+ $('.ldap_action_back').addClass('invisible');
+ $('.ldap_action_continue').removeClass('invisible');
+ } else
+ if(nextTabIndex == 1) {
+ $('.ldap_action_back').removeClass('invisible');
+ $('.ldap_action_continue').removeClass('invisible');
+ } else
+ if(nextTabIndex == 2) {
+ $('.ldap_action_continue').removeClass('invisible');
+ $('.ldap_action_back').removeClass('invisible');
+ } else
+ if(nextTabIndex == 3) {
+ $('.ldap_action_back').removeClass('invisible');
+ $('.ldap_action_continue').addClass('invisible');
+ }
+ }
+ };
+
+ OCA.LDAP.Wizard.WizardView = WizardView;
+})();
diff --git a/apps/user_ldap/js/wizard/wizard.js b/apps/user_ldap/js/wizard/wizard.js
new file mode 100644
index 00000000000..e8450d1c78f
--- /dev/null
+++ b/apps/user_ldap/js/wizard/wizard.js
@@ -0,0 +1,80 @@
+/**
+ * Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+OCA = OCA || {};
+
+
+
+/**
+ * initializes the wizard and related components and kicks it off.
+ */
+
+(function() {
+ var Wizard = function() {
+ var detectorQueue = new OCA.LDAP.Wizard.WizardDetectorQueue();
+ detectorQueue.init();
+
+ var detectors = [];
+ detectors.push(new OCA.LDAP.Wizard.WizardDetectorPort());
+ detectors.push(new OCA.LDAP.Wizard.WizardDetectorBaseDN());
+ detectors.push(new OCA.LDAP.Wizard.WizardDetectorEmailAttribute());
+ detectors.push(new OCA.LDAP.Wizard.WizardDetectorUserDisplayNameAttribute());
+ detectors.push(new OCA.LDAP.Wizard.WizardDetectorUserGroupAssociation());
+ detectors.push(new OCA.LDAP.Wizard.WizardDetectorUserObjectClasses());
+ detectors.push(new OCA.LDAP.Wizard.WizardDetectorGroupObjectClasses());
+ detectors.push(new OCA.LDAP.Wizard.WizardDetectorGroupsForUsers());
+ detectors.push(new OCA.LDAP.Wizard.WizardDetectorGroupsForGroups());
+ detectors.push(new OCA.LDAP.Wizard.WizardDetectorFilterUser());
+ detectors.push(new OCA.LDAP.Wizard.WizardDetectorFilterLogin());
+ detectors.push(new OCA.LDAP.Wizard.WizardDetectorFilterGroup());
+ detectors.push(new OCA.LDAP.Wizard.WizardDetectorUserCount());
+ detectors.push(new OCA.LDAP.Wizard.WizardDetectorGroupCount());
+ detectors.push(new OCA.LDAP.Wizard.WizardDetectorAvailableAttributes());
+ detectors.push(new OCA.LDAP.Wizard.WizardDetectorTestLoginName());
+ detectors.push(new OCA.LDAP.Wizard.WizardDetectorTestBaseDN());
+ detectors.push(new OCA.LDAP.Wizard.WizardDetectorTestConfiguration());
+ detectors.push(new OCA.LDAP.Wizard.WizardDetectorClearUserMappings());
+ detectors.push(new OCA.LDAP.Wizard.WizardDetectorClearGroupMappings());
+
+ var model = new OCA.LDAP.Wizard.ConfigModel();
+ model.init(detectorQueue);
+ // NOTE: order of detectors may play a role
+ // for example, BaseDN detector needs the port. The port is typically found
+ // by the Port Detector. If BaseDN detector was run first, it will not have
+ // all necessary information. Only after Port Detector was executed…
+ for (var i = 0; i <= detectors.length; i++) {
+ model.registerDetector(detectors[i]);
+ }
+
+ var filterOnTypeFactory = new OCA.LDAP.Wizard.FilterOnTypeFactory();
+
+ var tabs = [];
+ tabs.push(new OCA.LDAP.Wizard.WizardTabUserFilter(filterOnTypeFactory, 1));
+ tabs.push(new OCA.LDAP.Wizard.WizardTabLoginFilter(2));
+ tabs.push(new OCA.LDAP.Wizard.WizardTabGroupFilter(filterOnTypeFactory, 3));
+ tabs.push(new OCA.LDAP.Wizard.WizardTabAdvanced());
+ tabs.push(new OCA.LDAP.Wizard.WizardTabExpert());
+
+ var view = new OCA.LDAP.Wizard.WizardView(model);
+ view.init();
+ view.setModel(model);
+ for (var j = 0; j <= tabs.length; j++) {
+ view.registerTab(tabs[j], '#ldapWizard' + (j + 2));
+ }
+
+ var controller = new OCA.LDAP.Wizard.Controller();
+ controller.init();
+ controller.setView(view);
+ controller.setModel(model);
+ controller.run();
+ }
+
+ OCA.LDAP.Wizard.Wizard = Wizard;
+})();
+
+$(document).ready(function() {
+ new OCA.LDAP.Wizard.Wizard();
+});
diff --git a/apps/user_ldap/js/wizard/wizardDetectorAvailableAttributes.js b/apps/user_ldap/js/wizard/wizardDetectorAvailableAttributes.js
new file mode 100644
index 00000000000..f0272351749
--- /dev/null
+++ b/apps/user_ldap/js/wizard/wizardDetectorAvailableAttributes.js
@@ -0,0 +1,59 @@
+
+/**
+ * Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+OCA = OCA || {};
+
+(function() {
+
+ /**
+ * @classdesc an Attributes Detector. It executes the auto-detection of
+ * available attributes by the ownCloud server, if requirements are met.
+ *
+ * @constructor
+ */
+ var WizardDetectorAvailableAttributes = OCA.LDAP.Wizard.WizardDetectorGeneric.subClass({
+ /** @inheritdoc */
+ init: function() {
+ // given, it is not a configuration key
+ this.setTargetKey('ldap_loginfilter_attributes');
+ this.runsOnRequest = true;
+ },
+
+ /**
+ * runs the detector, if port is not set.
+ *
+ * @param {OCA.LDAP.Wizard.ConfigModel} model
+ * @param {string} configID - the configuration prefix
+ * @returns {boolean|jqXHR}
+ * @abstract
+ */
+ run: function(model, configID) {
+ model.notifyAboutDetectionStart(this.getTargetKey());
+ var params = OC.buildQueryString({
+ action: 'determineAttributes',
+ ldap_serverconfig_chooser: configID
+ });
+ return model.callWizard(params, this.processResult, this);
+ },
+
+ /**
+ * @inheritdoc
+ */
+ processResult: function(model, detector, result) {
+ if(result.status === 'success') {
+ var payload = {
+ feature: 'AvailableAttributes',
+ data: result.options[detector.getTargetKey()]
+ };
+ model.inform(payload);
+ }
+ this._super(model, detector, result);
+ }
+ });
+
+ OCA.LDAP.Wizard.WizardDetectorAvailableAttributes = WizardDetectorAvailableAttributes;
+})();
diff --git a/apps/user_ldap/js/wizard/wizardDetectorBaseDN.js b/apps/user_ldap/js/wizard/wizardDetectorBaseDN.js
new file mode 100644
index 00000000000..70b9923e58d
--- /dev/null
+++ b/apps/user_ldap/js/wizard/wizardDetectorBaseDN.js
@@ -0,0 +1,52 @@
+
+/**
+ * Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+OCA = OCA || {};
+
+(function() {
+
+ /**
+ * @classdesc a Base DN Detector. It executes the auto-detection of the base
+ * DN by the ownCloud server, if requirements are met.
+ *
+ * @constructor
+ */
+ var WizardDetectorBaseDN = OCA.LDAP.Wizard.WizardDetectorGeneric.subClass({
+ /** @inheritdoc */
+ init: function() {
+ this.setTargetKey('ldap_base');
+ this.runsOnRequest = true;
+ },
+
+ /**
+ * runs the detector, if specified configuration settings are set and
+ * base DN is not set.
+ *
+ * @param {OCA.LDAP.Wizard.ConfigModel} model
+ * @param {string} configID - the configuration prefix
+ * @returns {boolean|jqXHR}
+ * @abstract
+ */
+ run: function(model, configID) {
+ if( !model.configuration['ldap_host']
+ || !model.configuration['ldap_port']
+
+ )
+ {
+ return false;
+ }
+ model.notifyAboutDetectionStart(this.getTargetKey());
+ var params = OC.buildQueryString({
+ action: 'guessBaseDN',
+ ldap_serverconfig_chooser: configID
+ });
+ return model.callWizard(params, this.processResult, this);
+ }
+ });
+
+ OCA.LDAP.Wizard.WizardDetectorBaseDN = WizardDetectorBaseDN;
+})();
diff --git a/apps/user_ldap/js/wizard/wizardDetectorClearGroupMappings.js b/apps/user_ldap/js/wizard/wizardDetectorClearGroupMappings.js
new file mode 100644
index 00000000000..c6ef0a9cab1
--- /dev/null
+++ b/apps/user_ldap/js/wizard/wizardDetectorClearGroupMappings.js
@@ -0,0 +1,30 @@
+
+/**
+ * Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+OCA = OCA || {};
+
+(function() {
+
+ /**
+ * @classdesc requests clearing of user mappings
+ *
+ * @constructor
+ */
+ var WizardDetectorClearGroupMappings = OCA.LDAP.Wizard.WizardDetectorTestAbstract.subClass({
+ /** @inheritdoc */
+ init: function() {
+ // given, it is not a configuration key
+ this.setTargetKey('ldap_action_clear_group_mappings');
+ this.testName = 'ClearMappings';
+ this.isLegacy = true;
+ this.legacyDestination = 'clearMappings.php';
+ this.runsOnRequest = true;
+ }
+ });
+
+ OCA.LDAP.Wizard.WizardDetectorClearGroupMappings = WizardDetectorClearGroupMappings;
+})();
diff --git a/apps/user_ldap/js/wizard/wizardDetectorClearUserMappings.js b/apps/user_ldap/js/wizard/wizardDetectorClearUserMappings.js
new file mode 100644
index 00000000000..0e4811b39ea
--- /dev/null
+++ b/apps/user_ldap/js/wizard/wizardDetectorClearUserMappings.js
@@ -0,0 +1,30 @@
+
+/**
+ * Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+OCA = OCA || {};
+
+(function() {
+
+ /**
+ * @classdesc requests clearing of user mappings
+ *
+ * @constructor
+ */
+ var WizardDetectorClearUserMappings = OCA.LDAP.Wizard.WizardDetectorTestAbstract.subClass({
+ /** @inheritdoc */
+ init: function() {
+ // given, it is not a configuration key
+ this.setTargetKey('ldap_action_clear_user_mappings');
+ this.testName = 'ClearMappings';
+ this.isLegacy = true;
+ this.legacyDestination = 'clearMappings.php';
+ this.runsOnRequest = true;
+ }
+ });
+
+ OCA.LDAP.Wizard.WizardDetectorClearUserMappings = WizardDetectorClearUserMappings;
+})();
diff --git a/apps/user_ldap/js/wizard/wizardDetectorEmailAttribute.js b/apps/user_ldap/js/wizard/wizardDetectorEmailAttribute.js
new file mode 100644
index 00000000000..5f177734681
--- /dev/null
+++ b/apps/user_ldap/js/wizard/wizardDetectorEmailAttribute.js
@@ -0,0 +1,38 @@
+
+/**
+ * Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+OCA = OCA || {};
+
+(function() {
+
+ /**
+ * @classdesc let's the wizard backend count the available users
+ *
+ * @constructor
+ */
+ var WizardDetectorEmailAttribute = OCA.LDAP.Wizard.WizardDetectorFilterSimpleRequestAbstract.subClass({
+ init: function() {
+ this.setTargetKey('ldap_user_count');
+ this.wizardMethod = 'detectEmailAttribute';
+ this.runsOnRequest = true;
+ },
+
+ /**
+ * @inheritdoc
+ */
+ run: function(model, configID) {
+ if(model.configuration.ldap_email_attr) {
+ // a value is already set. Don't overwrite and don't ask LDAP
+ // without reason.
+ return false;
+ }
+ this._super(model, configID);
+ }
+ });
+
+ OCA.LDAP.Wizard.WizardDetectorEmailAttribute = WizardDetectorEmailAttribute;
+})();
diff --git a/apps/user_ldap/js/wizard/wizardDetectorFeatureAbstract.js b/apps/user_ldap/js/wizard/wizardDetectorFeatureAbstract.js
new file mode 100644
index 00000000000..e025d8d6242
--- /dev/null
+++ b/apps/user_ldap/js/wizard/wizardDetectorFeatureAbstract.js
@@ -0,0 +1,52 @@
+
+/**
+ * Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+OCA = OCA || {};
+
+(function() {
+
+ /**
+ * @classdesc abstract detector for detecting groups and object classes
+ *
+ * @constructor
+ */
+ var WizardDetectorFeatureAbstract = OCA.LDAP.Wizard.WizardDetectorGeneric.subClass({
+ /**
+ * runs the detector, if port is not set.
+ *
+ * @param {OCA.LDAP.Wizard.ConfigModel} model
+ * @param {string} configID - the configuration prefix
+ * @returns {boolean|jqXHR}
+ * @abstract
+ */
+ run: function(model, configID) {
+ model.notifyAboutDetectionStart(this.getTargetKey());
+ var params = OC.buildQueryString({
+ action: this.wizardMethod,
+ ldap_serverconfig_chooser: configID
+ });
+ return model.callWizard(params, this.processResult, this);
+ },
+
+ /**
+ * @inheritdoc
+ */
+ processResult: function(model, detector, result) {
+ if(result.status === 'success') {
+ var payload = {
+ feature: detector.featureName,
+ data: result.options[detector.getTargetKey()]
+ };
+ model.inform(payload);
+ }
+
+ this._super(model, detector, result);
+ }
+ });
+
+ OCA.LDAP.Wizard.WizardDetectorFeatureAbstract = WizardDetectorFeatureAbstract;
+})();
diff --git a/apps/user_ldap/js/wizard/wizardDetectorFilterGroup.js b/apps/user_ldap/js/wizard/wizardDetectorFilterGroup.js
new file mode 100644
index 00000000000..cca889839e4
--- /dev/null
+++ b/apps/user_ldap/js/wizard/wizardDetectorFilterGroup.js
@@ -0,0 +1,31 @@
+
+/**
+ * Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+OCA = OCA || {};
+
+(function() {
+
+ /**
+ * @classdesc a Port Detector. It executes the auto-detection of the port
+ * by the ownCloud server, if requirements are met.
+ *
+ * @constructor
+ */
+ var WizardDetectorFilterGroup = OCA.LDAP.Wizard.WizardDetectorFilterSimpleRequestAbstract.subClass({
+ init: function() {
+ this.setTrigger([
+ 'ldap_groupfilter_groups',
+ 'ldap_groupfilter_objectclass'
+ ]);
+ this.setTargetKey('ldap_group_filter');
+
+ this.wizardMethod = 'getGroupFilter';
+ }
+ });
+
+ OCA.LDAP.Wizard.WizardDetectorFilterGroup = WizardDetectorFilterGroup;
+})();
diff --git a/apps/user_ldap/js/wizard/wizardDetectorFilterLogin.js b/apps/user_ldap/js/wizard/wizardDetectorFilterLogin.js
new file mode 100644
index 00000000000..e796b81e0eb
--- /dev/null
+++ b/apps/user_ldap/js/wizard/wizardDetectorFilterLogin.js
@@ -0,0 +1,33 @@
+
+/**
+ * Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+OCA = OCA || {};
+
+(function() {
+
+ /**
+ * @classdesc a Port Detector. It executes the auto-detection of the port
+ * by the ownCloud server, if requirements are met.
+ *
+ * @constructor
+ */
+ var WizardDetectorFilterLogin = OCA.LDAP.Wizard.WizardDetectorFilterSimpleRequestAbstract.subClass({
+ init: function() {
+ this.setTrigger([
+ 'ldap_loginfilter_username',
+ 'ldap_loginfilter_email',
+ 'ldap_loginfilter_attributes'
+ ]);
+ this.setTargetKey('ldap_login_filter');
+ this.runsOnRequest = true;
+
+ this.wizardMethod = 'getUserLoginFilter';
+ }
+ });
+
+ OCA.LDAP.Wizard.WizardDetectorFilterLogin = WizardDetectorFilterLogin;
+})();
diff --git a/apps/user_ldap/js/wizard/wizardDetectorFilterUser.js b/apps/user_ldap/js/wizard/wizardDetectorFilterUser.js
new file mode 100644
index 00000000000..d34e244a1f5
--- /dev/null
+++ b/apps/user_ldap/js/wizard/wizardDetectorFilterUser.js
@@ -0,0 +1,32 @@
+
+/**
+ * Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+OCA = OCA || {};
+
+(function() {
+
+ /**
+ * @classdesc a Port Detector. It executes the auto-detection of the port
+ * by the ownCloud server, if requirements are met.
+ *
+ * @constructor
+ */
+ var WizardDetectorFilterUser = OCA.LDAP.Wizard.WizardDetectorFilterSimpleRequestAbstract.subClass({
+ init: function() {
+ this.setTrigger([
+ 'ldap_userfilter_groups',
+ 'ldap_userfilter_objectclass'
+ ]);
+ this.setTargetKey('ldap_userlist_filter');
+ this.runsOnRequest = true;
+
+ this.wizardMethod = 'getUserListFilter';
+ }
+ });
+
+ OCA.LDAP.Wizard.WizardDetectorFilterUser = WizardDetectorFilterUser;
+})();
diff --git a/apps/user_ldap/js/wizard/wizardDetectorGeneric.js b/apps/user_ldap/js/wizard/wizardDetectorGeneric.js
new file mode 100644
index 00000000000..fd80018943e
--- /dev/null
+++ b/apps/user_ldap/js/wizard/wizardDetectorGeneric.js
@@ -0,0 +1,117 @@
+
+/**
+ * Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+OCA = OCA || {};
+
+(function() {
+ /**
+ * @classdesc a generic (abstract) Detector template. A Detector's task is
+ * to kick off server side detection of certain LDAP features. It is invoked
+ * when changes to specified configuration keys happen.
+ *
+ * @constructor
+ */
+ var WizardDetectorGeneric = OCA.LDAP.Wizard.WizardObject.subClass({
+ /**
+ * initializes the instance. Always call it after creating the instance.
+ */
+ init: function() {
+ this.setTrigger([]);
+ this.targetKey = '';
+ this.runsOnRequest = false;
+ },
+
+ /**
+ * sets the configuration keys the detector is listening on
+ *
+ * @param {string[]} triggers
+ */
+ setTrigger: function(triggers) {
+ this.triggers = triggers;
+ },
+
+ /**
+ * tests whether the detector is triggered by the provided key
+ *
+ * @param {string} key
+ * @returns {boolean}
+ */
+ triggersOn: function(key) {
+ return ($.inArray(key, this.triggers) >= 0);
+ },
+
+ /**
+ * whether the detector runs on explicit request
+ *
+ * @param {string} key
+ * @returns {boolean}
+ */
+ runsOnFeatureRequest: function(key) {
+ return !!(this.runsOnRequest && this.targetKey === key);
+ },
+
+ /**
+ * sets the configuration key the detector is attempting to auto-detect
+ *
+ * @param {string} key
+ */
+ setTargetKey: function(key) {
+ this.targetKey = key;
+ },
+
+ /**
+ * returns the configuration key the detector is attempting to
+ * auto-detect
+ */
+ getTargetKey: function() {
+ return this.targetKey;
+ },
+
+ /**
+ * runs the detector. This method is supposed to be implemented by the
+ * concrete detector.
+ *
+ * Must return false if the detector decides not to run.
+ * Must return a jqXHR object otherwise, which is provided by the
+ * model's callWizard()
+ *
+ * @param {OCA.LDAP.Wizard.ConfigModel} model
+ * @param {string} configID - the configuration prefix
+ * @returns {boolean|jqXHR}
+ * @abstract
+ */
+ run: function(model, configID) {
+ // to be implemented by subClass
+ return false;
+ },
+
+ /**
+ * processes the result of the ownCloud server
+ *
+ * @param {OCA.LDAP.Wizard.ConfigModel} model
+ * @param {WizardDetectorGeneric} detector
+ * @param {object} result
+ */
+ processResult: function(model, detector, result) {
+ model['notifyAboutDetectionCompletion'](detector.getTargetKey());
+ if(result.status === 'success') {
+ for (var id in result.changes) {
+ // update and not set method, as values are already stored
+ model['update'](id, result.changes[id]);
+ }
+ } else {
+ var payload = { relatedKey: detector.targetKey };
+ if(!_.isUndefined(result.message)) {
+ payload.message = result.message;
+ }
+ model.gotServerError(payload);
+ }
+ }
+ });
+
+ OCA.LDAP.Wizard.WizardDetectorGeneric = WizardDetectorGeneric;
+})();
diff --git a/apps/user_ldap/js/wizard/wizardDetectorGroupCount.js b/apps/user_ldap/js/wizard/wizardDetectorGroupCount.js
new file mode 100644
index 00000000000..12d7df7514b
--- /dev/null
+++ b/apps/user_ldap/js/wizard/wizardDetectorGroupCount.js
@@ -0,0 +1,27 @@
+
+/**
+ * Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+OCA = OCA || {};
+
+(function() {
+
+ /**
+ * @classdesc a Port Detector. It executes the auto-detection of the port
+ * by the ownCloud server, if requirements are met.
+ *
+ * @constructor
+ */
+ var WizardDetectorGroupCount = OCA.LDAP.Wizard.WizardDetectorFilterSimpleRequestAbstract.subClass({
+ init: function() {
+ this.setTargetKey('ldap_group_count');
+ this.wizardMethod = 'countGroups';
+ this.runsOnRequest = true;
+ }
+ });
+
+ OCA.LDAP.Wizard.WizardDetectorGroupCount = WizardDetectorGroupCount;
+})();
diff --git a/apps/user_ldap/js/wizard/wizardDetectorGroupObjectClasses.js b/apps/user_ldap/js/wizard/wizardDetectorGroupObjectClasses.js
new file mode 100644
index 00000000000..6d6048b7986
--- /dev/null
+++ b/apps/user_ldap/js/wizard/wizardDetectorGroupObjectClasses.js
@@ -0,0 +1,29 @@
+
+/**
+ * Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+OCA = OCA || {};
+
+(function() {
+
+ /**
+ * @classdesc discovers object classes for the groups tab
+ *
+ * @constructor
+ */
+ var WizardDetectorGroupObjectClasses = OCA.LDAP.Wizard.WizardDetectorFeatureAbstract.subClass({
+ /** @inheritdoc */
+ init: function() {
+ // given, it is not a configuration key
+ this.setTargetKey('ldap_groupfilter_objectclass');
+ this.wizardMethod = 'determineGroupObjectClasses';
+ this.featureName = 'GroupObjectClasses';
+ this.runsOnRequest = true;
+ }
+ });
+
+ OCA.LDAP.Wizard.WizardDetectorGroupObjectClasses = WizardDetectorGroupObjectClasses;
+})();
diff --git a/apps/user_ldap/js/wizard/wizardDetectorGroupsForGroups.js b/apps/user_ldap/js/wizard/wizardDetectorGroupsForGroups.js
new file mode 100644
index 00000000000..fbb3f02e10a
--- /dev/null
+++ b/apps/user_ldap/js/wizard/wizardDetectorGroupsForGroups.js
@@ -0,0 +1,29 @@
+
+/**
+ * Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+OCA = OCA || {};
+
+(function() {
+
+ /**
+ * @classdesc detects groups for the groups tab
+ *
+ * @constructor
+ */
+ var WizardDetectorGroupsForGroups = OCA.LDAP.Wizard.WizardDetectorFeatureAbstract.subClass({
+ /** @inheritdoc */
+ init: function() {
+ // given, it is not a configuration key
+ this.setTargetKey('ldap_groupfilter_groups');
+ this.wizardMethod = 'determineGroupsForGroups';
+ this.featureName = 'GroupsForGroups';
+ this.runsOnRequest = true;
+ }
+ });
+
+ OCA.LDAP.Wizard.WizardDetectorGroupsForGroups = WizardDetectorGroupsForGroups;
+})();
diff --git a/apps/user_ldap/js/wizard/wizardDetectorGroupsForUsers.js b/apps/user_ldap/js/wizard/wizardDetectorGroupsForUsers.js
new file mode 100644
index 00000000000..fe67854c794
--- /dev/null
+++ b/apps/user_ldap/js/wizard/wizardDetectorGroupsForUsers.js
@@ -0,0 +1,29 @@
+
+/**
+ * Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+OCA = OCA || {};
+
+(function() {
+
+ /**
+ * @classdesc detects groups for the users tab
+ *
+ * @constructor
+ */
+ var WizardDetectorGroupsForUsers = OCA.LDAP.Wizard.WizardDetectorFeatureAbstract.subClass({
+ /** @inheritdoc */
+ init: function() {
+ // given, it is not a configuration key
+ this.setTargetKey('ldap_userfilter_groups');
+ this.wizardMethod = 'determineGroupsForUsers';
+ this.featureName = 'GroupsForUsers';
+ this.runsOnRequest = true;
+ }
+ });
+
+ OCA.LDAP.Wizard.WizardDetectorGroupsForUsers = WizardDetectorGroupsForUsers;
+})();
diff --git a/apps/user_ldap/js/wizard/wizardDetectorPort.js b/apps/user_ldap/js/wizard/wizardDetectorPort.js
new file mode 100644
index 00000000000..ba075189667
--- /dev/null
+++ b/apps/user_ldap/js/wizard/wizardDetectorPort.js
@@ -0,0 +1,44 @@
+
+/**
+ * Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+OCA = OCA || {};
+
+(function() {
+
+ /**
+ * @classdesc a Port Detector. It executes the auto-detection of the port
+ * by the ownCloud server, if requirements are met.
+ *
+ * @constructor
+ */
+ var WizardDetectorPort = OCA.LDAP.Wizard.WizardDetectorGeneric.subClass({
+ /** @inheritdoc */
+ init: function() {
+ this.setTargetKey('ldap_port');
+ this.runsOnRequest = true;
+ },
+
+ /**
+ * runs the detector, if port is not set.
+ *
+ * @param {OCA.LDAP.Wizard.ConfigModel} model
+ * @param {string} configID - the configuration prefix
+ * @returns {boolean|jqXHR}
+ * @abstract
+ */
+ run: function(model, configID) {
+ model.notifyAboutDetectionStart('ldap_port');
+ var params = OC.buildQueryString({
+ action: 'guessPortAndTLS',
+ ldap_serverconfig_chooser: configID
+ });
+ return model.callWizard(params, this.processResult, this);
+ }
+ });
+
+ OCA.LDAP.Wizard.WizardDetectorPort = WizardDetectorPort;
+})();
diff --git a/apps/user_ldap/js/wizard/wizardDetectorQueue.js b/apps/user_ldap/js/wizard/wizardDetectorQueue.js
new file mode 100644
index 00000000000..b6fa644558e
--- /dev/null
+++ b/apps/user_ldap/js/wizard/wizardDetectorQueue.js
@@ -0,0 +1,89 @@
+
+/**
+ * Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+OCA = OCA || {};
+
+(function() {
+ /**
+ * @classdesc only run detector is allowed to run at a time. Basically
+ * because we cannot have parallel LDAP connections per session. This
+ * queue is takes care of running all the detectors one after the other.
+ *
+ * @constructor
+ */
+ var WizardDetectorQueue = OCA.LDAP.Wizard.WizardObject.subClass({
+ /**
+ * initializes the instance. Always call it after creating the instance.
+ */
+ init: function() {
+ this.queue = [];
+ this.isRunning = false;
+ },
+
+ /**
+ * empties the queue and cancels a possibly running request
+ */
+ reset: function() {
+ this.queue = [];
+ if(!_.isUndefined(this.runningRequest)) {
+ this.runningRequest.abort();
+ delete this.runningRequest;
+ }
+ this.isRunning = false;
+ },
+
+ /**
+ * a parameter-free callback that eventually executes the run method of
+ * the detector.
+ *
+ * @callback detectorCallBack
+ * @see OCA.LDAP.Wizard.ConfigModel._processSetResult
+ */
+
+ /**
+ * adds a detector to the queue and attempts to trigger to run the
+ * next job, because it might be the first.
+ *
+ * @param {detectorCallBack} callback
+ */
+ add: function(callback) {
+ this.queue.push(callback);
+ this.next();
+ },
+
+ /**
+ * Executes the next detector if none is running. This method is also
+ * automatically invoked after a detector finished.
+ */
+ next: function() {
+ if(this.isRunning === true || this.queue.length === 0) {
+ return;
+ }
+
+ this.isRunning = true;
+ var callback = this.queue.shift();
+ var request = callback();
+
+ // we receive either false or a jqXHR object
+ // false in case the detector decided against executing
+ if(request === false) {
+ this.isRunning = false;
+ this.next();
+ return;
+ }
+ this.runningRequest = request;
+
+ var detectorQueue = this;
+ $.when(request).then(function() {
+ detectorQueue.isRunning = false;
+ detectorQueue.next();
+ });
+ }
+ });
+
+ OCA.LDAP.Wizard.WizardDetectorQueue = WizardDetectorQueue;
+})();
diff --git a/apps/user_ldap/js/wizard/wizardDetectorSimpleRequestAbstract.js b/apps/user_ldap/js/wizard/wizardDetectorSimpleRequestAbstract.js
new file mode 100644
index 00000000000..37e41f42a64
--- /dev/null
+++ b/apps/user_ldap/js/wizard/wizardDetectorSimpleRequestAbstract.js
@@ -0,0 +1,44 @@
+
+/**
+ * Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+OCA = OCA || {};
+
+(function() {
+
+ /**
+ * @classdesc a Port Detector. It executes the auto-detection of the port
+ * by the ownCloud server, if requirements are met.
+ *
+ * @constructor
+ */
+ var WizardDetectorFilterSimpleRequestAbstract = OCA.LDAP.Wizard.WizardDetectorGeneric.subClass({
+ runsOnRequest: true,
+
+ /**
+ * runs the detector, if port is not set.
+ *
+ * @param {OCA.LDAP.Wizard.ConfigModel} model
+ * @param {string} configID - the configuration prefix
+ * @returns {boolean|jqXHR}
+ * @abstract
+ */
+ run: function(model, configID) {
+ if(_.isUndefined(this.wizardMethod)) {
+ console.warn('wizardMethod not set! ' + this.constructor);
+ return false;
+ }
+ model.notifyAboutDetectionStart(this.targetKey);
+ var params = OC.buildQueryString({
+ action: this.wizardMethod,
+ ldap_serverconfig_chooser: configID
+ });
+ return model.callWizard(params, this.processResult, this);
+ }
+ });
+
+ OCA.LDAP.Wizard.WizardDetectorFilterSimpleRequestAbstract = WizardDetectorFilterSimpleRequestAbstract;
+})();
diff --git a/apps/user_ldap/js/wizard/wizardDetectorTestAbstract.js b/apps/user_ldap/js/wizard/wizardDetectorTestAbstract.js
new file mode 100644
index 00000000000..df0b0a2200a
--- /dev/null
+++ b/apps/user_ldap/js/wizard/wizardDetectorTestAbstract.js
@@ -0,0 +1,63 @@
+
+/**
+ * Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+OCA = OCA || {};
+
+(function() {
+
+ /**
+ * @classdesc a Port Detector. It executes the auto-detection of the port
+ * by the ownCloud server, if requirements are met.
+ *
+ * @constructor
+ */
+ var WizardDetectorTestAbstract = OCA.LDAP.Wizard.WizardDetectorGeneric.subClass({
+ isLegacy: false,
+
+ /**
+ * runs the test
+ *
+ * @param {OCA.LDAP.Wizard.ConfigModel} model
+ * @param {string} configID - the configuration prefix
+ * @param {Object} params - additional parameters needed to send to the
+ * wizard
+ * @returns {boolean|jqXHR}
+ * @abstract
+ */
+ run: function(model, configID, params) {
+ if(_.isUndefined(this.wizardMethod) && !this.isLegacy) {
+ console.warn('wizardMethod not set! ' + this.constructor);
+ return false;
+ }
+ model.notifyAboutDetectionStart(this.getTargetKey());
+ params = params || {};
+ params = OC.buildQueryString($.extend({
+ action: this.wizardMethod,
+ ldap_serverconfig_chooser: configID
+ }, params));
+ if(!this.isLegacy) {
+ return model.callWizard(params, this.processResult, this);
+ } else {
+ return model.callAjax(this.legacyDestination, params, this.processResult, this);
+ }
+ },
+
+ /**
+ * @inheritdoc
+ */
+ processResult: function(model, detector, result) {
+ model['notifyAboutDetectionCompletion'](detector.getTargetKey());
+ var payload = {
+ feature: detector.testName,
+ data: result
+ };
+ model.inform(payload);
+ }
+ });
+
+ OCA.LDAP.Wizard.WizardDetectorTestAbstract = WizardDetectorTestAbstract;
+})();
diff --git a/apps/user_ldap/js/wizard/wizardDetectorTestBaseDN.js b/apps/user_ldap/js/wizard/wizardDetectorTestBaseDN.js
new file mode 100644
index 00000000000..52848819bd8
--- /dev/null
+++ b/apps/user_ldap/js/wizard/wizardDetectorTestBaseDN.js
@@ -0,0 +1,29 @@
+
+/**
+ * Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+OCA = OCA || {};
+
+(function() {
+
+ /**
+ * @classdesc Tests, how many objects reside in the given base DN(s)
+ *
+ * @constructor
+ */
+ var WizardDetectorTestBaseDN = OCA.LDAP.Wizard.WizardDetectorTestAbstract.subClass({
+ /** @inheritdoc */
+ init: function() {
+ // given, it is not a configuration key
+ this.setTargetKey('ldap_test_base');
+ this.testName = 'TestBaseDN';
+ this.wizardMethod = 'countInBaseDN';
+ this.runsOnRequest = true;
+ }
+ });
+
+ OCA.LDAP.Wizard.WizardDetectorTestBaseDN = WizardDetectorTestBaseDN;
+})();
diff --git a/apps/user_ldap/js/wizard/wizardDetectorTestConfiguration.js b/apps/user_ldap/js/wizard/wizardDetectorTestConfiguration.js
new file mode 100644
index 00000000000..1308c182909
--- /dev/null
+++ b/apps/user_ldap/js/wizard/wizardDetectorTestConfiguration.js
@@ -0,0 +1,31 @@
+
+/**
+ * Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+OCA = OCA || {};
+
+(function() {
+
+ /**
+ * @classdesc a Port Detector. It executes the auto-detection of the port
+ * by the ownCloud server, if requirements are met.
+ *
+ * @constructor
+ */
+ var WizardDetectorTestConfiguration = OCA.LDAP.Wizard.WizardDetectorTestAbstract.subClass({
+ /** @inheritdoc */
+ init: function() {
+ // given, it is not a configuration key
+ this.setTargetKey('ldap_action_test_connection');
+ this.testName = 'TestConfiguration';
+ this.isLegacy = true;
+ this.legacyDestination = 'testConfiguration.php';
+ this.runsOnRequest = true;
+ }
+ });
+
+ OCA.LDAP.Wizard.WizardDetectorTestConfiguration = WizardDetectorTestConfiguration;
+})();
diff --git a/apps/user_ldap/js/wizard/wizardDetectorTestLoginName.js b/apps/user_ldap/js/wizard/wizardDetectorTestLoginName.js
new file mode 100644
index 00000000000..260df5a0fe0
--- /dev/null
+++ b/apps/user_ldap/js/wizard/wizardDetectorTestLoginName.js
@@ -0,0 +1,30 @@
+
+/**
+ * Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+OCA = OCA || {};
+
+(function() {
+
+ /**
+ * @classdesc checks whether the provided log in name can be resolved into
+ * a DN using the current login filter
+ *
+ * @constructor
+ */
+ var WizardDetectorTestLoginName = OCA.LDAP.Wizard.WizardDetectorTestAbstract.subClass({
+ /** @inheritdoc */
+ init: function() {
+ // given, it is not a configuration key
+ this.setTargetKey('ldap_test_loginname');
+ this.testName = 'TestLoginName';
+ this.wizardMethod = 'testLoginName';
+ this.runsOnRequest = true;
+ }
+ });
+
+ OCA.LDAP.Wizard.WizardDetectorTestLoginName = WizardDetectorTestLoginName;
+})();
diff --git a/apps/user_ldap/js/wizard/wizardDetectorUserCount.js b/apps/user_ldap/js/wizard/wizardDetectorUserCount.js
new file mode 100644
index 00000000000..bcff2cf3b10
--- /dev/null
+++ b/apps/user_ldap/js/wizard/wizardDetectorUserCount.js
@@ -0,0 +1,26 @@
+
+/**
+ * Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+OCA = OCA || {};
+
+(function() {
+
+ /**
+ * @classdesc let's the wizard backend count the available users
+ *
+ * @constructor
+ */
+ var WizardDetectorUserCount = OCA.LDAP.Wizard.WizardDetectorFilterSimpleRequestAbstract.subClass({
+ init: function() {
+ this.setTargetKey('ldap_user_count');
+ this.wizardMethod = 'countUsers';
+ this.runsOnRequest = true;
+ }
+ });
+
+ OCA.LDAP.Wizard.WizardDetectorUserCount = WizardDetectorUserCount;
+})();
diff --git a/apps/user_ldap/js/wizard/wizardDetectorUserDisplayNameAttribute.js b/apps/user_ldap/js/wizard/wizardDetectorUserDisplayNameAttribute.js
new file mode 100644
index 00000000000..ae734480c1c
--- /dev/null
+++ b/apps/user_ldap/js/wizard/wizardDetectorUserDisplayNameAttribute.js
@@ -0,0 +1,39 @@
+
+/**
+ * Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+OCA = OCA || {};
+
+(function() {
+
+ /**
+ * @classdesc let's the wizard backend count the available users
+ *
+ * @constructor
+ */
+ var WizardDetectorUserDisplayNameAttribute = OCA.LDAP.Wizard.WizardDetectorFilterSimpleRequestAbstract.subClass({
+ init: function() {
+ this.setTargetKey('ldap_user_count');
+ this.wizardMethod = 'detectUserDisplayNameAttribute';
+ this.runsOnRequest = true;
+ },
+
+ /**
+ * @inheritdoc
+ */
+ run: function(model, configID) {
+ // default value has capital N. Detected values are always lowercase
+ if(model.configuration.ldap_display_name && model.configuration.ldap_display_name !== 'displayName') {
+ // a value is already set. Don't overwrite and don't ask LDAP
+ // without reason.
+ return false;
+ }
+ this._super(model, configID);
+ }
+ });
+
+ OCA.LDAP.Wizard.WizardDetectorUserDisplayNameAttribute = WizardDetectorUserDisplayNameAttribute;
+})();
diff --git a/apps/user_ldap/js/wizard/wizardDetectorUserGroupAssociation.js b/apps/user_ldap/js/wizard/wizardDetectorUserGroupAssociation.js
new file mode 100644
index 00000000000..953a0b909a6
--- /dev/null
+++ b/apps/user_ldap/js/wizard/wizardDetectorUserGroupAssociation.js
@@ -0,0 +1,40 @@
+
+/**
+ * Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+OCA = OCA || {};
+
+(function() {
+
+ /**
+ * @classdesc let's the wizard backend count the available users
+ *
+ * @constructor
+ */
+ var WizardDetectorUserGroupAssociation = OCA.LDAP.Wizard.WizardDetectorFilterSimpleRequestAbstract.subClass({
+ init: function() {
+ this.setTargetKey('ldap_group_count');
+ this.wizardMethod = 'determineGroupMemberAssoc';
+ this.runsOnRequest = true;
+ },
+
+ /**
+ * @inheritdoc
+ */
+ run: function(model, configID) {
+ // TODO: might be better with configuration marker as uniqueMember
+ // is a valid value (although probably less common then member and memberUid).
+ if(model.configuration.ldap_group_member_assoc_attribute && model.configuration.ldap_group_member_assoc_attribute !== 'uniqueMember') {
+ // a value is already set. Don't overwrite and don't ask LDAP
+ // without reason.
+ return false;
+ }
+ this._super(model, configID);
+ }
+ });
+
+ OCA.LDAP.Wizard.WizardDetectorUserGroupAssociation = WizardDetectorUserGroupAssociation;
+})();
diff --git a/apps/user_ldap/js/wizard/wizardDetectorUserObjectClasses.js b/apps/user_ldap/js/wizard/wizardDetectorUserObjectClasses.js
new file mode 100644
index 00000000000..0fa324a0809
--- /dev/null
+++ b/apps/user_ldap/js/wizard/wizardDetectorUserObjectClasses.js
@@ -0,0 +1,29 @@
+
+/**
+ * Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+OCA = OCA || {};
+
+(function() {
+
+ /**
+ * @classdesc discovers object classes for the users tab
+ *
+ * @constructor
+ */
+ var WizardDetectorUserObjectClasses = OCA.LDAP.Wizard.WizardDetectorFeatureAbstract.subClass({
+ /** @inheritdoc */
+ init: function() {
+ // given, it is not a configuration key
+ this.setTargetKey('ldap_userfilter_objectclass');
+ this.wizardMethod = 'determineUserObjectClasses';
+ this.featureName = 'UserObjectClasses';
+ this.runsOnRequest = true;
+ }
+ });
+
+ OCA.LDAP.Wizard.WizardDetectorUserObjectClasses = WizardDetectorUserObjectClasses;
+})();
diff --git a/apps/user_ldap/js/wizard/wizardFilterOnType.js b/apps/user_ldap/js/wizard/wizardFilterOnType.js
new file mode 100644
index 00000000000..bb1871023a2
--- /dev/null
+++ b/apps/user_ldap/js/wizard/wizardFilterOnType.js
@@ -0,0 +1,76 @@
+/**
+ * Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+OCA = OCA || {};
+
+(function() {
+
+ /**
+ * @classdesc filters a select box when a text element is typed in
+ */
+ var FilterOnType = OCA.LDAP.Wizard.WizardObject.subClass({
+ /**
+ * initializes a type filter on a text input for a select element
+ *
+ * @param {jQuery} $select
+ * @param {jQuery} $textInput
+ */
+ init: function($select, $textInput) {
+ this.$select = $select;
+ this.$textInput = $textInput;
+ this.updateOptions();
+ this.lastSearch = '';
+
+ var fity = this;
+ $textInput.bind('change keyup', function () {
+ if(fity.runID) {
+ window.clearTimeout(fity.runID);
+ }
+ fity.runID = window.setTimeout(function() {
+ fity.filter(fity);
+ }, 250);
+ });
+ },
+
+ /**
+ * the options will be read in again. Should be called after a
+ * configuration switch.
+ */
+ updateOptions: function() {
+ var options = [];
+ this.$select.find('option').each(function() {
+ options.push({
+ value: $(this).val(),
+ normalized: $(this).val().toLowerCase()
+ }
+ );
+ });
+ this._options = options;
+ },
+
+ /**
+ * the actual search or filter method
+ *
+ * @param {FilterOnType} fity
+ */
+ filter: function(fity) {
+ var filterVal = fity.$textInput.val().toLowerCase();
+ if(filterVal === fity.lastSearch) {
+ return;
+ }
+ fity.lastSearch = filterVal;
+ fity.$select.empty();
+ $.each(fity._options, function() {
+ if(!filterVal || this.normalized.indexOf(filterVal) > -1) {
+ fity.$select.append($('<option>').val(this.value).text(this.value));
+ }
+ });
+ delete(fity.runID);
+ }
+ });
+
+ OCA.LDAP.Wizard.FilterOnType = FilterOnType;
+})();
diff --git a/apps/user_ldap/js/wizard/wizardFilterOnTypeFactory.js b/apps/user_ldap/js/wizard/wizardFilterOnTypeFactory.js
new file mode 100644
index 00000000000..bd6511dc8b0
--- /dev/null
+++ b/apps/user_ldap/js/wizard/wizardFilterOnTypeFactory.js
@@ -0,0 +1,27 @@
+/**
+ * Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+OCA = OCA || {};
+
+(function() {
+
+ /**
+ * @classdesc creates instances of OCA.LDAP.Wizard.FilterOnType upon request
+ */
+ var FilterOnTypeFactory = OCA.LDAP.Wizard.WizardObject.subClass({
+ /**
+ * initializes a type filter on a text input for a select element
+ *
+ * @param {jQuery} $select
+ * @param {jQuery} $textInput
+ */
+ get: function($select, $textInput) {
+ return new OCA.LDAP.Wizard.FilterOnType($select, $textInput);
+ }
+ });
+
+ OCA.LDAP.Wizard.FilterOnTypeFactory = FilterOnTypeFactory;
+})();
diff --git a/apps/user_ldap/js/wizard/wizardObject.js b/apps/user_ldap/js/wizard/wizardObject.js
new file mode 100644
index 00000000000..a90f1533a26
--- /dev/null
+++ b/apps/user_ldap/js/wizard/wizardObject.js
@@ -0,0 +1,60 @@
+
+/**
+ * Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+OCA = OCA || {};
+
+(function() {
+ var initializing = false;
+ var superPattern = /xyz/.test(function() { xyz; }) ? /\b_super\b/ : /.*/;
+
+ /**
+ * @classdesc a base class that allows inheritance
+ *
+ * @abstrcact
+ * @constructor
+ */
+ var WizardObject = function(){};
+ WizardObject.subClass = function(properties) {
+ var _super = this.prototype;
+
+ initializing = true;
+ var proto = new this();
+ initializing = false;
+
+ for (var name in properties) {
+ proto[name] =
+ typeof properties[name] === "function" &&
+ typeof _super[name] === 'function' &&
+ superPattern.test(properties[name]) ?
+ (function (name, fn) {
+ return function () {
+ var tmp = this._super;
+ this._super = _super[name];
+ var ret = fn.apply(this, arguments);
+ this._super = tmp;
+ return ret;
+ };
+ })(name, properties[name]) :
+ properties[name];
+ };
+
+ function Class() {
+ if(!initializing && this.init) {
+ this.init.apply(this, arguments);
+ }
+ }
+
+ Class.prototype = proto;
+ Class.constructor = Class;
+ Class.subClass = arguments.callee;
+ return Class;
+ };
+
+ WizardObject.constructor = WizardObject;
+
+ OCA.LDAP.Wizard.WizardObject = WizardObject;
+})();
diff --git a/apps/user_ldap/js/wizard/wizardTabAbstractFilter.js b/apps/user_ldap/js/wizard/wizardTabAbstractFilter.js
new file mode 100644
index 00000000000..024b6af65d0
--- /dev/null
+++ b/apps/user_ldap/js/wizard/wizardTabAbstractFilter.js
@@ -0,0 +1,378 @@
+/**
+ * Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+OCA = OCA || {};
+
+(function() {
+
+ /**
+ * @classdesc This class represents the view belonging to the server tab
+ * in the LDAP wizard.
+ */
+ var WizardTabAbstractFilter = OCA.LDAP.Wizard.WizardTabGeneric.subClass({
+ /**
+ * @property {number} number that needs to exceeded to use complex group
+ * selection element
+ */
+ _groupElementSwitchThreshold: 40,
+
+ /**
+ * @property {boolean} - tells whether multiselect or complex element is
+ * used for selecting groups
+ */
+ isComplexGroupChooser: false,
+
+ /** @property {string} */
+ tabID: '',
+
+ /**
+ * initializes the instance. Always call it after initialization.
+ * concrete view must set managed items first, and then call the parent
+ * init.
+ *
+ * @param {OCA.LDAP.Wizard.FilterOnTypeFactory} fotf
+ * @param {number} [tabIndex]
+ * @param {string} [tabID]
+ */
+ init: function (fotf, tabIndex, tabID) {
+ this._super(tabIndex, tabID);
+
+ /** @type {OCA.LDAP.Wizard.FilterOnTypeFactory} */
+ this.foTFactory = fotf;
+ this._initMultiSelect(
+ this.getGroupsItem().$element,
+ t('user_ldap', 'Select groups')
+ );
+ this._initMultiSelect(
+ this.getObjectClassItem().$element,
+ t('user_ldap', 'Select object classes')
+ );
+ this.filterName = this.getFilterItem().keyName;
+ this._initFilterModeSwitcher(
+ this.getToggleItem().$element,
+ this.getRawFilterContainerItem().$element,
+ [ this.getObjectClassItem().$element ],
+ this.getFilterModeKey(),
+ {
+ status: 'disabled',
+ $element: this.getGroupsItem().$element
+ }
+ );
+ _.bindAll(this, 'onCountButtonClick', 'onSelectGroup', 'onDeselectGroup');
+ this.getCountItem().$relatedElements.click(this.onCountButtonClick);
+ if(this.manyGroupsSupport) {
+ var $selectBtn = $(this.tabID).find('.ldapGroupListSelect');
+ $selectBtn.click(this.onSelectGroup);
+ var $deselectBtn = $(this.tabID).find('.ldapGroupListDeselect');
+ $deselectBtn.click(this.onDeselectGroup);
+ }
+ },
+
+ /**
+ * returns managed item for the object class chooser. must be
+ * implemented by concrete view
+ */
+ getObjectClassItem: function () {},
+
+ /**
+ * returns managed item for the group chooser. must be
+ * implemented by concrete view
+ */
+ getGroupsItem: function () {},
+
+ /**
+ * returns managed item for the effective filter. must be
+ * implemented by concrete view
+ */
+ getFilterItem: function () {},
+
+ /**
+ * returns managed item for the toggle element. must be
+ * implemented by concrete view
+ */
+ getToggleItem: function () {},
+
+ /**
+ * returns managed item for the raw filter container. must be
+ * implemented by concrete view
+ */
+ getRawFilterContainerItem: function () {},
+
+ /**
+ * returns managed item for the count control. must be
+ * implemented by concrete view
+ */
+ getCountItem: function () {},
+
+ /**
+ * returns name of the filter mode key. must be implemented by concrete
+ * view
+ */
+ getFilterModeKey: function () {},
+
+ /**
+ * Sets the config model for this view and subscribes to some events.
+ * Also binds the config chooser to the model
+ *
+ * @param {OCA.LDAP.Wizard.ConfigModel} configModel
+ */
+ setModel: function(configModel) {
+ this._super(configModel);
+ this.configModel.on('configLoaded', this.onConfigSwitch, this);
+ this.configModel.on('receivedLdapFeature', this.onFeatureReceived, this);
+ },
+
+ /**
+ * @inheritdoc
+ */
+ _setFilterModeAssisted: function () {
+ this._super();
+ if(this.isComplexGroupChooser) {
+ this.enableElement(this.getGroupsItem().$relatedElements);
+ }
+ },
+
+ /**
+ * @inheritdoc
+ */
+ _setFilterModeRaw: function () {
+ this._super();
+ if(this.manyGroupsSupport) {
+ this.disableElement(this.getGroupsItem().$relatedElements);
+ }
+ },
+
+ /**
+ * sets the selected user object classes
+ *
+ * @param {Array} classes
+ */
+ setObjectClass: function(classes) {
+ this.setElementValue(this.getObjectClassItem().$element, classes);
+ this.getObjectClassItem().$element.multiselect('refresh');
+ },
+
+ /**
+ * sets the selected groups
+ *
+ * @param {Array} groups
+ */
+ setGroups: function(groups) {
+ if(!this.isComplexGroupChooser) {
+ this.setElementValue(this.getGroupsItem().$element, groups);
+ this.getGroupsItem().$element.multiselect('refresh');
+ } else {
+ var $element = $(this.tabID).find('.ldapGroupListSelected');
+ this.equipMultiSelect($element, groups);
+ }
+ },
+
+ /**
+ * sets the filter
+ *
+ * @param {string} filter
+ */
+ setFilter: function(filter) {
+ this.setElementValue(this.getFilterItem().$element, filter);
+ this.$filterModeRawContainer.siblings('.ldapReadOnlyFilterContainer').find('.ldapFilterReadOnlyElement').text(filter);
+ },
+
+ /**
+ * sets the user count string
+ *
+ * @param {string} countInfo
+ */
+ setCount: function(countInfo) {
+ this.setElementValue(this.getCountItem().$element, countInfo);
+ },
+
+ /**
+ * @inheritdoc
+ */
+ considerFeatureRequests: function() {
+ if(!this.isActive) {
+ return;
+ }
+ if(this.getObjectClassItem().$element.find('option').length === 0) {
+ this.disableElement(this.getObjectClassItem().$element);
+ this.disableElement(this.getGroupsItem().$element);
+ if(this.parsedFilterMode === this.configModel.FILTER_MODE_ASSISTED) {
+ this.configModel.requestWizard(this.getObjectClassItem().keyName);
+ this.configModel.requestWizard(this.getGroupsItem().keyName);
+ }
+ }
+ },
+
+ /**
+ * updates (creates, if necessary) filterOnType instances
+ *
+ * @param {string} [only] - if only one search index should be updated
+ */
+ updateFilterOnType: function(only) {
+ if(_.isUndefined(this.filterOnType)) {
+ this.filterOnType = [];
+
+ var $availableGroups = $(this.tabID).find('.ldapGroupListAvailable');
+ this.filterOnType.push(this.foTFactory.get(
+ $availableGroups, $(this.tabID).find('.ldapManyGroupsSearch')
+ ));
+ var $selectedGroups = $(this.tabID).find('.ldapGroupListSelected');
+ this.filterOnType.push(this.foTFactory.get(
+ $selectedGroups, $(this.tabID).find('.ldapManyGroupsSearch')
+ ));
+ } else {
+ if(_.isUndefined || only.toLowerCase() === 'available') {
+ this.filterOnType[0].updateOptions();
+ }
+ if(_.isUndefined || only.toLowerCase() === 'selected') {
+ this.filterOnType[1].updateOptions();
+ }
+ }
+ },
+
+ /**
+ * @inheritdoc
+ */
+ onActivate: function() {
+ this.considerFeatureRequests();
+ },
+
+ /**
+ * resets the view when a configuration switch happened.
+ *
+ * @param {WizardTabAbstractFilter} view
+ * @param {Object} configuration
+ */
+ onConfigSwitch: function(view, configuration) {
+ view.getObjectClassItem().$element.find('option').remove();
+ view.getGroupsItem().$element.find('option').remove();
+ view.getCountItem().$element.text('');
+ $(view.tabID).find('.ldapGroupListAvailable').empty();
+ $(view.tabID).find('.ldapGroupListSelected').empty();
+ view.updateFilterOnType();
+ $(view.tabID).find('.ldapManyGroupsSearch').val('');
+
+ if(view.isComplexGroupChooser) {
+ view.isComplexGroupChooser = false;
+ view.getGroupsItem().$element.multiselect({classes: view.multiSelectPluginClass});
+ $(view.tabID).find(".ldapManyGroupsSupport").addClass('hidden');
+ }
+
+ view.onConfigLoaded(view, configuration);
+ },
+
+ /**
+ * @inheritdoc
+ */
+ onConfigLoaded: function(view, configuration) {
+ for(var key in view.managedItems){
+ if(!_.isUndefined(configuration[key])) {
+ var value = configuration[key];
+ var methodName = view.managedItems[key].setMethod;
+ if(!_.isUndefined(view[methodName])) {
+ view[methodName](value);
+ // we reimplement it here to update the filter index
+ // for groups. Maybe we can isolate it?
+ if(methodName === 'setGroups') {
+ view.updateFilterOnType('selected');
+ }
+ }
+ }
+ }
+ },
+
+ /**
+ * if UserObjectClasses are found, the corresponding element will be
+ * updated
+ *
+ * @param {WizardTabAbstractFilter} view
+ * @param {FeaturePayload} payload
+ */
+ onFeatureReceived: function(view, payload) {
+ if(payload.feature === view.getObjectClassItem().featureName) {
+ view.equipMultiSelect(view.getObjectClassItem().$element, payload.data);
+ if( !view.getFilterItem().$element.val()
+ && view.parsedFilterMode === view.configModel.FILTER_MODE_ASSISTED
+ ) {
+ view.configModel.requestWizard(view.getFilterItem().keyName)
+ }
+ } else if (payload.feature === view.getGroupsItem().featureName) {
+ if(view.manyGroupsSupport && payload.data.length > view._groupElementSwitchThreshold) {
+ // we need to fill the left list box, excluding the values
+ // that are already selected
+ var $element = $(view.tabID).find('.ldapGroupListAvailable');
+ var selected = view.configModel.configuration[view.getGroupsItem().keyName];
+ var available = $(payload.data).not(selected).get();
+ view.equipMultiSelect($element, available);
+ view.updateFilterOnType('available');
+ $(view.tabID).find(".ldapManyGroupsSupport").removeClass('hidden');
+ view.getGroupsItem().$element.multiselect({classes: view.multiSelectPluginClass + ' forceHidden'});
+ view.isComplexGroupChooser = true;
+ } else {
+ view.isComplexGroupChooser = false;
+ view.equipMultiSelect(view.getGroupsItem().$element, payload.data);
+ view.getGroupsItem().$element.multiselect({classes: view.multiSelectPluginClass});
+ $(view.tabID).find(".ldapManyGroupsSupport").addClass('hidden');
+
+ }
+ }
+ },
+
+ /**
+ * request to count the users with the current filter
+ *
+ * @param {Event} event
+ */
+ onCountButtonClick: function(event) {
+ event.preventDefault();
+ // let's clear the field
+ this.getCountItem().$element.text('');
+ this.configModel.requestWizard(this.getCountItem().keyName);
+ },
+
+ /**
+ * saves groups when using the complex UI
+ *
+ * @param {Array} groups
+ * @returns {boolean}
+ * @private
+ */
+ _saveGroups: function(groups) {
+ var toSave = '';
+ $(groups).each(function() { toSave = toSave + "\n" + this; } );
+ this.configModel.set(this.getGroupsItem().keyName, $.trim(toSave));
+ },
+
+ /**
+ * acts on adding groups to the filter
+ */
+ onSelectGroup: function() {
+ var $available = $(this.tabID).find('.ldapGroupListAvailable');
+ var $selected = $(this.tabID).find('.ldapGroupListSelected');
+ var selected = $.map($selected.find('option'), function(e) { return e.value; });
+
+ this._saveGroups(selected.concat($available.val()));
+ $available.find('option:selected').prependTo($selected);
+ this.updateFilterOnType();
+ },
+
+ /**
+ * acts on removing groups to the filter
+ */
+ onDeselectGroup: function() {
+ var $available = $(this.tabID).find('.ldapGroupListAvailable');
+ var $selected = $(this.tabID).find('.ldapGroupListSelected');
+ var selected = $.map($selected.find('option:not(:selected)'), function(e) { return e.value; });
+
+ this._saveGroups(selected);
+ $selected.find('option:selected').appendTo($available);
+ this.updateFilterOnType();
+ }
+
+ });
+
+ OCA.LDAP.Wizard.WizardTabAbstractFilter = WizardTabAbstractFilter;
+})();
diff --git a/apps/user_ldap/js/wizard/wizardTabAdvanced.js b/apps/user_ldap/js/wizard/wizardTabAdvanced.js
new file mode 100644
index 00000000000..a27ec87b7c4
--- /dev/null
+++ b/apps/user_ldap/js/wizard/wizardTabAdvanced.js
@@ -0,0 +1,330 @@
+
+/**
+ * Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+OCA = OCA || {};
+
+(function() {
+
+ /**
+ * @classdesc This class represents the view belonging to the advanced tab
+ * in the LDAP wizard.
+ */
+ var WizardTabAdvanced = OCA.LDAP.Wizard.WizardTabGeneric.subClass({
+ /**
+ * initializes the instance. Always call it after initialization.
+ *
+ * @param tabIndex
+ * @param tabID
+ */
+ init: function (tabIndex, tabID) {
+ this._super(tabIndex, tabID);
+
+ var items = {
+ // Connection settings
+ ldap_configuration_active: {
+ $element: $('#ldap_configuration_active'),
+ setMethod: 'setConfigurationState'
+ },
+ ldap_backup_host: {
+ $element: $('#ldap_backup_host'),
+ setMethod: 'setBackupHost'
+ },
+ ldap_backup_port: {
+ $element: $('#ldap_backup_port'),
+ setMethod: 'setBackupPort'
+ },
+ ldap_override_main_server: {
+ $element: $('#ldap_override_main_server'),
+ setMethod: 'setOverrideMainServerState'
+ },
+ ldap_nocase: {
+ $element: $('#ldap_nocase'),
+ setMethod: 'setNoCase'
+ },
+ ldap_turn_off_cert_check: {
+ $element: $('#ldap_turn_off_cert_check'),
+ setMethod: 'setCertCheckDisabled'
+ },
+ ldap_cache_ttl: {
+ $element: $('#ldap_cache_ttl'),
+ setMethod: 'setCacheTTL'
+ },
+
+ //Directory Settings
+ ldap_display_name: {
+ $element: $('#ldap_display_name'),
+ setMethod: 'setUserDisplayName'
+ },
+ ldap_base_users: {
+ $element: $('#ldap_base_users'),
+ setMethod: 'setBaseDNUsers'
+ },
+ ldap_attributes_for_user_search: {
+ $element: $('#ldap_attributes_for_user_search'),
+ setMethod: 'setSearchAttributesUsers'
+ },
+ ldap_group_display_name: {
+ $element: $('#ldap_group_display_name'),
+ setMethod: 'setGroupDisplayName'
+ },
+ ldap_base_groups: {
+ $element: $('#ldap_base_groups'),
+ setMethod: 'setBaseDNGroups'
+ },
+ ldap_attributes_for_group_search: {
+ $element: $('#ldap_attributes_for_group_search'),
+ setMethod: 'setSearchAttributesGroups'
+ },
+ ldap_group_member_assoc_attribute: {
+ $element: $('#ldap_group_member_assoc_attribute'),
+ setMethod: 'setGroupMemberAssociationAttribute'
+ },
+ ldap_nested_groups: {
+ $element: $('#ldap_nested_groups'),
+ setMethod: 'setUseNestedGroups'
+ },
+ ldap_paging_size: {
+ $element: $('#ldap_paging_size'),
+ setMethod: 'setPagingSize'
+ },
+
+ //Special Attributes
+ ldap_quota_attr: {
+ $element: $('#ldap_quota_attr'),
+ setMethod: 'setQuotaAttribute'
+ },
+ ldap_quota_def: {
+ $element: $('#ldap_quota_def'),
+ setMethod: 'setQuotaDefault'
+ },
+ ldap_email_attr: {
+ $element: $('#ldap_email_attr'),
+ setMethod: 'setEmailAttribute'
+ },
+ home_folder_naming_rule: {
+ $element: $('#home_folder_naming_rule'),
+ setMethod: 'setHomeFolderAttribute'
+ }
+ };
+ this.setManagedItems(items);
+ },
+
+ /**
+ * Sets the config model for this view and subscribes to some events.
+ * Also binds the config chooser to the model
+ *
+ * @param {OCA.LDAP.Wizard.ConfigModel} configModel
+ */
+ setModel: function(configModel) {
+ this._super(configModel);
+ this.configModel.on('configLoaded', this.onConfigLoaded, this);
+ this.configModel.on('receivedLdapFeature', this.onResultReceived, this);
+ },
+
+ /**
+ * updates the experienced admin check box
+ *
+ * @param {string} isConfigActive contains an int
+ */
+ setConfigurationState: function(isConfigActive) {
+ this.setElementValue(
+ this.managedItems.ldap_configuration_active.$element, isConfigActive
+ );
+ },
+
+ /**
+ * updates the backup host configuration text field
+ *
+ * @param {string} host
+ */
+ setBackupHost: function(host) {
+ this.setElementValue(this.managedItems.ldap_backup_host.$element, host);
+ },
+
+ /**
+ * updates the backup port configuration text field
+ *
+ * @param {string} port
+ */
+ setBackupPort: function(port) {
+ this.setElementValue(this.managedItems.ldap_backup_port.$element, port);
+ },
+
+ /**
+ * sets whether the main server should be overridden or not
+ *
+ * @param {string} doOverride contains an int
+ */
+ setOverrideMainServerState: function(doOverride) {
+ this.setElementValue(
+ this.managedItems.ldap_override_main_server.$element, doOverride
+ );
+ },
+
+ /**
+ * whether the server is case insensitive. This setting does not play
+ * a role anymore (probably never had).
+ *
+ * @param {string} noCase contains an int
+ */
+ setNoCase: function(noCase) {
+ this.setElementValue(this.managedItems.ldap_nocase.$element, noCase);
+ },
+
+ /**
+ * sets whether the SSL/TLS certification check shout be disabled
+ *
+ * @param {string} doCertCheck contains an int
+ */
+ setCertCheckDisabled: function(doCertCheck) {
+ this.setElementValue(
+ this.managedItems.ldap_turn_off_cert_check.$element, doCertCheck
+ );
+ },
+
+ /**
+ * sets the time-to-live of the LDAP cache (in seconds)
+ *
+ * @param {string} cacheTTL contains an int
+ */
+ setCacheTTL: function(cacheTTL) {
+ this.setElementValue(this.managedItems.ldap_cache_ttl.$element, cacheTTL);
+ },
+
+ /**
+ * sets the user display name attribute
+ *
+ * @param {string} attribute
+ */
+ setUserDisplayName: function(attribute) {
+ this.setElementValue(this.managedItems.ldap_display_name.$element, attribute);
+ },
+
+ /**
+ * sets the Base DN for users
+ *
+ * @param {string} base
+ */
+ setBaseDNUsers: function(base) {
+ this.setElementValue(this.managedItems.ldap_base_users.$element, base);
+ },
+
+ /**
+ * sets the attributes for user searches
+ *
+ * @param {string} attributes
+ */
+ setSearchAttributesUsers: function(attributes) {
+ this.setElementValue(this.managedItems.ldap_attributes_for_user_search.$element, attributes);
+ },
+
+ /**
+ * sets the display name attribute for groups
+ *
+ * @param {string} attribute
+ */
+ setGroupDisplayName: function(attribute) {
+ this.setElementValue(this.managedItems.ldap_group_display_name.$element, attribute);
+ },
+
+ /**
+ * sets the Base DN for groups
+ *
+ * @param {string} base
+ */
+ setBaseDNGroups: function(base) {
+ this.setElementValue(this.managedItems.ldap_base_groups.$element, base);
+ },
+
+ /**
+ * sets the attributes for group search
+ *
+ * @param {string} attributes
+ */
+ setSearchAttributesGroups: function(attributes) {
+ this.setElementValue(this.managedItems.ldap_attributes_for_group_search.$element, attributes);
+ },
+
+ /**
+ * sets the attribute for the association of users and groups
+ *
+ * @param {string} attribute
+ */
+ setGroupMemberAssociationAttribute: function(attribute) {
+ this.setElementValue(this.managedItems.ldap_group_member_assoc_attribute.$element, attribute);
+ },
+
+ /**
+ * enabled or disables the use of nested groups (groups in groups in
+ * groups…)
+ *
+ * @param {string} useNestedGroups contains an int
+ */
+ setUseNestedGroups: function(useNestedGroups) {
+ this.setElementValue(this.managedItems.ldap_nested_groups.$element, useNestedGroups);
+ },
+
+ /**
+ * sets the size of pages for paged search
+ *
+ * @param {string} size contains an int
+ */
+ setPagingSize: function(size) {
+ this.setElementValue(this.managedItems.ldap_paging_size.$element, size);
+ },
+
+ /**
+ * sets the email attribute
+ *
+ * @param {string} attribute
+ */
+ setEmailAttribute: function(attribute) {
+ this.setElementValue(this.managedItems.ldap_email_attr.$element, attribute);
+ },
+
+ /**
+ * sets the quota attribute
+ *
+ * @param {string} attribute
+ */
+ setQuotaAttribute: function(attribute) {
+ this.setElementValue(this.managedItems.ldap_quota_attr.$element, attribute);
+ },
+
+ /**
+ * sets the default quota for LDAP users
+ *
+ * @param {string} quota contains an int
+ */
+ setQuotaDefault: function(quota) {
+ this.setElementValue(this.managedItems.ldap_quota_def.$element, quota);
+ },
+
+ /**
+ * sets the attribute for the ownCloud user specific home folder location
+ *
+ * @param {string} attribute
+ */
+ setHomeFolderAttribute: function(attribute) {
+ this.setElementValue(this.managedItems.home_folder_naming_rule.$element, attribute);
+ },
+
+ /**
+ * deals with the result of the Test Connection test
+ *
+ * @param {WizardTabAdvanced} view
+ * @param {FeaturePayload} payload
+ */
+ onResultReceived: function(view, payload) {
+ if(payload.feature === 'TestConfiguration') {
+ OC.Notification.showTemporary(payload.data.message);
+ }
+ }
+ });
+
+ OCA.LDAP.Wizard.WizardTabAdvanced = WizardTabAdvanced;
+})();
diff --git a/apps/user_ldap/js/wizard/wizardTabElementary.js b/apps/user_ldap/js/wizard/wizardTabElementary.js
new file mode 100644
index 00000000000..c7767b9cf66
--- /dev/null
+++ b/apps/user_ldap/js/wizard/wizardTabElementary.js
@@ -0,0 +1,347 @@
+
+/**
+ * Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+OCA = OCA || {};
+
+(function() {
+
+ /**
+ * @classdesc This class represents the view belonging to the server tab
+ * in the LDAP wizard.
+ */
+ var WizardTabElementary = OCA.LDAP.Wizard.WizardTabGeneric.subClass({
+ /**
+ * initializes the instance. Always call it after initialization.
+ *
+ * @param tabIndex
+ * @param tabID
+ */
+ init: function (tabIndex, tabID) {
+ tabIndex = 0;
+ this._super(tabIndex, tabID);
+ this.isActive = true;
+ this.configChooserID = '#ldap_serverconfig_chooser';
+
+ var items = {
+ ldap_host: {
+ $element: $('#ldap_host'),
+ setMethod: 'setHost'
+ },
+ ldap_port: {
+ $element: $('#ldap_port'),
+ setMethod: 'setPort',
+ $relatedElements: $('.ldapDetectPort')
+ },
+ ldap_dn: {
+ $element: $('#ldap_dn'),
+ setMethod: 'setAgentDN'
+ },
+ ldap_agent_password: {
+ $element: $('#ldap_agent_password'),
+ setMethod: 'setAgentPwd'
+ },
+ ldap_base: {
+ $element: $('#ldap_base'),
+ setMethod: 'setBase',
+ $relatedElements: $('.ldapDetectBase, .ldapTestBase'),
+ $detectButton: $('.ldapDetectBase'),
+ $testButton: $('.ldapTestBase')
+ },
+ ldap_base_test: {
+ $element: $('#ldap_base')
+ },
+ ldap_experienced_admin: {
+ $element: $('#ldap_experienced_admin'),
+ setMethod: 'setExperiencedAdmin'
+ }
+ };
+ this.setManagedItems(items);
+ _.bindAll(this, 'onPortButtonClick', 'onBaseDNButtonClick', 'onBaseDNTestButtonClick');
+ this.managedItems.ldap_port.$relatedElements.click(this.onPortButtonClick);
+ this.managedItems.ldap_base.$detectButton.click(this.onBaseDNButtonClick);
+ this.managedItems.ldap_base.$testButton.click(this.onBaseDNTestButtonClick);
+ },
+
+ /**
+ * Sets the config model for this view and subscribes to some events.
+ * Also binds the config chooser to the model
+ *
+ * @param {OCA.LDAP.Wizard.ConfigModel} configModel
+ */
+ setModel: function(configModel) {
+ this._super(configModel);
+ this.configModel.on('configLoaded', this.onConfigSwitch, this);
+ this.configModel.on('newConfiguration', this.onNewConfiguration, this);
+ this.configModel.on('deleteConfiguration', this.onDeleteConfiguration, this);
+ this.configModel.on('receivedLdapFeature', this.onTestResultReceived, this);
+ this._enableConfigChooser();
+ this._enableConfigButtons();
+ },
+
+ /**
+ * returns the currently selected configuration ID
+ *
+ * @returns {string}
+ */
+ getConfigID: function() {
+ return $(this.configChooserID).val();
+ },
+
+ /**
+ * updates the host configuration text field
+ *
+ * @param {string} host
+ */
+ setHost: function(host) {
+ this.setElementValue(this.managedItems.ldap_host.$element, host);
+ if(host) {
+ this.enableElement(this.managedItems.ldap_port.$relatedElements);
+ } else {
+ this.disableElement(this.managedItems.ldap_port.$relatedElements);
+ }
+ },
+
+ /**
+ * updates the port configuration text field
+ *
+ * @param {string} port
+ */
+ setPort: function(port) {
+ this.setElementValue(this.managedItems.ldap_port.$element, port);
+ },
+
+ /**
+ * updates the user (agent) DN text field
+ *
+ * @param {string} agentDN
+ */
+ setAgentDN: function(agentDN) {
+ this.setElementValue(this.managedItems.ldap_dn.$element, agentDN);
+ },
+
+ /**
+ * updates the user (agent) password field
+ *
+ * @param {string} agentPwd
+ */
+ setAgentPwd: function(agentPwd) {
+ this.setElementValue(
+ this.managedItems.ldap_agent_password.$element, agentPwd
+ );
+ },
+ /**
+ * updates the base DN text area
+ *
+ * @param {string} bases
+ */
+ setBase: function(bases) {
+ this.setElementValue(this.managedItems.ldap_base.$element, bases);
+ if(!bases) {
+ this.disableElement(this.managedItems.ldap_base.$testButton);
+ } else {
+ this.enableElement(this.managedItems.ldap_base.$testButton);
+ }
+ },
+
+ /**
+ * updates the experienced admin check box
+ *
+ * @param {string} xpAdminMode contains an int
+ */
+ setExperiencedAdmin: function(xpAdminMode) {
+ this.setElementValue(
+ this.managedItems.ldap_experienced_admin.$element, xpAdminMode
+ );
+ },
+
+ /**
+ * @inheritdoc
+ */
+ overrideErrorMessage: function(message, key) {
+ switch(key) {
+ case 'ldap_port':
+ if (message === 'Invalid credentials') {
+ return t('user_ldap', 'Please check the credentials, they seem to be wrong.');
+ } else {
+ return t('user_ldap', 'Please specify the port, it could not be auto-detected.');
+ }
+ break;
+ case 'ldap_base':
+ if( message === 'Server is unwilling to perform'
+ || message === 'Could not connect to LDAP'
+ ) {
+ return t('user_ldap', 'Base DN could not be auto-detected, please revise credentials, host and port.');
+ }
+ return t('user_ldap', 'Could not detect Base DN, please enter it manually.');
+ break;
+ }
+ return message;
+ },
+
+ /**
+ * resets the view when a configuration switch happened.
+ *
+ * @param {WizardTabElementary} view
+ * @param {Object} configuration
+ */
+ onConfigSwitch: function(view, configuration) {
+ view.disableElement(view.managedItems.ldap_port.$relatedElements);
+
+ view.onConfigLoaded(view, configuration);
+ },
+
+ /**
+ * updates the configuration chooser when a new configuration was added
+ * which also means it is being switched to. The configuration fields
+ * are updated on a different step.
+ *
+ * @param {WizardTabElementary} view
+ * @param {Object} result
+ */
+ onNewConfiguration: function(view, result) {
+ if(result.isSuccess === true) {
+ $(view.configChooserID + ' option:selected').removeAttr('selected');
+ var html = '<option value="'+result.configPrefix+'" selected="selected">'+t('user_ldap','{nthServer}. Server', {nthServer: $(view.configChooserID + ' option').length + 1})+'</option>';
+ $(view.configChooserID + ' option:last').after(html);
+ }
+ },
+
+ /**
+ * updates the configuration chooser upon the deletion of a
+ * configuration and, if necessary, loads an existing one.
+ *
+ * @param view
+ * @param result
+ */
+ onDeleteConfiguration: function(view, result) {
+ if(result.isSuccess === true) {
+ if(view.getConfigID() === result.configPrefix) {
+ // if the deleted value is still the selected one (99% of
+ // the cases), remove it from the list and load the topmost
+ $(view.configChooserID + ' option:selected').remove();
+ $(view.configChooserID + ' option:first').select();
+ if($(view.configChooserID + ' option').length < 2) {
+ view.configModel.newConfig(false);
+ } else {
+ view.configModel.load(view.getConfigID());
+ }
+ } else {
+ // otherwise just remove the entry
+ $(view.configChooserID + ' option[value=' + result.configPrefix + ']').remove();
+ }
+ } else {
+ OC.Notification.showTemporary(result.errorMessage);
+ }
+ },
+
+ /**
+ * Base DN test results will arrive here
+ *
+ * @param {WizardTabElementary} view
+ * @param {FeaturePayload} payload
+ */
+ onTestResultReceived: function(view, payload) {
+ if(payload.feature === 'TestBaseDN') {
+ var message;
+ if(payload.data.status === 'success') {
+ var objectsFound = parseInt(payload.data.changes.ldap_test_base, 10);
+ if(objectsFound < 1) {
+ message = t('user_ldap', 'No object found in the given Base DN. Please revise.');
+ } else if(objectsFound > 1000) {
+ message = t('user_ldap', 'More then 1.000 directory entries available.');
+ } else {
+ message = t('user_ldap', objectsFound + ' entries available within the provided Base DN');
+ }
+ } else {
+ message = t('user_ldap', 'An error occurred. Please check the Base DN, as well as connection settings and credentials.');
+ if(payload.data.message) {
+ console.warn(payload.data.message);
+ }
+ }
+ OC.Notification.showTemporary(message);
+ }
+ },
+
+ /**
+ * request to count the users with the current filter
+ *
+ * @param {Event} event
+ */
+ onPortButtonClick: function(event) {
+ event.preventDefault();
+ this.configModel.requestWizard('ldap_port');
+ },
+
+ /**
+ * request to count the users with the current filter
+ *
+ * @param {Event} event
+ */
+ onBaseDNButtonClick: function(event) {
+ event.preventDefault();
+ this.configModel.requestWizard('ldap_base');
+ },
+
+ /**
+ * request to count the users with the current filter
+ *
+ * @param {Event} event
+ */
+ onBaseDNTestButtonClick: function(event) {
+ event.preventDefault();
+ this.configModel.requestWizard('ldap_test_base');
+ },
+
+ /**
+ * registers the change event on the configuration chooser and makes
+ * the model load a newly selected configuration
+ *
+ * @private
+ */
+ _enableConfigChooser: function() {
+ var view = this;
+ $(this.configChooserID).change(function(){
+ var value = $(view.configChooserID + ' option:selected:first').attr('value');
+ view.configModel.load(value);
+ });
+ },
+
+ /**
+ * adds actions to the action buttons for configuration management
+ *
+ * @private
+ */
+ _enableConfigButtons: function() {
+ var view = this;
+ $('#ldap_action_delete_configuration').click(function(event) {
+ event.preventDefault();
+ OC.dialogs.confirm(
+ t('user_ldap', 'Do you really want to delete the current Server Configuration?'),
+ t('user_ldap', 'Confirm Deletion'),
+ function(doDelete) {
+ if(doDelete) {
+ view.configModel.deleteConfig(view.getConfigID());
+ }
+ },
+ false
+ );
+ });
+
+ $('#ldap_action_add_configuration').click(function(event) {
+ event.preventDefault();
+ view.configModel.newConfig(false);
+ });
+
+ $('#ldap_action_copy_configuration').click(function(event) {
+ event.preventDefault();
+ view.configModel.newConfig(true);
+ });
+ }
+ });
+
+ OCA.LDAP.Wizard.WizardTabElementary = WizardTabElementary;
+})();
diff --git a/apps/user_ldap/js/wizard/wizardTabExpert.js b/apps/user_ldap/js/wizard/wizardTabExpert.js
new file mode 100644
index 00000000000..7cfd49ba0f6
--- /dev/null
+++ b/apps/user_ldap/js/wizard/wizardTabExpert.js
@@ -0,0 +1,130 @@
+
+/**
+ * Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+OCA = OCA || {};
+
+(function() {
+
+ /**
+ * @classdesc This class represents the view belonging to the expert tab
+ * in the LDAP wizard.
+ */
+ var WizardTabExpert = OCA.LDAP.Wizard.WizardTabGeneric.subClass({
+ /**
+ * initializes the instance. Always call it after initialization.
+ *
+ * @param tabIndex
+ * @param tabID
+ */
+ init: function (tabIndex, tabID) {
+ this._super(tabIndex, tabID);
+
+ var items = {
+ ldap_expert_username_attr: {
+ $element: $('#ldap_expert_username_attr'),
+ setMethod: 'setUsernameAttribute'
+ },
+ ldap_expert_uuid_user_attr: {
+ $element: $('#ldap_expert_uuid_user_attr'),
+ setMethod: 'setUserUUIDAttribute'
+ },
+ ldap_expert_uuid_group_attr: {
+ $element: $('#ldap_expert_uuid_group_attr'),
+ setMethod: 'setGroupUUIDAttribute'
+ },
+
+ //Buttons
+ ldap_action_clear_user_mappings: {
+ $element: $('#ldap_action_clear_user_mappings')
+ },
+ ldap_action_clear_group_mappings: {
+ $element: $('#ldap_action_clear_group_mappings')
+ }
+
+ };
+ this.setManagedItems(items);
+ _.bindAll(this, 'onClearUserMappingsClick', 'onClearGroupMappingsClick');
+ this.managedItems.ldap_action_clear_user_mappings.$element.click(this.onClearUserMappingsClick);
+ this.managedItems.ldap_action_clear_group_mappings.$element.click(this.onClearGroupMappingsClick);
+ },
+
+ /**
+ * Sets the config model for this view and subscribes to some events.
+ * Also binds the config chooser to the model
+ *
+ * @param {OCA.LDAP.Wizard.ConfigModel} configModel
+ */
+ setModel: function(configModel) {
+ this._super(configModel);
+ this.configModel.on('configLoaded', this.onConfigLoaded, this);
+ this.configModel.on('receivedLdapFeature', this.onResultReceived, this);
+ },
+
+ /**
+ * sets the attribute to be used to create an ownCloud ID (username)
+ *
+ * @param {string} attribute
+ */
+ setUsernameAttribute: function(attribute) {
+ this.setElementValue(this.managedItems.ldap_expert_username_attr.$element, attribute);
+ },
+
+ /**
+ * sets the attribute that provides an unique identifier per LDAP user
+ * entry
+ *
+ * @param {string} attribute
+ */
+ setUserUUIDAttribute: function(attribute) {
+ this.setElementValue(this.managedItems.ldap_expert_uuid_user_attr.$element, attribute);
+ },
+
+ /**
+ * sets the attribute that provides an unique identifier per LDAP group
+ * entry
+ *
+ * @param {string} attribute
+ */
+ setGroupUUIDAttribute: function(attribute) {
+ this.setElementValue(this.managedItems.ldap_expert_uuid_group_attr.$element, attribute);
+ },
+
+ /**
+ * requests clearing of all user mappings
+ */
+ onClearUserMappingsClick: function() {
+ this.configModel.requestWizard('ldap_action_clear_user_mappings', {ldap_clear_mapping: 'user'});
+ },
+
+ /**
+ * requests clearing of all group mappings
+ */
+ onClearGroupMappingsClick: function() {
+ this.configModel.requestWizard('ldap_action_clear_group_mappings', {ldap_clear_mapping: 'group'});
+ },
+
+ /**
+ * deals with the result of the Test Connection test
+ *
+ * @param {WizardTabAdvanced} view
+ * @param {FeaturePayload} payload
+ */
+ onResultReceived: function(view, payload) {
+ if(payload.feature === 'ClearMappings') {
+ var message;
+ if(payload.data.status === 'success') {
+ message = t('user_ldap', 'Mappings cleared successfully!');
+ } else {
+ message = t('user_ldap', 'Error while clearing the mappings.');
+ }
+ OC.Notification.showTemporary(message);
+ }
+ }
+ });
+
+ OCA.LDAP.Wizard.WizardTabExpert = WizardTabExpert;
+})();
diff --git a/apps/user_ldap/js/wizard/wizardTabGeneric.js b/apps/user_ldap/js/wizard/wizardTabGeneric.js
new file mode 100644
index 00000000000..524d2a048a1
--- /dev/null
+++ b/apps/user_ldap/js/wizard/wizardTabGeneric.js
@@ -0,0 +1,547 @@
+
+/**
+ * Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+OCA = OCA || {};
+
+(function() {
+
+ /**
+ * @classdesc An abstract tab view
+ * @abstract
+ */
+ var WizardTabGeneric = OCA.LDAP.Wizard.WizardObject.subClass({
+ isActive: false,
+
+ /**
+ * @property {string} - class that identifies a multiselect-plugin
+ * control.
+ */
+ multiSelectPluginClass: 'multiSelectPlugin',
+
+ /** @inheritdoc */
+ init: function(tabIndex, tabID) {
+ this.tabIndex = tabIndex;
+ this.tabID = tabID;
+ this.spinner = $('.ldapSpinner').first().clone().removeClass('hidden');
+ _.bindAll(this, '_toggleRawFilterMode', '_toggleRawFilterModeConfirmation');
+ },
+
+ /**
+ * sets the configuration items that are managed by that view.
+ *
+ * The parameter contains key-value pairs the key being the
+ * configuration keys and the value being its setter method.
+ *
+ * @param {object} managedItems
+ */
+ setManagedItems: function(managedItems) {
+ this.managedItems = managedItems;
+ this._enableAutoSave();
+ },
+
+ /**
+ * Sets the config model. The concrete view likely wants to subscribe
+ * to events as well.
+ *
+ * @param {OCA.LDAP.Wizard.ConfigModel} configModel
+ */
+ setModel: function(configModel) {
+ this.configModel = configModel;
+ this.parsedFilterMode = this.configModel.FILTER_MODE_ASSISTED;
+ this.configModel.on('detectionStarted', this.onDetectionStarted, this);
+ this.configModel.on('detectionCompleted', this.onDetectionCompleted, this);
+ this.configModel.on('serverError', this.onServerError, this);
+ this.configModel.on('setCompleted', this.onItemSaved, this);
+ this.configModel.on('configUpdated', this.onConfigLoaded, this);
+ },
+
+ /**
+ * the method can be used to display a different error/information
+ * message than provided by the ownCloud server response. The concrete
+ * Tab View may optionally implement it. Returning an empty string will
+ * avoid any notification.
+ *
+ * @param {string} message
+ * @param {string} key
+ * @returns {string}
+ */
+ overrideErrorMessage: function(message, key) {
+ return message;
+ },
+
+ /**
+ * this is called by the main view, if the tab is being switched to.
+ * The concrete tab view can implement this if necessary.
+ */
+ onActivate: function() { },
+
+ /**
+ * updates the tab when the model loaded a configuration and notified
+ * this view.
+ *
+ * @param {WizardTabGeneric} view - this instance
+ * @param {Object} configuration
+ */
+ onConfigLoaded: function(view, configuration) {
+ for(var key in view.managedItems){
+ if(!_.isUndefined(configuration[key])) {
+ var value = configuration[key];
+ var methodName = view.managedItems[key].setMethod;
+ if(!_.isUndefined(view[methodName])) {
+ view[methodName](value);
+ }
+ }
+ }
+ },
+
+ /**
+ * reacts on a set action on the model and updates the tab with the
+ * valid value.
+ *
+ * @param {WizardTabGeneric} view
+ * @param {Object} result
+ */
+ onItemSaved: function(view, result) {
+ if(!_.isUndefined(view.managedItems[result.key])) {
+ var methodName = view.managedItems[result.key].setMethod;
+ view[methodName](result.value);
+ if(!result.isSuccess) {
+ OC.Notification.showTemporary(t('user_ldap', 'Saving failed. Please make sure the database is in Operation. Reload before continuing.'));
+ console.warn(result.errorMessage);
+ }
+ }
+ },
+
+ /**
+ * displays server error messages.
+ *
+ * @param view
+ * @param payload
+ */
+ onServerError: function(view, payload) {
+ if ( !_.isUndefined(view.managedItems[payload.relatedKey])) {
+ var message = view.overrideErrorMessage(payload.message, payload.relatedKey);
+ if(message) {
+ OC.Notification.showTemporary(message);
+ }
+ }
+ },
+
+ /**
+ * disables affected, managed fields if a detector is running against them
+ *
+ * @param {WizardTabGeneric} view
+ * @param {string} key
+ */
+ onDetectionStarted: function(view, key) {
+ if(!_.isUndefined(view.managedItems[key])) {
+ view.disableElement(view.managedItems[key].$element);
+ if(!_.isUndefined(view.managedItems[key].$relatedElements)){
+ view.disableElement(view.managedItems[key].$relatedElements);
+ }
+ view.attachSpinner(view.managedItems[key].$element.attr('id'));
+ }
+ },
+
+ /**
+ * enables affected, managed fields after a detector was run against them
+ *
+ * @param {WizardTabGeneric} view
+ * @param {string} key
+ */
+ onDetectionCompleted: function(view, key) {
+ if(!_.isUndefined(view.managedItems[key])) {
+ view.enableElement(view.managedItems[key].$element);
+ if(!_.isUndefined(view.managedItems[key].$relatedElements)){
+ view.enableElement(view.managedItems[key].$relatedElements);
+ }
+ view.removeSpinner(view.managedItems[key].$element.attr('id'));
+ }
+ },
+
+ /**
+ * sets the value to an HTML element. Checkboxes, text areas and (text)
+ * input fields are supported.
+ *
+ * @param {jQuery} $element - the target element
+ * @param {string|number|Array} value
+ */
+ setElementValue: function($element, value) {
+ // deal with check box
+ if ($element.is('input[type=checkbox]')) {
+ this._setCheckBox($element, value);
+ return;
+ }
+
+ // deal with text area
+ if ($element.is('textarea') && $.isArray(value)) {
+ value = value.join("\n");
+ }
+
+ if ($element.is('span')) {
+ $element.text(value);
+ } else {
+ $element.val(value);
+ }
+ },
+
+ /**
+ * replaces options on a multiselect element
+ *
+ * @param {jQuery} $element - the multiselect element
+ * @param {Array} options
+ */
+ equipMultiSelect: function($element, options) {
+ $element.empty();
+ for (var i in options) {
+ var name = options[i];
+ $element.append($('<option>').val(name).text(name).attr('title', name));
+ }
+ if(!$element.hasClass('ldapGroupList')) {
+ $element.multiselect('refresh');
+ this.enableElement($element);
+ }
+ },
+
+ /**
+ * enables the specified HTML element
+ *
+ * @param {jQuery} $element
+ */
+ enableElement: function($element) {
+ var isMS = $element.is('select[multiple]');
+ var hasOptions = isMS ? ($element.find('option').length > 0) : false;
+
+ if($element.hasClass(this.multiSelectPluginClass) && hasOptions) {
+ $element.multiselect("enable");
+ } else if(!isMS || (isMS && hasOptions)) {
+ $element.prop('disabled', false);
+ }
+ },
+
+ /**
+ * disables the specified HTML element
+ *
+ * @param {jQuery} $element
+ */
+ disableElement: function($element) {
+ if($element.hasClass(this.multiSelectPluginClass)) {
+ $element.multiselect("disable");
+ } else {
+ $element.prop('disabled', 'disabled');
+ }
+ },
+
+ /**
+ * attaches a spinner icon to the HTML element specified by ID
+ *
+ * @param {string} elementID
+ */
+ attachSpinner: function(elementID) {
+ if($('#' + elementID + ' + .ldapSpinner').length == 0) {
+ var spinner = this.spinner.clone();
+ var $element = $('#' + elementID);
+ $(spinner).insertAfter($element);
+ // and special treatment for multiselects:
+ if ($element.is('select[multiple]')) {
+ $('#' + elementID + " + img + button").css('display', 'none');
+ }
+ }
+ },
+
+ /**
+ * removes the spinner icon from the HTML element specified by ID
+ *
+ * @param {string} elementID
+ */
+ removeSpinner: function(elementID) {
+ $('#' + elementID+' + .ldapSpinner').remove();
+ // and special treatment for multiselects:
+ $('#' + elementID + " + button").css('display', 'inline');
+ },
+
+ /**
+ * whether the wizard works in experienced admin mode
+ *
+ * @returns {boolean}
+ */
+ isExperiencedMode: function() {
+ return parseInt(this.configModel.configuration.ldap_experienced_admin, 10) === 1;
+ },
+
+ /**
+ * sets up auto-save functionality to the managed items
+ *
+ * @private
+ */
+ _enableAutoSave: function() {
+ var view = this;
+
+ for(var id in this.managedItems) {
+ if(_.isUndefined(this.managedItems[id].$element)
+ || _.isUndefined(this.managedItems[id].setMethod)) {
+ continue;
+ }
+ var $element = this.managedItems[id].$element;
+ if (!$element.is('select[multiple]')) {
+ $element.change(function() {
+ view._requestSave($(this));
+ });
+ }
+ }
+ },
+
+ /**
+ * initializes a multiSelect element
+ *
+ * @param {jQuery} $element
+ * @param {string} caption
+ * @private
+ */
+ _initMultiSelect: function($element, caption) {
+ var view = this;
+ $element.multiselect({
+ header: false,
+ selectedList: 9,
+ noneSelectedText: caption,
+ classes: this.multiSelectPluginClass,
+ close: function() {
+ view._requestSave($element);
+ }
+ });
+ },
+
+ /**
+ * @typedef {object} viewSaveInfo
+ * @property {function} val
+ * @property {function} attr
+ * @property {function} is
+ */
+
+ /**
+ * requests a save operation from the model for a given value
+ * represented by a HTML element and its ID.
+ *
+ * @param {jQuery|viewSaveInfo} $element
+ * @private
+ */
+ _requestSave: function($element) {
+ var value = '';
+ if($element.is('input[type=checkbox]')
+ && !$element.is(':checked')) {
+ value = 0;
+ } else if ($element.is('select[multiple]')) {
+ var entries = $element.multiselect("getChecked");
+ for(var i = 0; i < entries.length; i++) {
+ value = value + "\n" + entries[i].value;
+ }
+ value = $.trim(value);
+ } else {
+ value = $element.val();
+ }
+ this.configModel.set($element.attr('id'), value);
+ },
+
+ /**
+ * updates a checkbox element according to the provided value
+ *
+ * @param {jQuery} $element
+ * @param {string|number} value
+ * @private
+ */
+ _setCheckBox: function($element, value) {
+ if(parseInt(value, 10) === 1) {
+ $element.attr('checked', 'checked');
+ } else {
+ $element.removeAttr('checked');
+ }
+ },
+
+ /**
+ * this is called when the filter mode is switched to assisted. The
+ * concrete tab view should implement this, to load LDAP features
+ * (e.g. object classes, groups, attributes…), if necessary.
+ */
+ considerFeatureRequests: function() {},
+
+ /**
+ * this is called when the filter mode is switched to Assisted. The
+ * concrete tab view should request the compilation of the respective
+ * filter.
+ */
+ requestCompileFilter: function() {
+ this.configModel.requestWizard(this.filterName);
+ },
+
+ /**
+ * sets the filter mode according to the provided configuration value
+ *
+ * @param {string} mode
+ */
+ setFilterMode: function(mode) {
+ if(parseInt(mode, 10) === this.configModel.FILTER_MODE_ASSISTED) {
+ this.parsedFilterMode = this.configModel.FILTER_MODE_ASSISTED;
+ this.considerFeatureRequests();
+ this._setFilterModeAssisted();
+ if(this.isActive) {
+ // filter compilation should happen only, if the mode was
+ // switched manually, but not when initiating the view
+ this.requestCompileFilter();
+ }
+ } else {
+ this._setFilterModeRaw();
+ this.parsedFilterMode = this.configModel.FILTER_MODE_RAW;
+ }
+ },
+
+ /**
+ * updates the UI so that it represents the assisted mode setting
+ *
+ * @private
+ */
+ _setFilterModeAssisted: function() {
+ var view = this;
+ this.$filterModeRawContainer.addClass('invisible');
+ var filter = this.$filterModeRawContainer.find('.ldapFilterInputElement').val();
+ this.$filterModeRawContainer.siblings('.ldapReadOnlyFilterContainer').find('.ldapFilterReadOnlyElement').text(filter);
+ this.$filterModeRawContainer.siblings('.ldapReadOnlyFilterContainer').removeClass('hidden');
+ $.each(this.filterModeDisableableElements, function(i, $element) {
+ view.enableElement($element);
+ });
+ if(!_.isUndefined(this.filterModeStateElement)) {
+ if (this.filterModeStateElement.status === 'enabled') {
+ this.enableElement(this.filterModeStateElement.$element);
+ } else {
+ this.filterModeStateElement.status = 'disabled';
+ }
+ }
+ },
+
+ /**
+ * updates the UI so that it represents the raw mode setting
+ *
+ * @private
+ */
+ _setFilterModeRaw: function() {
+ var view = this;
+ this.$filterModeRawContainer.removeClass('invisible');
+ this.$filterModeRawContainer.siblings('.ldapReadOnlyFilterContainer').addClass('hidden');
+ $.each(this.filterModeDisableableElements, function (i, $element) {
+ view.disableElement($element);
+ });
+
+ if(!_.isUndefined(this.filterModeStateElement)) {
+ if(this.filterModeStateElement.$element.multiselect().attr('disabled') === 'disabled') {
+ this.filterModeStateElement.status = 'disabled';
+ } else {
+ this.filterModeStateElement.status = 'enabled';
+ }
+ }
+ if(!_.isUndefined(this.filterModeStateElement)) {
+ this.disableElement(this.filterModeStateElement.$element);
+ }
+ },
+
+ /**
+ * @callback toggleConfirmCallback
+ * @param {boolean} isConfirmed
+ */
+
+ /**
+ * shows a confirmation dialogue before switching from raw to assisted
+ * mode if experienced mode is enabled.
+ *
+ * @param {toggleConfirmCallback} toggleFnc
+ * @private
+ */
+ _toggleRawFilterModeConfirmation: function(toggleFnc) {
+ if( !this.isExperiencedMode()
+ || this.parsedFilterMode === this.configModel.FILTER_MODE_ASSISTED
+ ) {
+ toggleFnc(true);
+ } else {
+ OCdialogs.confirm(
+ t('user_ldap', 'Switching the mode will enable automatic LDAP queries. Depending on your LDAP size they may take a while. Do you still want to switch the mode?'),
+ t('user_ldap', 'Mode switch'),
+ toggleFnc
+ );
+ }
+ },
+
+ /**
+ * toggles the visibility of a raw filter container and so also the
+ * state of the multi-select controls. The model is requested to save
+ * the state.
+ */
+ _toggleRawFilterMode: function() {
+ var view = this;
+ this._toggleRawFilterModeConfirmation(function(isConfirmed) {
+ if(!isConfirmed) {
+ return;
+ }
+ /** var {number} */
+ var mode;
+ if (view.parsedFilterMode === view.configModel.FILTER_MODE_ASSISTED) {
+ mode = view.configModel.FILTER_MODE_RAW;
+ } else {
+ mode = view.configModel.FILTER_MODE_ASSISTED;
+ }
+ view.setFilterMode(mode);
+ /** @var {viewSaveInfo} */
+ var saveInfo = {
+ val: function () {
+ return mode;
+ },
+ attr: function () {
+ return view.filterModeKey;
+ },
+ is: function () {
+ return false;
+ }
+ };
+ view._requestSave(saveInfo);
+ });
+ },
+
+ /**
+ * @typedef {object} filterModeStateElementObj
+ * @property {string} status - either "enabled" or "disabled"
+ * @property {jQuery} $element
+ */
+
+ /**
+ * initializes a raw filter mode switcher
+ *
+ * @param {jQuery} $switcher - the element receiving the click
+ * @param {jQuery} $filterModeRawContainer - contains the raw filter
+ * input elements
+ * @param {jQuery[]} filterModeDisableableElements - an array of elements
+ * not belonging to the raw filter part that shall be en/disabled.
+ * @param {string} filterModeKey - the setting key that save the state
+ * of the mode
+ * @param {filterModeStateElementObj} [filterModeStateElement] - one element
+ * which status (enabled or not) is tracked by a setting
+ * @private
+ */
+ _initFilterModeSwitcher: function(
+ $switcher,
+ $filterModeRawContainer,
+ filterModeDisableableElements,
+ filterModeKey,
+ filterModeStateElement
+ ) {
+ this.$filterModeRawContainer = $filterModeRawContainer;
+ this.filterModeDisableableElements = filterModeDisableableElements;
+ this.filterModeStateElement = filterModeStateElement;
+ this.filterModeKey = filterModeKey;
+ $switcher.click(this._toggleRawFilterMode);
+ }
+
+ });
+
+ OCA.LDAP.Wizard.WizardTabGeneric = WizardTabGeneric;
+})();
diff --git a/apps/user_ldap/js/wizard/wizardTabGroupFilter.js b/apps/user_ldap/js/wizard/wizardTabGroupFilter.js
new file mode 100644
index 00000000000..528b5d83670
--- /dev/null
+++ b/apps/user_ldap/js/wizard/wizardTabGroupFilter.js
@@ -0,0 +1,124 @@
+/**
+ * Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+OCA = OCA || {};
+
+(function() {
+
+ /**
+ * @classdesc This class represents the view belonging to the server tab
+ * in the LDAP wizard.
+ */
+ var WizardTabGroupFilter = OCA.LDAP.Wizard.WizardTabAbstractFilter.subClass({
+ /**
+ * @inheritdoc
+ */
+ init: function (fotf, tabIndex, tabID) {
+ tabID = '#ldapWizard4';
+ var items = {
+ ldap_groupfilter_objectclass: {
+ $element: $('#ldap_groupfilter_objectclass'),
+ setMethod: 'setObjectClass',
+ keyName: 'ldap_groupfilter_objectclass',
+ featureName: 'GroupObjectClasses'
+ },
+ ldap_group_filter_mode: {
+ setMethod: 'setFilterMode'
+ },
+ ldap_groupfilter_groups: {
+ $element: $('#ldap_groupfilter_groups'),
+ setMethod: 'setGroups',
+ keyName: 'ldap_groupfilter_groups',
+ featureName: 'GroupsForGroups',
+ $relatedElements: $(
+ tabID + ' .ldapGroupListAvailable,' +
+ tabID + ' .ldapGroupListSelected,' +
+ tabID + ' .ldapManyGroupsSearch'
+ )
+ },
+ ldap_group_filter: {
+ $element: $('#ldap_group_filter'),
+ setMethod: 'setFilter',
+ keyName: 'ldap_group_filter'
+ },
+ groupFilterRawToggle: {
+ $element: $('#toggleRawGroupFilter')
+ },
+ groupFilterRawContainer: {
+ $element: $('#rawGroupFilterContainer')
+ },
+ ldap_group_count: {
+ $element: $('#ldap_group_count'),
+ $relatedElements: $('.ldapGetGroupCount'),
+ setMethod: 'setCount',
+ keyName: 'ldap_group_count'
+ }
+ };
+ this.setManagedItems(items);
+ this.manyGroupsSupport = true;
+ this._super(fotf, tabIndex, tabID);
+ },
+
+ /**
+ * @inheritdoc
+ * @returns {Object}
+ */
+ getObjectClassItem: function () {
+ return this.managedItems.ldap_groupfilter_objectclass;
+ },
+
+ /**
+ * @inheritdoc
+ * @returns {Object}
+ */
+ getGroupsItem: function () {
+ return this.managedItems.ldap_groupfilter_groups;
+ },
+
+ /**
+ * @inheritdoc
+ * @returns {Object}
+ */
+ getFilterItem: function () {
+ return this.managedItems.ldap_group_filter;
+ },
+
+ /**
+ * @inheritdoc
+ * @returns {Object}
+ */
+ getToggleItem: function () {
+ return this.managedItems.groupFilterRawToggle;
+ },
+
+ /**
+ * @inheritdoc
+ * @returns {Object}
+ */
+ getRawFilterContainerItem: function () {
+ return this.managedItems.groupFilterRawContainer;
+ },
+
+ /**
+ * @inheritdoc
+ * @returns {Object}
+ */
+ getCountItem: function () {
+ return this.managedItems.ldap_group_count;
+ },
+
+ /**
+ * @inheritdoc
+ * @returns {string}
+ */
+ getFilterModeKey: function () {
+ return 'ldap_group_filter_mode';
+ }
+
+ });
+
+ OCA.LDAP.Wizard.WizardTabGroupFilter = WizardTabGroupFilter;
+})();
diff --git a/apps/user_ldap/js/wizard/wizardTabLoginFilter.js b/apps/user_ldap/js/wizard/wizardTabLoginFilter.js
new file mode 100644
index 00000000000..9438fd73346
--- /dev/null
+++ b/apps/user_ldap/js/wizard/wizardTabLoginFilter.js
@@ -0,0 +1,238 @@
+/**
+ * Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+OCA = OCA || {};
+
+(function() {
+
+ /**
+ * @classdesc This class represents the view belonging to the login filter
+ * tab in the LDAP wizard.
+ */
+ var WizardTabLoginFilter = OCA.LDAP.Wizard.WizardTabGeneric.subClass({
+ /**
+ * initializes the instance. Always call it after initialization.
+ *
+ * @param tabIndex
+ * @param tabID
+ */
+ init: function (tabIndex, tabID) {
+ this._super(tabIndex, tabID);
+
+ var items = {
+ ldap_loginfilter_username: {
+ $element: $('#ldap_loginfilter_username'),
+ setMethod: 'setLoginAttributeUsername'
+ },
+ ldap_loginfilter_email: {
+ $element: $('#ldap_loginfilter_email'),
+ setMethod: 'setLoginAttributeEmail'
+ },
+ ldap_login_filter_mode: {
+ setMethod: 'setFilterMode'
+ },
+ ldap_loginfilter_attributes: {
+ $element: $('#ldap_loginfilter_attributes'),
+ setMethod: 'setLoginAttributesOther'
+ },
+ ldap_login_filter: {
+ $element: $('#ldap_login_filter'),
+ setMethod: 'setLoginFilter'
+ },
+ loginFilterRawToggle: {
+ $element: $('#toggleRawLoginFilter')
+ },
+ loginFilterRawContainer: {
+ $element: $('#rawLoginFilterContainer')
+ },
+ ldap_test_loginname: {
+ $element: $('#ldap_test_loginname'),
+ $relatedElements: $('.ldapVerifyLoginName')
+ }
+ };
+ this.setManagedItems(items);
+
+ this.filterModeKey = 'ldapLoginFilterMode';
+ this._initMultiSelect(
+ this.managedItems.ldap_loginfilter_attributes.$element,
+ t('user_ldap', 'Select attributes')
+ );
+ this.filterName = 'ldap_login_filter';
+ this._initFilterModeSwitcher(
+ this.managedItems.loginFilterRawToggle.$element,
+ this.managedItems.loginFilterRawContainer.$element,
+ [
+ this.managedItems.ldap_loginfilter_username.$element,
+ this.managedItems.ldap_loginfilter_email.$element,
+ this.managedItems.ldap_loginfilter_attributes.$element
+ ],
+ 'ldap_login_filter_mode'
+ );
+ _.bindAll(this, 'onVerifyClick');
+ this.managedItems.ldap_test_loginname.$relatedElements.click(this.onVerifyClick);
+ },
+
+ /**
+ * Sets the config model for this view and subscribes to some events.
+ * Also binds the config chooser to the model
+ *
+ * @param {OCA.LDAP.Wizard.ConfigModel} configModel
+ */
+ setModel: function(configModel) {
+ this._super(configModel);
+ this.configModel.on('configLoaded', this.onConfigSwitch, this);
+ this.configModel.on('receivedLdapFeature', this.onFeatureReceived, this);
+ },
+
+ /**
+ * sets the selected attributes
+ *
+ * @param {Array} attributes
+ */
+ setLoginAttributesOther: function(attributes) {
+ this.setElementValue(this.managedItems.ldap_loginfilter_attributes.$element, attributes);
+ this.managedItems.ldap_loginfilter_attributes.$element.multiselect('refresh');
+ },
+
+ /**
+ * sets the login list filter
+ *
+ * @param {string} filter
+ */
+ setLoginFilter: function(filter) {
+ this.setElementValue(this.managedItems.ldap_login_filter.$element, filter);
+ this.$filterModeRawContainer.siblings('.ldapReadOnlyFilterContainer').find('.ldapFilterReadOnlyElement').text(filter);
+ },
+
+ /**
+ * updates the username attribute check box
+ *
+ * @param {string} useUsername contains an int
+ */
+ setLoginAttributeUsername: function(useUsername) {
+ this.setElementValue(
+ this.managedItems.ldap_loginfilter_username.$element, useUsername
+ );
+ },
+
+ /**
+ * updates the email attribute check box
+ *
+ * @param {string} useEmail contains an int
+ */
+ setLoginAttributeEmail: function(useEmail) {
+ this.setElementValue(
+ this.managedItems.ldap_loginfilter_email.$element, useEmail
+ );
+ },
+
+ /**
+ * presents the result of the login name test
+ *
+ * @param result
+ */
+ handleLoginTestResult: function(result) {
+ var message;
+ var isHtml = false;
+ if(result.status === 'success') {
+ var usersFound = parseInt(result.changes.ldap_test_loginname, 10);
+ if(usersFound < 1) {
+ var filter = $('<p>').text(result.changes.ldap_test_effective_filter).html();
+ message = t('user_ldap', 'User not found. Please check your login attributes and username. Effective filter (to copy-and-paste for command line validation): <br/>' + filter);
+ console.warn(filter);
+ isHtml = true;
+ } else if(usersFound === 1) {
+ message = t('user_ldap', 'User found and settings verified.');
+ } else if(usersFound > 1) {
+ message = t('user_ldap', 'Settings verified, but one user found. Only the first will be able to login. Consider a more narrow filter.');
+ }
+ } else {
+ message = t('user_ldap', 'An unspecified error occurred. Please check the settings and the log.');
+ if(!_.isUndefined(result.message) && result.message) {
+ message = result.message;
+ }
+ if(message === 'Bad search filter') {
+ message = t('user_ldap', 'The search filter is invalid, probably due to syntax issues like uneven number of opened and closed brackets. Please revise.');
+ } else if(message === 'connection error') {
+ message = t('user_ldap', 'A connection error to LDAP / AD occurred, please check host, port and credentials.');
+ } else if(message === 'missing placeholder') {
+ message = t('user_ldap', 'The %uid placeholder is missing. It will be replaced with the login name when querying LDAP / AD.');
+ }
+ }
+ OC.Notification.showTemporary(message, {isHTML: isHtml});
+ },
+
+ /**
+ * @inheritdoc
+ */
+ considerFeatureRequests: function() {
+ if(!this.isActive) {
+ return;
+ }
+ if(this.managedItems.ldap_loginfilter_attributes.$element.find('option').length === 0) {
+ this.disableElement(this.managedItems.ldap_loginfilter_attributes.$element);
+ if(this.parsedFilterMode === this.configModel.FILTER_MODE_ASSISTED) {
+ this.configModel.requestWizard('ldap_loginfilter_attributes');
+ }
+ }
+ },
+
+ /**
+ * @inheritdoc
+ */
+ onActivate: function() {
+ this.considerFeatureRequests();
+ if(!this.managedItems.ldap_login_filter.$element.val()) {
+ this.configModel.requestWizard('ldap_login_filter');
+ }
+ },
+
+ /**
+ * resets the view when a configuration switch happened.
+ *
+ * @param {WizardTabLoginFilter} view
+ * @param {Object} configuration
+ */
+ onConfigSwitch: function(view, configuration) {
+ view.managedItems.ldap_loginfilter_attributes.$element.find('option').remove();
+
+ view.onConfigLoaded(view, configuration);
+ },
+
+ /**
+ * if UserObjectClasses are found, the corresponding element will be
+ * updated
+ *
+ * @param {WizardTabLoginFilter} view
+ * @param {FeaturePayload} payload
+ */
+ onFeatureReceived: function(view, payload) {
+ if(payload.feature === 'AvailableAttributes') {
+ view.equipMultiSelect(view.managedItems.ldap_loginfilter_attributes.$element, payload.data);
+ } else if(payload.feature === 'TestLoginName') {
+ view.handleLoginTestResult(payload.data);
+ }
+ },
+
+ /**
+ * request to test the provided login name
+ *
+ * @param {Event} event
+ */
+ onVerifyClick: function(event) {
+ event.preventDefault();
+ var testLogin = this.managedItems.ldap_test_loginname.$element.val();
+ if(!testLogin) {
+ OC.Notification.showTemporary(t('user_ldap', 'Please provide a login name to test against'), 3);
+ } else {
+ this.configModel.requestWizard('ldap_test_loginname', {ldap_test_loginname: testLogin});
+ }
+ }
+
+ });
+
+ OCA.LDAP.Wizard.WizardTabLoginFilter = WizardTabLoginFilter;
+})();
diff --git a/apps/user_ldap/js/wizard/wizardTabUserFilter.js b/apps/user_ldap/js/wizard/wizardTabUserFilter.js
new file mode 100644
index 00000000000..992c1ccf379
--- /dev/null
+++ b/apps/user_ldap/js/wizard/wizardTabUserFilter.js
@@ -0,0 +1,136 @@
+/**
+ * Copyright (c) 2015, Arthur Schiwon <blizzz@owncloud.com>
+ * This file is licensed under the Affero General Public License version 3 or later.
+ * See the COPYING-README file.
+ */
+
+OCA = OCA || {};
+
+(function() {
+
+ /**
+ * @classdesc This class represents the view belonging to the server tab
+ * in the LDAP wizard.
+ */
+ var WizardTabUserFilter = OCA.LDAP.Wizard.WizardTabAbstractFilter.subClass({
+ /**
+ * @inheritdoc
+ */
+ init: function (fotf, tabIndex, tabID) {
+ tabID = '#ldapWizard2';
+ var items = {
+ ldap_userfilter_objectclass: {
+ $element: $('#ldap_userfilter_objectclass'),
+ setMethod: 'setObjectClass',
+ keyName: 'ldap_userfilter_objectclass',
+ featureName: 'UserObjectClasses'
+ },
+ ldap_user_filter_mode: {
+ setMethod: 'setFilterMode'
+ },
+ ldap_userfilter_groups: {
+ $element: $('#ldap_userfilter_groups'),
+ setMethod: 'setGroups',
+ keyName: 'ldap_userfilter_groups',
+ featureName: 'GroupsForUsers',
+ $relatedElements: $(
+ tabID + ' .ldapGroupListAvailable,' +
+ tabID + ' .ldapGroupListSelected,' +
+ tabID + ' .ldapManyGroupsSearch'
+ )
+ },
+ ldap_userlist_filter: {
+ $element: $('#ldap_userlist_filter'),
+ setMethod: 'setFilter',
+ keyName: 'ldap_userlist_filter'
+ },
+ userFilterRawToggle: {
+ $element: $('#toggleRawUserFilter')
+ },
+ userFilterRawContainer: {
+ $element: $('#rawUserFilterContainer')
+ },
+ ldap_user_count: {
+ $element: $('#ldap_user_count'),
+ $relatedElements: $('.ldapGetUserCount'),
+ setMethod: 'setCount',
+ keyName: 'ldap_user_count'
+ }
+ };
+ this.setManagedItems(items);
+ this.manyGroupsSupport = true;
+ this._super(fotf, tabIndex, tabID);
+ },
+
+ /**
+ * @inheritdoc
+ * @returns {Object}
+ */
+ getObjectClassItem: function () {
+ return this.managedItems.ldap_userfilter_objectclass;
+ },
+
+ /**
+ * @inheritdoc
+ * @returns {Object}
+ */
+ getGroupsItem: function () {
+ return this.managedItems.ldap_userfilter_groups;
+ },
+
+ /**
+ * @inheritdoc
+ * @returns {Object}
+ */
+ getFilterItem: function () {
+ return this.managedItems.ldap_userlist_filter;
+ },
+
+ /**
+ * @inheritdoc
+ * @returns {Object}
+ */
+ getToggleItem: function () {
+ return this.managedItems.userFilterRawToggle;
+ },
+
+ /**
+ * @inheritdoc
+ * @returns {Object}
+ */
+ getRawFilterContainerItem: function () {
+ return this.managedItems.userFilterRawContainer;
+ },
+
+ /**
+ * @inheritdoc
+ * @returns {Object}
+ */
+ getCountItem: function () {
+ return this.managedItems.ldap_user_count;
+ },
+
+ /**
+ * @inheritdoc
+ * @returns {string}
+ */
+ getFilterModeKey: function () {
+ return 'ldap_user_filter_mode';
+ },
+
+ /**
+ * @inheritdoc
+ */
+ overrideErrorMessage: function(message, key) {
+ if( key === 'ldap_userfilter_groups'
+ && message === 'memberOf is not supported by the server'
+ ) {
+ message = t('user_ldap', 'The group box was disabled, because the LDAP / AD server does not support memberOf.');
+ }
+ return message;
+ }
+
+ });
+
+ OCA.LDAP.Wizard.WizardTabUserFilter = WizardTabUserFilter;
+})();
diff --git a/apps/user_ldap/lib/access.php b/apps/user_ldap/lib/access.php
index e7fb4165c36..f38d11d4be3 100644
--- a/apps/user_ldap/lib/access.php
+++ b/apps/user_ldap/lib/access.php
@@ -596,6 +596,22 @@ class Access extends LDAPUtility implements user\IUserTools {
}
/**
+ * fetches a list of users according to a provided loginName and utilizing
+ * the login filter.
+ *
+ * @param string $loginName
+ * @param array $attributes optional, list of attributes to read
+ * @return array
+ */
+ public function fetchUsersByLoginName($loginName, $attributes = array('dn')) {
+ $loginName = $this->escapeFilterPart($loginName);
+ $filter = \OCP\Util::mb_str_replace(
+ '%uid', $loginName, $this->connection->ldapLoginFilter, 'UTF-8');
+ $users = $this->fetchListOfUsers($filter, $attributes);
+ return $users;
+ }
+
+ /**
* @param string $filter
* @param string|string[] $attr
* @param int $limit
@@ -687,6 +703,17 @@ class Access extends LDAPUtility implements user\IUserTools {
}
/**
+ * returns the number of available objects on the base DN
+ *
+ * @param int|null $limit
+ * @param int|null $offset
+ * @return int|bool
+ */
+ public function countObjects($limit = null, $offset = null) {
+ return $this->count('objectclass=*', $this->connection->ldapBase, array('dn'), $limit, $offset);
+ }
+
+ /**
* retrieved. Results will according to the order in the array.
* @param int $limit optional, maximum results to be counted
* @param int $offset optional, a starting point
diff --git a/apps/user_ldap/lib/configuration.php b/apps/user_ldap/lib/configuration.php
index 9b01fd2e559..373c5b48417 100644
--- a/apps/user_ldap/lib/configuration.php
+++ b/apps/user_ldap/lib/configuration.php
@@ -201,11 +201,14 @@ class Configuration {
case 'ldapAgentPassword':
$readMethod = 'getPwd';
break;
- case 'ldapUserDisplayName':
case 'ldapGroupDisplayName':
$readMethod = 'getLcValue';
break;
+ case 'ldapUserDisplayName':
default:
+ // user display name does not lower case because
+ // we rely on an upper case N as indicator whether to
+ // auto-detect it or not. FIXME
$readMethod = 'getValue';
break;
}
@@ -374,7 +377,7 @@ class Configuration {
'ldap_groupfilter_groups' => '',
'ldap_display_name' => 'displayName',
'ldap_group_display_name' => 'cn',
- 'ldap_tls' => 1,
+ 'ldap_tls' => 0,
'ldap_nocase' => 0,
'ldap_quota_def' => '',
'ldap_quota_attr' => '',
diff --git a/apps/user_ldap/lib/wizard.php b/apps/user_ldap/lib/wizard.php
index 97f3002ccaf..7bb5752352f 100644
--- a/apps/user_ldap/lib/wizard.php
+++ b/apps/user_ldap/lib/wizard.php
@@ -75,9 +75,11 @@ class Wizard extends LDAPUtility {
/**
* counts entries in the LDAP directory
+ *
* @param string $filter the LDAP search filter
* @param string $type a string being either 'users' or 'groups';
- * @return int|bool
+ * @return bool|int
+ * @throws \Exception
*/
public function countEntries($filter, $type) {
$reqs = array('ldapHost', 'ldapPort', 'ldapBase');
@@ -88,17 +90,36 @@ class Wizard extends LDAPUtility {
throw new \Exception('Requirements not met', 400);
}
+ $attr = array('dn'); // default
+ $limit = 1001;
if($type === 'groups') {
- $result = $this->access->countGroups($filter);
+ $result = $this->access->countGroups($filter, $attr, $limit);
} else if($type === 'users') {
- $result = $this->access->countUsers($filter);
+ $result = $this->access->countUsers($filter, $attr, $limit);
+ } else if ($type === 'objects') {
+ $result = $this->access->countObjects($limit);
} else {
- throw new \Exception('internal error: invald object type', 500);
+ throw new \Exception('internal error: invalid object type', 500);
}
return $result;
}
+ /**
+ * formats the return value of a count operation to the string to be
+ * inserted.
+ *
+ * @param bool|int $count
+ * @return int|string
+ */
+ private function formatCountResult($count) {
+ $formatted = ($count !== false) ? $count : 0;
+ if($formatted > 1000) {
+ $formatted = '> 1000';
+ }
+ return $formatted;
+ }
+
public function countGroups() {
$filter = $this->configuration->ldapGroupFilter;
@@ -109,7 +130,7 @@ class Wizard extends LDAPUtility {
}
try {
- $groupsTotal = $this->countEntries($filter, 'groups');
+ $groupsTotal = $this->formatCountResult($this->countEntries($filter, 'groups'));
} catch (\Exception $e) {
//400 can be ignored, 500 is forwarded
if($e->getCode() === 500) {
@@ -117,7 +138,6 @@ class Wizard extends LDAPUtility {
}
return false;
}
- $groupsTotal = ($groupsTotal !== false) ? $groupsTotal : 0;
$output = self::$l->n('%s group found', '%s groups found', $groupsTotal, array($groupsTotal));
$this->result->addChange('ldap_group_count', $output);
return $this->result;
@@ -130,14 +150,29 @@ class Wizard extends LDAPUtility {
public function countUsers() {
$filter = $this->access->getFilterForUserCount();
- $usersTotal = $this->countEntries($filter, 'users');
- $usersTotal = ($usersTotal !== false) ? $usersTotal : 0;
+ $usersTotal = $this->formatCountResult($this->countEntries($filter, 'users'));
$output = self::$l->n('%s user found', '%s users found', $usersTotal, array($usersTotal));
$this->result->addChange('ldap_user_count', $output);
return $this->result;
}
/**
+ * counts any objects in the currently set base dn
+ *
+ * @return WizardResult
+ * @throws \Exception
+ */
+ public function countInBaseDN() {
+ // we don't need to provide a filter in this case
+ $total = $this->countEntries(null, 'objects');
+ if($total === false) {
+ throw new \Exception('invalid results received');
+ }
+ $this->result->addChange('ldap_test_base', $total);
+ return $this->result;
+ }
+
+ /**
* counts users with a specified attribute
* @param string $attr
* @param bool $existsCheck
@@ -282,45 +317,6 @@ class Wizard extends LDAPUtility {
}
/**
- * return the state of the Group Filter Mode
- * @return WizardResult
- */
- public function getGroupFilterMode() {
- $this->getFilterMode('ldapGroupFilterMode');
- return $this->result;
- }
-
- /**
- * return the state of the Login Filter Mode
- * @return WizardResult
- */
- public function getLoginFilterMode() {
- $this->getFilterMode('ldapLoginFilterMode');
- return $this->result;
- }
-
- /**
- * return the state of the User Filter Mode
- * @return WizardResult
- */
- public function getUserFilterMode() {
- $this->getFilterMode('ldapUserFilterMode');
- return $this->result;
- }
-
- /**
- * return the state of the mode of the specified filter
- * @param string $confKey contains the access key of the Configuration
- */
- private function getFilterMode($confKey) {
- $mode = $this->configuration->$confKey;
- if(is_null($mode)) {
- $mode = $this->LFILTER_MODE_ASSISTED;
- }
- $this->result->addChange($confKey, $mode);
- }
-
- /**
* detects the available LDAP attributes
* @return array|false The instance's WizardResult instance
* @throws \Exception
@@ -467,8 +463,7 @@ class Wizard extends LDAPUtility {
return false;
}
$this->configuration->setConfiguration(array('ldapGroupMemberAssocAttr' => $attribute));
- //so it will be saved on destruct
- $this->result->markChange();
+ $this->result->addChange('ldap_group_member_assoc_attribute', $attribute);
return $this->result;
}
@@ -604,6 +599,41 @@ class Wizard extends LDAPUtility {
}
/**
+ * @return bool|WizardResult
+ * @param string $loginName
+ * @throws \Exception
+ */
+ public function testLoginName($loginName) {
+ if(!$this->checkRequirements(array('ldapHost',
+ 'ldapPort',
+ 'ldapBase',
+ 'ldapLoginFilter',
+ ))) {
+ return false;
+ }
+
+ $cr = $this->access->connection->getConnectionResource();
+ if(!$this->ldap->isResource($cr)) {
+ throw new \Exception('connection error');
+ }
+
+ if(mb_strpos($this->access->connection->ldapLoginFilter, '%uid', 0, 'UTF-8')
+ === false) {
+ throw new \Exception('missing placeholder');
+ }
+
+ $users = $this->access->fetchUsersByLoginName($loginName);
+ if($this->ldap->errno($cr) !== 0) {
+ throw new \Exception($this->ldap->error($cr));
+ }
+ $filter = \OCP\Util::mb_str_replace(
+ '%uid', $loginName, $this->access->connection->ldapLoginFilter, 'UTF-8');
+ $this->result->addChange('ldap_test_loginname', count($users));
+ $this->result->addChange('ldap_test_effective_filter', $filter);
+ return $this->result;
+ }
+
+ /**
* Tries to determine the port, requires given Host, User DN and Password
* @return WizardResult|false WizardResult on success, false otherwise
* @throws \Exception
@@ -674,10 +704,13 @@ class Wizard extends LDAPUtility {
}
$dparts = explode('.', $domain);
- $base2 = implode('dc=', $dparts);
- if($base !== $base2 && $this->testBaseDN($base2)) {
- $this->applyFind('ldap_base', $base2);
- return $this->result;
+ while(count($dparts) > 0) {
+ $base2 = 'dc=' . implode(',dc=', $dparts);
+ if ($base !== $base2 && $this->testBaseDN($base2)) {
+ $this->applyFind('ldap_base', $base2);
+ return $this->result;
+ }
+ array_shift($dparts);
}
return false;
@@ -720,7 +753,7 @@ class Wizard extends LDAPUtility {
* @throws \Exception
*/
private function detectGroupMemberAssoc() {
- $possibleAttrs = array('uniqueMember', 'memberUid', 'member', 'unfugasdfasdfdfa');
+ $possibleAttrs = array('uniqueMember', 'memberUid', 'member');
$filter = $this->configuration->ldapGroupFilter;
if(empty($filter)) {
return false;
@@ -730,7 +763,7 @@ class Wizard extends LDAPUtility {
throw new \Exception('Could not connect to LDAP');
}
$base = $this->configuration->ldapBase[0];
- $rr = $this->ldap->search($cr, $base, $filter, $possibleAttrs);
+ $rr = $this->ldap->search($cr, $base, $filter, $possibleAttrs, 0, 1000);
if(!$this->ldap->isResource($rr)) {
return false;
}
@@ -1114,7 +1147,8 @@ class Wizard extends LDAPUtility {
//skip when the filter is a wildcard and results were found
continue;
}
- $rr = $this->ldap->search($cr, $base, $filter, array($attr));
+ // 20k limit for performance and reason
+ $rr = $this->ldap->search($cr, $base, $filter, array($attr), 0, 20000);
if(!$this->ldap->isResource($rr)) {
continue;
}
diff --git a/apps/user_ldap/settings.php b/apps/user_ldap/settings.php
index 85bfbaa076f..d17361cdfd5 100644
--- a/apps/user_ldap/settings.php
+++ b/apps/user_ldap/settings.php
@@ -29,13 +29,6 @@
OC_Util::checkAdminUser();
-OCP\Util::addScript('user_ldap', 'ldapFilter');
-OCP\Util::addScript('user_ldap', 'experiencedAdmin');
-OCP\Util::addScript('user_ldap', 'settings');
-\OC_Util::addVendorScript('user_ldap', 'ui-multiselect/src/jquery.multiselect');
-OCP\Util::addStyle('user_ldap', 'settings');
-\OC_Util::addVendorStyle('user_ldap', 'ui-multiselect/jquery.multiselect');
-
// fill template
$tmpl = new OCP\Template('user_ldap', 'settings');
@@ -55,9 +48,9 @@ $l = \OC::$server->getL10N('user_ldap');
$wizTabs = array();
$wizTabs[] = array('tpl' => 'part.wizard-server', 'cap' => $l->t('Server'));
-$wizTabs[] = array('tpl' => 'part.wizard-userfilter', 'cap' => $l->t('User Filter'));
-$wizTabs[] = array('tpl' => 'part.wizard-loginfilter', 'cap' => $l->t('Login Filter'));
-$wizTabs[] = array('tpl' => 'part.wizard-groupfilter', 'cap' => $l->t('Group Filter'));
+$wizTabs[] = array('tpl' => 'part.wizard-userfilter', 'cap' => $l->t('Users'));
+$wizTabs[] = array('tpl' => 'part.wizard-loginfilter', 'cap' => $l->t('Login Attributes'));
+$wizTabs[] = array('tpl' => 'part.wizard-groupfilter', 'cap' => $l->t('Groups'));
$wizTabsCount = count($wizTabs);
for($i = 0; $i < $wizTabsCount; $i++) {
$tab = new OCP\Template('user_ldap', $wizTabs[$i]['tpl']);
diff --git a/apps/user_ldap/templates/part.settingcontrols.php b/apps/user_ldap/templates/part.settingcontrols.php
index e67cea41d9c..bac00daa39f 100644
--- a/apps/user_ldap/templates/part.settingcontrols.php
+++ b/apps/user_ldap/templates/part.settingcontrols.php
@@ -1,5 +1,4 @@
<div class="ldapSettingControls">
- <input class="ldap_submit" value="<?php p($l->t('Save'));?>" type="submit">
<button type="button" class="ldap_action_test_connection" name="ldap_action_test_connection">
<?php p($l->t('Test Configuration'));?>
</button>
diff --git a/apps/user_ldap/templates/part.wizard-groupfilter.php b/apps/user_ldap/templates/part.wizard-groupfilter.php
index 1953d2eaa6e..bfcfd115218 100644
--- a/apps/user_ldap/templates/part.wizard-groupfilter.php
+++ b/apps/user_ldap/templates/part.wizard-groupfilter.php
@@ -5,31 +5,48 @@
</p>
<p>
<label for="ldap_groupfilter_objectclass">
- <?php p($l->t('only those object classes:'));?>
+ <?php p($l->t('Only these object classes:'));?>
</label>
<select id="ldap_groupfilter_objectclass" multiple="multiple"
- name="ldap_groupfilter_objectclass">
+ name="ldap_groupfilter_objectclass" class="multiSelectPlugin">
</select>
</p>
<p>
<label for="ldap_groupfilter_groups">
- <?php p($l->t('only from those groups:'));?>
+ <?php p($l->t('Only from these groups:'));?>
</label>
+ <input type="text" class="ldapManyGroupsSupport ldapManyGroupsSearch hidden" placeholder="<?php p($l->t('Search groups'));?>" />
+
<select id="ldap_groupfilter_groups" multiple="multiple"
- name="ldap_groupfilter_groups">
+ name="ldap_groupfilter_groups" class="multiSelectPlugin">
</select>
+
+ </p>
+ <p class="ldapManyGroupsSupport hidden">
+ <label></label>
+ <select class="ldapGroupList ldapGroupListAvailable" multiple="multiple"
+ title="<?php p($l->t('Available groups'));?>"></select>
+ <span>
+ <button class="ldapGroupListSelect" type="button">&gt;</button><br/>
+ <button class="ldapGroupListDeselect" type="button">&lt;</button>
+ </span>
+ <select class="ldapGroupList ldapGroupListSelected" multiple="multiple"
+ title="<?php p($l->t('Selected groups'));?>"></select>
</p>
<p>
- <label><a id='toggleRawGroupFilter'>↓ <?php p($l->t('Edit raw filter instead'));?></a></label>
+ <label><a id='toggleRawGroupFilter' class='ldapToggle'>↓ <?php p($l->t('Edit LDAP Query'));?></a></label>
+ </p>
+ <p id="ldapReadOnlyGroupFilterContainer" class="hidden ldapReadOnlyFilterContainer">
+ <label><?php p($l->t('LDAP Filter:'));?></label>
+ <span class="ldapFilterReadOnlyElement ldapInputColElement"></span>
</p>
<p id="rawGroupFilterContainer" class="invisible">
- <input type="text" id="ldap_group_filter" name="ldap_group_filter"
- class="lwautosave"
- placeholder="<?php p($l->t('Raw LDAP filter'));?>"
- title="<?php p($l->t('The filter specifies which LDAP groups shall have access to the %s instance.', $theme->getName()));?>"
- />
+ <textarea type="text" id="ldap_group_filter" name="ldap_group_filter"
+ placeholder="<?php p($l->t('Edit LDAP Query'));?>"
+ title="<?php p($l->t('The filter specifies which LDAP groups shall have access to the %s instance.', $theme->getName()));?>">
+ </textarea>
<button class="ldapGetEntryCount hidden" name="ldapGetEntryCount" type="button">
<?php p($l->t('Test Filter'));?>
</button>
@@ -38,7 +55,10 @@
<div class="ldapWizardInfo invisible">&nbsp;</div>
</p>
<p class="ldap_count">
- <span id="ldap_group_count">0 <?php p($l->t('groups found'));?></span>
+ <button class="ldapGetEntryCount ldapGetGroupCount" name="ldapGetEntryCount" type="button">
+ <?php p($l->t('Verify settings and count groups'));?>
+ </button>
+ <span id="ldap_group_count"></span>
</p>
<?php print_unescaped($_['wizardControls']); ?>
</div>
diff --git a/apps/user_ldap/templates/part.wizard-loginfilter.php b/apps/user_ldap/templates/part.wizard-loginfilter.php
index 3dde46fa979..fa17a9b430b 100644
--- a/apps/user_ldap/templates/part.wizard-loginfilter.php
+++ b/apps/user_ldap/templates/part.wizard-loginfilter.php
@@ -1,23 +1,25 @@
<fieldset id="ldapWizard3">
<div>
<p>
- <?php p($l->t('Users login with this attribute:'));?>
+ <?php p($l->t('When logging in, %s will find the user based on the following attributes:', $theme->getName()));?>
</p>
<p>
<label for="ldap_loginfilter_username">
- <?php p($l->t('LDAP Username:'));?>
+ <?php p($l->t('LDAP / AD Username:'));?>
</label>
<input type="checkbox" id="ldap_loginfilter_username"
- name="ldap_loginfilter_username" value="1" class="lwautosave" />
+ title="<?php p($l->t('Allows login against the LDAP / AD username, which is either uid or samaccountname and will be detected.'));?>"
+ name="ldap_loginfilter_username" value="1" />
</p>
<p>
<label for="ldap_loginfilter_email">
- <?php p($l->t('LDAP Email Address:'));?>
+ <?php p($l->t('LDAP / AD Email Address:'));?>
</label>
<input type="checkbox" id="ldap_loginfilter_email"
- name="ldap_loginfilter_email" value="1" class="lwautosave" />
+ title="<?php p($l->t('Allows login against an email attribute. Mail and mailPrimaryAddress will be allowed.'));?>"
+ name="ldap_loginfilter_email" value="1" />
</p>
<p>
<label for="ldap_loginfilter_attributes">
@@ -25,23 +27,35 @@
</label>
<select id="ldap_loginfilter_attributes" multiple="multiple"
- name="ldap_loginfilter_attributes">
+ name="ldap_loginfilter_attributes" class="multiSelectPlugin">
</select>
</p>
<p>
- <label><a id='toggleRawLoginFilter'>↓ <?php p($l->t('Edit raw filter instead'));?></a></label>
+ <label><a id='toggleRawLoginFilter' class='ldapToggle'>↓ <?php p($l->t('Edit LDAP Query'));?></a></label>
+ </p>
+ <p id="ldapReadOnlyLoginFilterContainer" class="hidden ldapReadOnlyFilterContainer">
+ <label><?php p($l->t('LDAP Filter:'));?></label>
+ <span class="ldapFilterReadOnlyElement ldapInputColElement"></span>
</p>
<p id="rawLoginFilterContainer" class="invisible">
- <input type="text" id="ldap_login_filter" name="ldap_login_filter"
- class="lwautosave"
- placeholder="<?php p($l->t('Raw LDAP filter'));?>"
- title="<?php p($l->t('Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action. Example: "uid=%%uid"'));?>"
- />
+ <textarea type="text" id="ldap_login_filter" name="ldap_login_filter"
+ class="ldapFilterInputElement"
+ placeholder="<?php p($l->t('Edit LDAP Query'));?>"
+ title="<?php p($l->t('Defines the filter to apply, when login is attempted. %%uid replaces the username in the login action. Example: "uid=%%uid"'));?>">
+ </textarea>
</p>
<p>
<div class="ldapWizardInfo invisible">&nbsp;</div>
</p>
-
+ <p class="ldap_verify">
+ <input type="text" id="ldap_test_loginname" name="ldap_test_loginname"
+ placeholder="<?php p($l->t('Test Loginname'));?>"
+ class="ldapVerifyInput"
+ title="Attempts to receive a DN for the given loginname and the current login filter"/>
+ <button class="ldapVerifyLoginName" name="ldapTestLoginSettings" type="button">
+ <?php p($l->t('Verify settings'));?>
+ </button>
+ </p>
<?php print_unescaped($_['wizardControls']); ?>
</div>
</fieldset> \ No newline at end of file
diff --git a/apps/user_ldap/templates/part.wizard-server.php b/apps/user_ldap/templates/part.wizard-server.php
index c1744143f98..3ce912fac4a 100644
--- a/apps/user_ldap/templates/part.wizard-server.php
+++ b/apps/user_ldap/templates/part.wizard-server.php
@@ -22,32 +22,41 @@
}
}
?>
- <option value="NEW"><?php p($l->t('Add Server Configuration'));?></option>
</select>
+ <button type="button" id="ldap_action_add_configuration"
+ name="ldap_action_add_configuration" class="icon-add"
+ title="Adds a new and blank configuration">&nbsp;</button>
+ <button type="button" id="ldap_action_copy_configuration"
+ name="ldap_action_copy_configuration"
+ class="ldapIconCopy icon-default-style"
+ title="Copy current configuration into new directory binding">&nbsp;</button>
<button type="button" id="ldap_action_delete_configuration"
- name="ldap_action_delete_configuration"><?php p($l->t('Delete Configuration'));?></button>
+ name="ldap_action_delete_configuration" class="icon-delete"
+ title="Delete the current configuration">&nbsp;</button>
</p>
<div class="hostPortCombinator">
<div class="tablerow">
<div class="tablecell">
<div class="table">
- <input type="text" class="host tablecell lwautosave" id="ldap_host"
+ <input type="text" class="host" id="ldap_host"
name="ldap_host"
placeholder="<?php p($l->t('Host'));?>"
title="<?php p($l->t('You can omit the protocol, except you require SSL. Then start with ldaps://'));?>"
/>
<span>
<input type="number" id="ldap_port" name="ldap_port"
- class="lwautosave"
placeholder="<?php p($l->t('Port'));?>" />
+ <button class="ldapDetectPort" name="ldapDetectPort" type="button">
+ <?php p($l->t('Detect Port'));?>
+ </button>
</span>
</div>
</div>
</div>
<div class="tablerow">
<input type="text" id="ldap_dn" name="ldap_dn"
- class="tablecell lwautosave"
+ class="tablecell"
placeholder="<?php p($l->t('User DN'));?>" autocomplete="off"
title="<?php p($l->t('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.'));?>"
/>
@@ -55,7 +64,7 @@
<div class="tablerow">
<input type="password" id="ldap_agent_password"
- class="tablecell lwautosave" name="ldap_agent_password"
+ class="tablecell" name="ldap_agent_password"
placeholder="<?php p($l->t('Password'));?>" autocomplete="off"
title="<?php p($l->t('For anonymous access, leave DN and Password empty.'));?>"
/>
@@ -63,15 +72,21 @@
<div class="tablerow">
<textarea id="ldap_base" name="ldap_base"
- class="tablecell lwautosave"
+ class="tablecell"
placeholder="<?php p($l->t('One Base DN per line'));?>"
title="<?php p($l->t('You can specify Base DN for users and groups in the Advanced tab'));?>">
</textarea>
+ <button class="ldapDetectBase" name="ldapDetectBase" type="button">
+ <?php p($l->t('Detect Base DN'));?>
+ </button>
+ <button class="ldapTestBase" name="ldapTestBase" type="button">
+ <?php p($l->t('Test Base DN'));?>
+ </button>
</div>
<div class="tablerow left">
<input type="checkbox" id="ldap_experienced_admin" value="1"
- name="ldap_experienced_admin" class="tablecell lwautosave"
+ name="ldap_experienced_admin" class="tablecell"
title="<?php p($l->t('Avoids automatic LDAP requests. Better for bigger setups, but requires some LDAP knowledge.'));?>"
/>
<label for="ldap_experienced_admin" class="tablecell">
diff --git a/apps/user_ldap/templates/part.wizard-userfilter.php b/apps/user_ldap/templates/part.wizard-userfilter.php
index 99a6e75370b..691c41a66a6 100644
--- a/apps/user_ldap/templates/part.wizard-userfilter.php
+++ b/apps/user_ldap/templates/part.wizard-userfilter.php
@@ -5,40 +5,61 @@
</p>
<p>
<label for="ldap_userfilter_objectclass">
- <?php p($l->t('only those object classes:'));?>
+ <?php p($l->t('Only these object classes:'));?>
</label>
<select id="ldap_userfilter_objectclass" multiple="multiple"
- name="ldap_userfilter_objectclass">
+ name="ldap_userfilter_objectclass" class="multiSelectPlugin">
</select>
</p>
<p>
+ <label></label>
+ <span class="ldapInputColElement"><?php p($l->t('The most common object classes for users are organizationalPerson, person, user, and inetOrgPerson. If you are not sure which object class to select, please consult your directory admin.'));?></span>
+ </p>
+ <p>
<label for="ldap_userfilter_groups">
- <?php p($l->t('only from those groups:'));?>
+ <?php p($l->t('Only from these groups:'));?>
</label>
+ <input type="text" class="ldapManyGroupsSupport ldapManyGroupsSearch hidden" placeholder="<?php p($l->t('Search groups'));?>" />
+
<select id="ldap_userfilter_groups" multiple="multiple"
- name="ldap_userfilter_groups">
+ name="ldap_userfilter_groups" class="multiSelectPlugin">
</select>
</p>
+ <p class="ldapManyGroupsSupport hidden">
+ <label></label>
+ <select class="ldapGroupList ldapGroupListAvailable" multiple="multiple"
+ title="<?php p($l->t('Available groups'));?>"></select>
+ <span>
+ <button class="ldapGroupListSelect" type="button">&gt;</button><br/>
+ <button class="ldapGroupListDeselect" type="button">&lt;</button>
+ </span>
+ <select class="ldapGroupList ldapGroupListSelected" multiple="multiple"
+ title="<?php p($l->t('Selected groups'));?>"></select>
+ </p>
<p>
- <label><a id='toggleRawUserFilter'>↓ <?php p($l->t('Edit raw filter instead'));?></a></label>
- </p>
- <p id="rawUserFilterContainer" class="invisible">
- <input type="text" id="ldap_userlist_filter" name="ldap_userlist_filter"
- class="lwautosave"
- placeholder="<?php p($l->t('Raw LDAP filter'));?>"
- title="<?php p($l->t('The filter specifies which LDAP users shall have access to the %s instance.', $theme->getName()));?>"
- />
- <button class="ldapGetEntryCount hidden" name="ldapGetEntryCount" type="button">
- <?php p($l->t('Test Filter'));?>
- </button>
+ <label><a id='toggleRawUserFilter' class='ldapToggle'>↓ <?php p($l->t('Edit LDAP Query'));?></a></label>
+ </p>
+ <p id="ldapReadOnlyUserFilterContainer" class="hidden ldapReadOnlyFilterContainer">
+ <label><?php p($l->t('LDAP Filter:'));?></label>
+ <span class="ldapFilterReadOnlyElement ldapInputColElement"></span>
+ </p>
+ <p id="rawUserFilterContainer">
+ <textarea type="text" id="ldap_userlist_filter" name="ldap_userlist_filter"
+ class="ldapFilterInputElement"
+ placeholder="<?php p($l->t('Edit LDAP Query'));?>"
+ title="<?php p($l->t('The filter specifies which LDAP users shall have access to the %s instance.', $theme->getName()));?>">
+ </textarea>
</p>
<p>
<div class="ldapWizardInfo invisible">&nbsp;</div>
</p>
<p class="ldap_count">
- <span id="ldap_user_count">0 <?php p($l->t('users found'));?></span>
+ <button class="ldapGetEntryCount ldapGetUserCount" name="ldapGetEntryCount" type="button">
+ <?php p($l->t('Verify settings and count users'));?>
+ </button>
+ <span id="ldap_user_count"></span>
</p>
<?php print_unescaped($_['wizardControls']); ?>
</div>
diff --git a/apps/user_ldap/templates/settings.php b/apps/user_ldap/templates/settings.php
index 6aa2183726b..f40eba005d8 100644
--- a/apps/user_ldap/templates/settings.php
+++ b/apps/user_ldap/templates/settings.php
@@ -1,3 +1,56 @@
+<?php
+
+vendor_script('user_ldap', 'ui-multiselect/src/jquery.multiselect');
+
+vendor_style('user_ldap', 'ui-multiselect/jquery.multiselect');
+
+script('user_ldap', [
+ 'wizard/controller',
+ 'wizard/configModel',
+ 'wizard/view',
+ 'wizard/wizardObject',
+ 'wizard/wizardTabGeneric',
+ 'wizard/wizardTabElementary',
+ 'wizard/wizardTabAbstractFilter',
+ 'wizard/wizardTabUserFilter',
+ 'wizard/wizardTabLoginFilter',
+ 'wizard/wizardTabGroupFilter',
+ 'wizard/wizardTabAdvanced',
+ 'wizard/wizardTabExpert',
+ 'wizard/wizardDetectorQueue',
+ 'wizard/wizardDetectorGeneric',
+ 'wizard/wizardDetectorPort',
+ 'wizard/wizardDetectorBaseDN',
+ 'wizard/wizardDetectorFeatureAbstract',
+ 'wizard/wizardDetectorUserObjectClasses',
+ 'wizard/wizardDetectorGroupObjectClasses',
+ 'wizard/wizardDetectorGroupsForUsers',
+ 'wizard/wizardDetectorGroupsForGroups',
+ 'wizard/wizardDetectorSimpleRequestAbstract',
+ 'wizard/wizardDetectorFilterUser',
+ 'wizard/wizardDetectorFilterLogin',
+ 'wizard/wizardDetectorFilterGroup',
+ 'wizard/wizardDetectorUserCount',
+ 'wizard/wizardDetectorGroupCount',
+ 'wizard/wizardDetectorEmailAttribute',
+ 'wizard/wizardDetectorUserDisplayNameAttribute',
+ 'wizard/wizardDetectorUserGroupAssociation',
+ 'wizard/wizardDetectorAvailableAttributes',
+ 'wizard/wizardDetectorTestAbstract',
+ 'wizard/wizardDetectorTestLoginName',
+ 'wizard/wizardDetectorTestBaseDN',
+ 'wizard/wizardDetectorTestConfiguration',
+ 'wizard/wizardDetectorClearUserMappings',
+ 'wizard/wizardDetectorClearGroupMappings',
+ 'wizard/wizardFilterOnType',
+ 'wizard/wizardFilterOnTypeFactory',
+ 'wizard/wizard'
+]);
+
+style('user_ldap', 'settings');
+
+?>
+
<form id="ldap" class="section" action="#" method="post">
<h2><?php p($l->t('LDAP')); ?></h2>
@@ -65,5 +118,6 @@
<?php print_unescaped($_['settingControls']); ?>
</fieldset>
</div>
-
+ <!-- Spinner Template -->
+ <img class="ldapSpinner hidden" src="<?php p(\OCP\Util::imagePath('core', 'loading.gif')); ?>">
</form>
diff --git a/apps/user_ldap/tests/user_ldap.php b/apps/user_ldap/tests/user_ldap.php
index fa3afe9c511..b9beed1d35a 100644
--- a/apps/user_ldap/tests/user_ldap.php
+++ b/apps/user_ldap/tests/user_ldap.php
@@ -108,12 +108,6 @@ class Test_User_Ldap_Direct extends \Test\TestCase {
* @return void
*/
private function prepareAccessForCheckPassword(&$access, $noDisplayName = false) {
- $access->expects($this->once())
- ->method('escapeFilterPart')
- ->will($this->returnCallback(function($uid) {
- return $uid;
- }));
-
$access->connection->expects($this->any())
->method('__get')
->will($this->returnCallback(function($name) {
@@ -132,6 +126,15 @@ class Test_User_Ldap_Direct extends \Test\TestCase {
return array();
}));
+ $access->expects($this->any())
+ ->method('fetchUsersByLoginName')
+ ->will($this->returnCallback(function($uid) {
+ if($uid === 'roland') {
+ return array(array('dn' => 'dnOfRoland,dc=test'));
+ }
+ return array();
+ }));
+
$retVal = 'gunslinger';
if($noDisplayName === true) {
$retVal = false;
diff --git a/apps/user_ldap/user_ldap.php b/apps/user_ldap/user_ldap.php
index ea1371c14d3..54e14c093f3 100644
--- a/apps/user_ldap/user_ldap.php
+++ b/apps/user_ldap/user_ldap.php
@@ -79,14 +79,10 @@ class USER_LDAP extends BackendUtility implements \OCP\IUserBackend, \OCP\UserIn
* Check if the password is correct without logging in the user
*/
public function checkPassword($uid, $password) {
- $uid = $this->access->escapeFilterPart($uid);
-
//find out dn of the user name
$attrs = array($this->access->connection->ldapUserDisplayName, 'dn',
'uid', 'samaccountname');
- $filter = \OCP\Util::mb_str_replace(
- '%uid', $uid, $this->access->connection->ldapLoginFilter, 'UTF-8');
- $users = $this->access->fetchListOfUsers($filter, $attrs);
+ $users = $this->access->fetchUsersByLoginName($uid, $attrs);
if(count($users) < 1) {
return false;
}
diff --git a/config/config.sample.php b/config/config.sample.php
index 60932ab7d9b..e3b81f69f6b 100644
--- a/config/config.sample.php
+++ b/config/config.sample.php
@@ -577,6 +577,15 @@ $CONFIG = array(
'appstoreurl' => 'https://api.owncloud.com/v1',
/**
+ * Whether to show experimental apps in the appstore interface
+ *
+ * Experimental apps are not checked for security issues and are new or known
+ * to be unstable and under heavy development. Installing these can cause data
+ * loss or security breaches.
+ */
+'appstore.experimental.enabled' => false,
+
+/**
* Use the ``apps_paths`` parameter to set the location of the Apps directory,
* which should be scanned for available apps, and where user-specific apps
* should be installed from the Apps store. The ``path`` defines the absolute
@@ -620,12 +629,12 @@ $CONFIG = array(
* The maximum width, in pixels, of a preview. A value of ``null`` means there
* is no limit.
*/
-'preview_max_x' => null,
+'preview_max_x' => 2048,
/**
* The maximum height, in pixels, of a preview. A value of ``null`` means there
* is no limit.
*/
-'preview_max_y' => null,
+'preview_max_y' => 2048,
/**
* If a lot of small pictures are stored on the ownCloud instance and the
* preview system generates blurry previews, you might want to consider setting
diff --git a/console.php b/console.php
index a4d829f683a..7536908a5c1 100644
--- a/console.php
+++ b/console.php
@@ -24,6 +24,7 @@
*/
use Symfony\Component\Console\Application;
+use Symfony\Component\Console\Input\ArgvInput;
define('OC_CONSOLE', 1);
@@ -71,6 +72,18 @@ try {
} else {
echo "ownCloud is not installed - only a limited number of commands are available" . PHP_EOL;
}
+ $input = new ArgvInput();
+ if ($input->getFirstArgument() !== 'check') {
+ $errors = \OC_Util::checkServer(\OC::$server->getConfig());
+ if (!empty($errors)) {
+ foreach ($errors as $error) {
+ echo $error['error'] . "\n";
+ echo $error['hint'] . "\n\n";
+ }
+ exit(1);
+ }
+ }
+
$application->run();
} catch (Exception $ex) {
echo "An unhandled exception has been thrown:" . PHP_EOL;
diff --git a/core/ajax/share.php b/core/ajax/share.php
index 05756fc1c8b..bc83c41642c 100644
--- a/core/ajax/share.php
+++ b/core/ajax/share.php
@@ -255,6 +255,14 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo
$usergroups = OC_Group::getUserGroups(OC_User::getUser());
$groups = array_intersect($groups, $usergroups);
}
+
+ $sharedUsers = [];
+ $sharedGroups = [];
+ if (isset($_GET['itemShares'])) {
+ $sharedUsers = isset($_GET['itemShares'][OCP\Share::SHARE_TYPE_USER]) ? $_GET['itemShares'][OCP\Share::SHARE_TYPE_USER] : [];
+ $sharedGroups = isset($_GET['itemShares'][OCP\Share::SHARE_TYPE_GROUP]) ? $_GET['itemShares'][OCP\Share::SHARE_TYPE_GROUP] : [];
+ }
+
$count = 0;
$users = array();
$limit = 0;
@@ -266,8 +274,13 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo
} else {
$users = OC_User::getDisplayNames((string)$_GET['search'], $limit, $offset);
}
+
$offset += $limit;
foreach ($users as $uid => $displayName) {
+ if (in_array($uid, $sharedUsers)) {
+ continue;
+ }
+
if ((!isset($_GET['itemShares'])
|| !is_array((string)$_GET['itemShares'][OCP\Share::SHARE_TYPE_USER])
|| !in_array($uid, (string)$_GET['itemShares'][OCP\Share::SHARE_TYPE_USER]))
@@ -288,6 +301,10 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo
$l = \OC::$server->getL10N('core');
foreach ($groups as $group) {
+ if (in_array($group, $sharedGroups)) {
+ continue;
+ }
+
if ($count < 15) {
if (!isset($_GET['itemShares'])
|| !isset($_GET['itemShares'][OCP\Share::SHARE_TYPE_GROUP])
diff --git a/core/command/app/listapps.php b/core/command/app/listapps.php
index dbb04c41eed..37a1d645ed4 100644
--- a/core/command/app/listapps.php
+++ b/core/command/app/listapps.php
@@ -23,21 +23,23 @@
namespace OC\Core\Command\App;
-use Symfony\Component\Console\Command\Command;
+use OC\Core\Command\Base;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
-class ListApps extends Command {
+class ListApps extends Base {
protected function configure() {
+ parent::configure();
+
$this
->setName('app:list')
- ->setDescription('List all available apps');
+ ->setDescription('List all available apps')
+ ;
}
protected function execute(InputInterface $input, OutputInterface $output) {
$apps = \OC_App::getAllApps();
- $enabledApps = array();
- $disabledApps = array();
+ $enabledApps = $disabledApps = [];
$versions = \OC_App::getAppVersions();
//sort enabled apps above disabled apps
@@ -49,15 +51,39 @@ class ListApps extends Command {
}
}
+ $apps = ['enabled' => [], 'disabled' => []];
+
sort($enabledApps);
- sort($disabledApps);
- $output->writeln('Enabled:');
foreach ($enabledApps as $app) {
- $output->writeln(' - ' . $app . (isset($versions[$app]) ? ' (' . $versions[$app] . ')' : ''));
+ $apps['enabled'][$app] = (isset($versions[$app])) ? $versions[$app] : '';
}
- $output->writeln('Disabled:');
+
+ sort($disabledApps);
foreach ($disabledApps as $app) {
- $output->writeln(' - ' . $app . (isset($versions[$app]) ? ' (' . $versions[$app] . ')' : ''));
+ $apps['disabled'][$app] = (isset($versions[$app])) ? $versions[$app] : '';
+ }
+
+ $this->writeAppList($input, $output, $apps);
+ }
+
+ /**
+ * @param InputInterface $input
+ * @param OutputInterface $output
+ * @param array $items
+ */
+ protected function writeAppList(InputInterface $input, OutputInterface $output, $items) {
+ switch ($input->getOption('output')) {
+ case 'plain':
+ $output->writeln('Enabled:');
+ parent::writeArrayInOutputFormat($input, $output, $items['enabled']);
+
+ $output->writeln('Disabled:');
+ parent::writeArrayInOutputFormat($input, $output, $items['disabled']);
+ break;
+
+ default:
+ parent::writeArrayInOutputFormat($input, $output, $items);
+ break;
}
}
}
diff --git a/core/command/base.php b/core/command/base.php
new file mode 100644
index 00000000000..c2d5cf97f02
--- /dev/null
+++ b/core/command/base.php
@@ -0,0 +1,62 @@
+<?php
+/**
+ * @author Joas Schilling <nickvergessen@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Core\Command;
+
+use Symfony\Component\Console\Command\Command;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Input\InputOption;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class Base extends Command {
+ protected function configure() {
+ $this
+ ->addOption(
+ 'output',
+ null,
+ InputOption::VALUE_OPTIONAL,
+ 'Output format (plain, json or json_pretty, default is plain)',
+ 'plain'
+ )
+ ;
+ }
+
+ /**
+ * @param InputInterface $input
+ * @param OutputInterface $output
+ * @param array $items
+ */
+ protected function writeArrayInOutputFormat(InputInterface $input, OutputInterface $output, $items) {
+ switch ($input->getOption('output')) {
+ case 'json':
+ $output->writeln(json_encode($items));
+ break;
+ case 'json_pretty':
+ $output->writeln(json_encode($items, JSON_PRETTY_PRINT));
+ break;
+ default:
+ foreach ($items as $key => $item) {
+ $output->writeln(' - ' . (!is_int($key) ? $key . ': ' : '') . $item);
+ }
+ break;
+ }
+ }
+}
diff --git a/core/command/check.php b/core/command/check.php
new file mode 100644
index 00000000000..ddfe9b73bba
--- /dev/null
+++ b/core/command/check.php
@@ -0,0 +1,41 @@
+<?php
+
+namespace OC\Core\Command;
+
+use OCP\IConfig;
+use Symfony\Component\Console\Input\InputInterface;
+use Symfony\Component\Console\Output\OutputInterface;
+
+class Check extends Base {
+ /**
+ * @var IConfig
+ */
+ private $config;
+
+ public function __construct(IConfig $config) {
+ parent::__construct();
+ $this->config = $config;
+ }
+
+ protected function configure() {
+ parent::configure();
+
+ $this
+ ->setName('check')
+ ->setDescription('check dependencies of the server environment')
+ ;
+ }
+
+ protected function execute(InputInterface $input, OutputInterface $output) {
+ $errors = \OC_Util::checkServer($this->config);
+ if (!empty($errors)) {
+ $errors = array_map(function($item) {
+ return (string) $item['error'];
+ }, $errors);
+
+ $this->writeArrayInOutputFormat($input, $output, $errors);
+ return 1;
+ }
+ return 0;
+ }
+}
diff --git a/core/command/status.php b/core/command/status.php
index 8c6653a8910..3859f69febc 100644
--- a/core/command/status.php
+++ b/core/command/status.php
@@ -22,12 +22,13 @@
namespace OC\Core\Command;
-use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
-class Status extends Command {
+class Status extends Base {
protected function configure() {
+ parent::configure();
+
$this
->setName('status')
->setDescription('show some status information')
@@ -41,6 +42,7 @@ class Status extends Command {
'versionstring' => \OC_Util::getVersionString(),
'edition' => \OC_Util::getEditionString(),
);
- print_r($values);
+
+ $this->writeArrayInOutputFormat($input, $output, $values);
}
}
diff --git a/core/command/user/add.php b/core/command/user/add.php
index 1ae0ffbe2ad..60c70bf13dd 100644
--- a/core/command/user/add.php
+++ b/core/command/user/add.php
@@ -58,10 +58,10 @@ class Add extends Command {
'User ID used to login (must only contain a-z, A-Z, 0-9, -, _ and @)'
)
->addOption(
- 'password',
- 'p',
- InputOption::VALUE_OPTIONAL,
- ''
+ 'password-from-env',
+ null,
+ InputOption::VALUE_NONE,
+ 'read password from environment variable OC_PASS'
)
->addOption(
'display-name',
@@ -84,14 +84,33 @@ class Add extends Command {
return 1;
}
- $password = $input->getOption('password');
- while (!$password) {
- $question = new Question('Please enter a non-empty password:');
- $question->setHidden(true);
- $question->setHiddenFallback(false);
+ if ($input->getOption('password-from-env')) {
+ $password = getenv('OC_PASS');
+ if (!$password) {
+ $output->writeln('<error>--password-from-env given, but OC_PASS is empty!</error>');
+ return 1;
+ }
+ } elseif ($input->isInteractive()) {
+ /** @var $dialog \Symfony\Component\Console\Helper\DialogHelper */
+ $dialog = $this->getHelperSet()->get('dialog');
+ $password = $dialog->askHiddenResponse(
+ $output,
+ '<question>Enter password: </question>',
+ false
+ );
+ $confirm = $dialog->askHiddenResponse(
+ $output,
+ '<question>Confirm password: </question>',
+ false
+ );
- $helper = $this->getHelper('question');
- $password = $helper->ask($input, $output, $question);
+ if ($password !== $confirm) {
+ $output->writeln("<error>Passwords did not match!</error>");
+ return 1;
+ }
+ } else {
+ $output->writeln("<error>Interactive input or --password-from-env is needed for entering a password!</error>");
+ return 1;
}
$user = $this->userManager->createUser(
diff --git a/core/command/user/resetpassword.php b/core/command/user/resetpassword.php
index 3afbfeeb9b9..3e16c8f79a5 100644
--- a/core/command/user/resetpassword.php
+++ b/core/command/user/resetpassword.php
@@ -26,6 +26,7 @@ namespace OC\Core\Command\User;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputArgument;
+use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
class ResetPassword extends Command {
@@ -47,6 +48,12 @@ class ResetPassword extends Command {
InputArgument::REQUIRED,
'Username to reset password'
)
+ ->addOption(
+ 'password-from-env',
+ null,
+ InputOption::VALUE_NONE,
+ 'read password from environment variable OC_PASS'
+ )
;
}
@@ -60,7 +67,13 @@ class ResetPassword extends Command {
return 1;
}
- if ($input->isInteractive()) {
+ if ($input->getOption('password-from-env')) {
+ $password = getenv('OC_PASS');
+ if (!$password) {
+ $output->writeln('<error>--password-from-env given, but OC_PASS is empty!</error>');
+ return 1;
+ }
+ } elseif ($input->isInteractive()) {
/** @var $dialog \Symfony\Component\Console\Helper\DialogHelper */
$dialog = $this->getHelperSet()->get('dialog');
@@ -84,20 +97,20 @@ class ResetPassword extends Command {
false
);
- if ($password === $confirm) {
- $success = $user->setPassword($password);
- if ($success) {
- $output->writeln("<info>Successfully reset password for " . $username . "</info>");
- } else {
- $output->writeln("<error>Error while resetting password!</error>");
- return 1;
- }
- } else {
+ if ($password !== $confirm) {
$output->writeln("<error>Passwords did not match!</error>");
return 1;
}
} else {
- $output->writeln("<error>Interactive input is needed for entering a new password!</error>");
+ $output->writeln("<error>Interactive input or --password-from-env is needed for entering a new password!</error>");
+ return 1;
+ }
+
+ $success = $user->setPassword($password);
+ if ($success) {
+ $output->writeln("<info>Successfully reset password for " . $username . "</info>");
+ } else {
+ $output->writeln("<error>Error while resetting password!</error>");
return 1;
}
}
diff --git a/core/css/apps.css b/core/css/apps.css
index e8c60bd4773..9b662cc31af 100644
--- a/core/css/apps.css
+++ b/core/css/apps.css
@@ -457,11 +457,16 @@
width: 100%;
padding: 0;
margin: 0;
- background-color: transparent; background-image: url('../img/actions/settings.svg');
- background-position: 10px center; background-repeat: no-repeat;
+ background-color: transparent;
+ background-image: url('../img/actions/settings.svg');
+ background-position: 14px center;
+ background-repeat: no-repeat;
box-shadow: none;
border: 0;
border-radius: 0;
+ text-align: left;
+ padding-left: 42px;
+ font-weight: normal;
}
.settings-button:hover,
.settings-button:focus {
diff --git a/core/css/icons.css b/core/css/icons.css
index ecf6b17995d..0f602515883 100644
--- a/core/css/icons.css
+++ b/core/css/icons.css
@@ -79,6 +79,9 @@
.icon-info {
background-image: url('../img/actions/info.svg');
}
+.icon-info-white {
+ background-image: url('../img/actions/info-white.svg');
+}
.icon-logout {
background-image: url('../img/actions/logout.svg');
diff --git a/core/css/share.css b/core/css/share.css
index 448f0bac239..bcfd90e4204 100644
--- a/core/css/share.css
+++ b/core/css/share.css
@@ -37,8 +37,12 @@
display: none !important;
}
+#dropdown .shareWithRemoteInfo {
+ padding: 11px 20px;
+}
+
#dropdown .avatar {
- margin-right: 2px;
+ margin-right: 8px;
display: inline-block;
overflow: hidden;
vertical-align: middle;
@@ -108,7 +112,8 @@ a.unshare {
}
#dropdown input[type="text"],#dropdown input[type="password"] {
- width:90%;
+ width: 86%;
+ margin-left: 7px;
}
#dropdown form {
diff --git a/core/css/styles.css b/core/css/styles.css
index cbce78c525b..3df2abd49d6 100644
--- a/core/css/styles.css
+++ b/core/css/styles.css
@@ -409,7 +409,9 @@ input[type="submit"].enabled {
padding: 5px;
}
-#body-login div.buttons { text-align:center; }
+#body-login div.buttons {
+ text-align: center;
+}
#body-login p.info {
width: 22em;
margin: 0 auto;
@@ -420,14 +422,26 @@ input[type="submit"].enabled {
padding: 13px;
margin: -13px;
}
-#body-login #submit.login { margin-right:7px; } /* quick fix for log in button not being aligned with input fields, should be properly fixed by input field width later */
-
-#body-login form { width:22em; margin:2em auto 2em; padding:0; }
+/* quick fix for log in button not being aligned with input fields, should be properly fixed by input field width later */
+#body-login #submit.login {
+ margin-right: 7px;
+}
+#body-login form {
+ width: 22em;
+ margin: 2em auto 2em;
+ padding: 0;
+}
#body-login form fieldset {
margin-bottom: 20px;
text-align: left;
}
-#body-login form #adminaccount { margin-bottom:15px; }
+#body-login form #sqliteInformation {
+ margin-top: -20px;
+ margin-bottom: 20px;
+}
+#body-login form #adminaccount {
+ margin-bottom: 15px;
+}
#body-login form fieldset legend, #datadirContent label {
width: 100%;
}
@@ -445,6 +459,9 @@ input[type="submit"].enabled {
vertical-align: bottom; /* adjust position of Advanced dropdown arrow */
margin-left: -4px;
}
+#body-login .icon-info-white {
+ padding: 10px;
+}
/* strengthify wrapper */
#body-login .strengthify-wrapper {
@@ -618,7 +635,9 @@ label.infield {
/* Warnings and errors are the same */
-#body-login .warning, #body-login .update, #body-login .error {
+#body-login .warning,
+#body-login .update,
+#body-login .error {
display: block;
padding: 10px;
background-color: rgba(0,0,0,.3);
@@ -670,7 +689,7 @@ label.infield {
}
.warning-input {
- border-color: #cc3333 !important;
+ border-color: #ce3702 !important;
}
/* Fixes for log in page, TODO should be removed some time */
diff --git a/core/img/actions/info-white.png b/core/img/actions/info-white.png
new file mode 100644
index 00000000000..670d7309c4e
--- /dev/null
+++ b/core/img/actions/info-white.png
Binary files differ
diff --git a/core/img/actions/info-white.svg b/core/img/actions/info-white.svg
new file mode 100644
index 00000000000..d1f9ddb78cf
--- /dev/null
+++ b/core/img/actions/info-white.svg
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="16" width="16" version="1.0" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <rect style="color:#000000" fill-opacity="0" height="97.986" width="163.31" y="-32.993" x="-62.897"/>
+ <path d="m4.9999 7.4745c0.1553 0.3811 0.3254 0.6881 0.6445 0.2459 0.4066-0.2685 1.7587-1.4279 1.6616-0.3421-0.3681 2.0169-0.8342 4.0167-1.1711 6.0387-0.3916 1.115 0.635 2.068 1.6379 1.312 1.0779-0.503 1.9915-1.288 2.9275-2.012-0.144-0.322-0.25-0.789-0.596-0.346-0.4687 0.239-1.4695 1.317-1.6967 0.471 0.3154-2.181 0.9755-4.2953 1.3654-6.4616 0.3973-1.0049-0.3645-2.2233-1.3997-1.3634-1.2565 0.6173-2.2895 1.5844-3.3734 2.4575zm4.4593-7.4718c-1.3075-0.017336-1.9056 2.1455-0.6427 2.6795 1.0224 0.378 2.0768-0.7138 1.7898-1.7504-0.098-0.54186-0.598-0.96979-1.1471-0.92912h-0.000001z" fill="#fff"/>
+</svg>
diff --git a/core/img/actions/info.png b/core/img/actions/info.png
index cb25905a5c5..b280a019ab4 100644
--- a/core/img/actions/info.png
+++ b/core/img/actions/info.png
Binary files differ
diff --git a/core/img/actions/info.svg b/core/img/actions/info.svg
index 527c1d3dbd1..3be67826423 100644
--- a/core/img/actions/info.svg
+++ b/core/img/actions/info.svg
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="16" width="16" version="1.0" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
<rect style="color:#000000" fill-opacity="0" height="97.986" width="163.31" y="-32.993" x="-62.897"/>
- <path opacity=".7" d="m4.9999 7.4745c0.1553 0.3811 0.3254 0.6881 0.6445 0.2459 0.4066-0.2685 1.7587-1.4279 1.6616-0.3421-0.3681 2.0169-0.8342 4.0167-1.1711 6.0387-0.3916 1.115 0.635 2.068 1.6379 1.312 1.0779-0.503 1.9915-1.288 2.9275-2.012-0.144-0.322-0.25-0.789-0.596-0.346-0.4687 0.239-1.4695 1.317-1.6967 0.471 0.3154-2.181 0.9755-4.2953 1.3654-6.4616 0.3973-1.0049-0.3645-2.2233-1.3997-1.3634-1.2565 0.6173-2.2895 1.5844-3.3734 2.4575zm4.4593-7.4718c-1.3075-0.017336-1.9056 2.1455-0.6427 2.6795 1.0224 0.378 2.0768-0.7138 1.7898-1.7504-0.098-0.54186-0.598-0.96979-1.1471-0.92912h-0.000001z" fill="#1e1e1e"/>
+ <path opacity=".5" d="m4.9999 7.4745c0.1553 0.3811 0.3254 0.6881 0.6445 0.2459 0.4066-0.2685 1.7587-1.4279 1.6616-0.3421-0.3681 2.0169-0.8342 4.0167-1.1711 6.0387-0.3916 1.115 0.635 2.068 1.6379 1.312 1.0779-0.503 1.9915-1.288 2.9275-2.012-0.144-0.322-0.25-0.789-0.596-0.346-0.4687 0.239-1.4695 1.317-1.6967 0.471 0.3154-2.181 0.9755-4.2953 1.3654-6.4616 0.3973-1.0049-0.3645-2.2233-1.3997-1.3634-1.2565 0.6173-2.2895 1.5844-3.3734 2.4575zm4.4593-7.4718c-1.3075-0.017336-1.9056 2.1455-0.6427 2.6795 1.0224 0.378 2.0768-0.7138 1.7898-1.7504-0.098-0.54186-0.598-0.96979-1.1471-0.92912h-0.000001z"/>
</svg>
diff --git a/core/img/actions/search.png b/core/img/actions/search.png
index 82ebac97255..5f4767a6f46 100644
--- a/core/img/actions/search.png
+++ b/core/img/actions/search.png
Binary files differ
diff --git a/core/img/actions/search.svg b/core/img/actions/search.svg
index 7e7a41a2ea7..6024607dd0a 100644
--- a/core/img/actions/search.svg
+++ b/core/img/actions/search.svg
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="16" width="16" version="1.0" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
<rect style="color:#000000" fill-opacity="0" height="97.986" width="163.31" y="-32.993" x="-62.897"/>
- <path opacity=".7" style="color:#000000" d="m6 1c-2.7614 0-5 2.2386-5 5s2.2386 5 5 5c0.98478 0 1.8823-0.28967 2.6562-0.78125l4.4688 4.625c0.09558 0.10527 0.22619 0.16452 0.375 0.15625 0.14882-0.0083 0.3031-0.07119 0.40625-0.1875l0.9375-1.0625c0.19194-0.22089 0.19549-0.53592 0-0.71875l-4.594-4.406c0.478-0.7663 0.75-1.6555 0.75-2.625 0-2.7614-2.2386-5-5-5zm0 2c1.6569 0 3 1.3431 3 3s-1.3431 3-3 3-3-1.3431-3-3 1.3431-3 3-3z" fill="#1e1e1e"/>
+ <path opacity=".5" style="color:#000000" d="m6 1c-2.7614 0-5 2.2386-5 5s2.2386 5 5 5c0.98478 0 1.8823-0.28967 2.6562-0.78125l4.4688 4.625c0.09558 0.10527 0.22619 0.16452 0.375 0.15625 0.14882-0.0083 0.3031-0.07119 0.40625-0.1875l0.9375-1.0625c0.19194-0.22089 0.19549-0.53592 0-0.71875l-4.594-4.406c0.478-0.7663 0.75-1.6555 0.75-2.625 0-2.7614-2.2386-5-5-5zm0 2c1.6569 0 3 1.3431 3 3s-1.3431 3-3 3-3-1.3431-3-3 1.3431-3 3-3z"/>
</svg>
diff --git a/core/img/actions/settings.png b/core/img/actions/settings.png
index 28f01bc7927..3ab939ca37a 100644
--- a/core/img/actions/settings.png
+++ b/core/img/actions/settings.png
Binary files differ
diff --git a/core/img/actions/settings.svg b/core/img/actions/settings.svg
index 51bd7460389..251bd54c19d 100644
--- a/core/img/actions/settings.svg
+++ b/core/img/actions/settings.svg
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="16" width="16" version="1.0" xmlns:cc="http://creativecommons.org/ns#" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/">
- <g opacity=".7" transform="translate(0 -.056)" fill="#1e1e1e">
- <path fill="#1e1e1e" display="block" d="m6.9375 0.056c-0.2484 0-0.4375 0.18908-0.4375 0.4375v1.25c-0.5539 0.1422-1.0512 0.3719-1.5312 0.6563l-0.9063-0.9063c-0.17566-0.17566-0.44934-0.17566-0.625 0l-1.5 1.5c-0.17566 0.17566-0.17566 0.44934 0 0.625l0.9063 0.9063c-0.2844 0.48-0.5141 0.9773-0.6563 1.5312h-1.25c-0.24842 0-0.4375 0.1891-0.4375 0.4375v2.125c1e-8 0.24842 0.18908 0.4375 0.4375 0.4375h1.25c0.1422 0.5539 0.37188 1.0512 0.65625 1.5312l-0.9063 0.907c-0.17566 0.17566-0.17566 0.44934 0 0.625l1.5 1.5c0.17566 0.17566 0.44934 0.17566 0.625 0l0.9063-0.907c0.48 0.285 0.9773 0.514 1.5312 0.656v1.25c1e-7 0.24842 0.18908 0.4375 0.4375 0.4375h2.125c0.2484 0 0.4375-0.189 0.4375-0.438v-1.25c0.5539-0.1422 1.0512-0.37188 1.5312-0.65625l0.90625 0.90625c0.17566 0.17566 0.44934 0.17566 0.625 0l1.5-1.5c0.17566-0.17566 0.17566-0.44934 0-0.625l-0.906-0.906c0.285-0.48 0.514-0.9771 0.656-1.531h1.25c0.249 0 0.438-0.1891 0.438-0.4375v-2.125c0-0.2484-0.189-0.4375-0.438-0.4375h-1.25c-0.142-0.5539-0.371-1.0512-0.656-1.5312l0.906-0.9063c0.17566-0.17566 0.17566-0.44934 0-0.625l-1.5-1.5c-0.17566-0.17566-0.44934-0.17566-0.625 0l-0.906 0.9063c-0.48-0.2844-0.977-0.5141-1.531-0.6563v-1.25c0-0.24842-0.1891-0.4375-0.4375-0.4375zm1.0625 4.1573c1.8451 0 3.3427 1.4975 3.3427 3.3427 0 1.8451-1.4975 3.3427-3.3427 3.3427-1.8451 0-3.3427-1.4979-3.3427-3.343s1.4976-3.3427 3.3427-3.3427z"/>
+<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="16" width="16" version="1.0" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
+ <g opacity=".5" transform="translate(0 -.056)">
+ <path d="m6.9375 0.056c-0.2484 0-0.4375 0.18908-0.4375 0.4375v1.25c-0.5539 0.1422-1.0512 0.3719-1.5312 0.6563l-0.9063-0.9063c-0.17566-0.17566-0.44934-0.17566-0.625 0l-1.5 1.5c-0.17566 0.17566-0.17566 0.44934 0 0.625l0.9063 0.9063c-0.2844 0.48-0.5141 0.9773-0.6563 1.5312h-1.25c-0.24842 0-0.4375 0.1891-0.4375 0.4375v2.125c1e-8 0.24842 0.18908 0.4375 0.4375 0.4375h1.25c0.1422 0.5539 0.37188 1.0512 0.65625 1.5312l-0.9063 0.907c-0.17566 0.17566-0.17566 0.44934 0 0.625l1.5 1.5c0.17566 0.17566 0.44934 0.17566 0.625 0l0.9063-0.907c0.48 0.285 0.9773 0.514 1.5312 0.656v1.25c1e-7 0.24842 0.18908 0.4375 0.4375 0.4375h2.125c0.2484 0 0.4375-0.189 0.4375-0.438v-1.25c0.5539-0.1422 1.0512-0.37188 1.5312-0.65625l0.90625 0.90625c0.17566 0.17566 0.44934 0.17566 0.625 0l1.5-1.5c0.17566-0.17566 0.17566-0.44934 0-0.625l-0.906-0.906c0.285-0.48 0.514-0.9771 0.656-1.531h1.25c0.249 0 0.438-0.1891 0.438-0.4375v-2.125c0-0.2484-0.189-0.4375-0.438-0.4375h-1.25c-0.142-0.5539-0.371-1.0512-0.656-1.5312l0.906-0.9063c0.17566-0.17566 0.17566-0.44934 0-0.625l-1.5-1.5c-0.17566-0.17566-0.44934-0.17566-0.625 0l-0.906 0.9063c-0.48-0.2844-0.977-0.5141-1.531-0.6563v-1.25c0-0.24842-0.1891-0.4375-0.4375-0.4375zm1.0625 4.1573c1.8451 0 3.3427 1.4975 3.3427 3.3427 0 1.8451-1.4975 3.3427-3.3427 3.3427-1.8451 0-3.3427-1.4979-3.3427-3.343s1.4976-3.3427 3.3427-3.3427z" display="block"/>
</g>
</svg>
diff --git a/core/img/actions/star.png b/core/img/actions/star.png
index 6a04282f3fa..88e4ad54584 100644
--- a/core/img/actions/star.png
+++ b/core/img/actions/star.png
Binary files differ
diff --git a/core/img/actions/star.svg b/core/img/actions/star.svg
index c2b3b60a2b8..36e08170a9a 100644
--- a/core/img/actions/star.svg
+++ b/core/img/actions/star.svg
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="22" width="22" version="1.1" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
- <g transform="matrix(.068322 0 0 .068322 -10.114 -50.902)">
- <path d="m330.36 858.43 43.111 108.06 117.64 9.2572-89.445 74.392 27.55 114.75-98.391-62.079-100.62 61.66 28.637-112.76-89.734-76.638 116.09-7.6094z" transform="translate(-21.071,-112.5)" fill="#CCC"/>
+ <g opacity=".5" transform="matrix(.068322 0 0 .068322 -10.114 -50.902)">
+ <path d="m330.36 858.43 43.111 108.06 117.64 9.2572-89.445 74.392 27.55 114.75-98.391-62.079-100.62 61.66 28.637-112.76-89.734-76.638 116.09-7.6094z" transform="translate(-21.071,-112.5)"/>
</g>
</svg>
diff --git a/core/img/places/file.png b/core/img/places/file.png
index c2e5db953a8..e0f04c31731 100644
--- a/core/img/places/file.png
+++ b/core/img/places/file.png
Binary files differ
diff --git a/core/img/places/file.svg b/core/img/places/file.svg
index be6d9866835..7db9a201a9f 100644
--- a/core/img/places/file.svg
+++ b/core/img/places/file.svg
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="16" width="16" version="1.0" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
<rect style="color:#000000" fill-opacity="0" height="97.986" width="163.31" y="-32.993" x="-62.897"/>
- <path opacity=".7" style="block-progression:tb;color:#000000;text-transform:none;text-indent:0" d="m3.3501 1.002c-0.1975 0.0382-0.3535 0.2333-0.35 0.4374v13.123c0.0000047 0.22904 0.20522 0.43743 0.43077 0.43744h10.139c0.22555-0.000006 0.43076-0.2084 0.43077-0.43744v-10.143c-0.004-0.06684-0.022-0.13284-0.054-0.19134-0.966-1.3896-2.035-2.4191-3.312-3.1988-0.043-0.0164-0.088-0.0256-0.134-0.0274h-7.0695c-0.026843-0.0026-0.053928-0.0026-0.080774 0zm5.6499 2.498c0-0.2357 0.2643-0.5 0.5-0.5h0.5v2h2v0.5c0 0.2357-0.264 0.5-0.5 0.5h-2c-0.2357 0-0.5-0.2643-0.5-0.5 0-0.46411 0.0000019-1.4917 0.0000019-2z" fill="#1e1e1e"/>
+ <path opacity=".5" style="block-progression:tb;color:#000000;text-transform:none;text-indent:0" d="m3.3501 1.002c-0.1975 0.0382-0.3535 0.2333-0.35 0.4374v13.123c0.0000047 0.22904 0.20522 0.43743 0.43077 0.43744h10.139c0.22555-0.000006 0.43076-0.2084 0.43077-0.43744v-10.143c-0.004-0.06684-0.022-0.13284-0.054-0.19134-0.966-1.3896-2.035-2.4191-3.312-3.1988-0.043-0.0164-0.088-0.0256-0.134-0.0274h-7.0695c-0.026843-0.0026-0.053928-0.0026-0.080774 0zm5.6499 2.498c0-0.2357 0.2643-0.5 0.5-0.5h0.5v2h2v0.5c0 0.2357-0.264 0.5-0.5 0.5h-2c-0.2357 0-0.5-0.2643-0.5-0.5 0-0.46411 0.0000019-1.4917 0.0000019-2z"/>
</svg>
diff --git a/core/img/places/home.png b/core/img/places/home.png
index 5cf94495c34..2e0313d59a7 100644
--- a/core/img/places/home.png
+++ b/core/img/places/home.png
Binary files differ
diff --git a/core/img/places/home.svg b/core/img/places/home.svg
index 020a90f7720..2edc3af25bb 100644
--- a/core/img/places/home.svg
+++ b/core/img/places/home.svg
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" height="16" width="16" version="1.0" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/">
<rect style="color:#000000" fill-opacity="0" height="97.986" width="163.31" y="-32.993" x="-62.897"/>
- <path opacity=".7" fill="#1e1e1e" d="m8 1.0306-8 7.9694h3v6.0001h10v-6h3l-3-3.0306v-3.9695h-3v1.0812l-2-2.0505z" fill-rule="evenodd"/>
+ <path opacity=".5" d="m8 1.0306-8 7.9694h3v6.0001h10v-6h3l-3-3.0306v-3.9695h-3v1.0812l-2-2.0505z" fill-rule="evenodd"/>
</svg>
diff --git a/core/js/backgroundjobs.js b/core/js/backgroundjobs.js
index 4a558a66b4b..c3100792e9e 100644
--- a/core/js/backgroundjobs.js
+++ b/core/js/backgroundjobs.js
@@ -22,4 +22,6 @@
// start worker once page has loaded
$(document).ready(function(){
$.get( OC.webroot+'/cron.php' );
+
+ $('.section .icon-info').tipsy({gravity: 'w'});
});
diff --git a/core/js/config.php b/core/js/config.php
index b57289fde48..d6946f671d1 100644
--- a/core/js/config.php
+++ b/core/js/config.php
@@ -50,14 +50,16 @@ foreach(OC_App::getEnabledApps() as $app) {
$apps_paths[$app] = OC_App::getAppWebPath($app);
}
-$value = \OCP\Config::getAppValue('core', 'shareapi_default_expire_date', 'no');
+$config = \OC::$server->getConfig();
+$value = $config->getAppValue('core', 'shareapi_default_expire_date', 'no');
$defaultExpireDateEnabled = ($value === 'yes') ? true :false;
$defaultExpireDate = $enforceDefaultExpireDate = null;
if ($defaultExpireDateEnabled) {
- $defaultExpireDate = (int)\OCP\Config::getAppValue('core', 'shareapi_expire_after_n_days', '7');
- $value = \OCP\Config::getAppValue('core', 'shareapi_enforce_expire_date', 'no');
+ $defaultExpireDate = (int) $config->getAppValue('core', 'shareapi_expire_after_n_days', '7');
+ $value = $config->getAppValue('core', 'shareapi_enforce_expire_date', 'no');
$enforceDefaultExpireDate = ($value === 'yes') ? true : false;
}
+$outgoingServer2serverShareEnabled = $config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'yes';
$array = array(
"oc_debug" => (defined('DEBUG') && DEBUG) ? 'true' : 'false',
@@ -110,6 +112,8 @@ $array = array(
'enforcePasswordForPublicLink' => \OCP\Util::isPublicLinkPasswordRequired(),
'sharingDisabledForUser' => \OCP\Util::isSharingDisabledForUser(),
'resharingAllowed' => \OCP\Share::isResharingAllowed(),
+ 'remoteShareAllowed' => $outgoingServer2serverShareEnabled,
+ 'federatedCloudShareDoc' => \OC::$server->getURLGenerator()->linkToDocs('user-sharing-federated')
)
)
),
diff --git a/core/js/js.js b/core/js/js.js
index 274eddffff7..cb93e73f2e0 100644
--- a/core/js/js.js
+++ b/core/js/js.js
@@ -476,11 +476,14 @@ var OC={
registerMenu: function($toggle, $menuEl) {
$menuEl.addClass('menu');
$toggle.on('click.menu', function(event) {
+ // prevent the link event (append anchor to URL)
+ event.preventDefault();
+
if ($menuEl.is(OC._currentMenu)) {
$menuEl.slideUp(OC.menuSpeed);
OC._currentMenu = null;
OC._currentMenuToggle = null;
- return false;
+ return;
}
// another menu was open?
else if (OC._currentMenu) {
@@ -490,7 +493,6 @@ var OC={
$menuEl.slideToggle(OC.menuSpeed);
OC._currentMenu = $menuEl;
OC._currentMenuToggle = $toggle;
- return false;
});
},
diff --git a/core/js/maintenance-check.js b/core/js/maintenance-check.js
index 8ca00456fef..061a434214b 100644
--- a/core/js/maintenance-check.js
+++ b/core/js/maintenance-check.js
@@ -7,14 +7,14 @@ function checkStatus() {
0, location.pathname.indexOf('index.php')
);
request.open("GET", ocroot+'status.php', true);
- request.send();
request.onreadystatechange = function() {
if (request.readyState === 4) {
var response = request.responseText;
var responseobj = JSON.parse(response);
- if (responseobj.maintenance === 'false') {
+ if (responseobj.maintenance === false) {
window.location.reload();
}
}
};
+ request.send();
}
diff --git a/core/js/share.js b/core/js/share.js
index b9b4a5bc754..5018d10ee9c 100644
--- a/core/js/share.js
+++ b/core/js/share.js
@@ -391,8 +391,18 @@ OC.Share={
}
});
+ var sharePlaceholder = t('core', 'Share with users or groups …');
+ if(oc_appconfig.core.remoteShareAllowed) {
+ sharePlaceholder = t('core', 'Share with users, groups or remote users …');
+ }
+
html += '<label for="shareWith" class="hidden-visually">'+t('core', 'Share')+'</label>';
- html += '<input id="shareWith" type="text" placeholder="'+t('core', 'Share with user or group …')+'" />';
+ html += '<input id="shareWith" type="text" placeholder="' + sharePlaceholder + '" />';
+ if(oc_appconfig.core.remoteShareAllowed) {
+ var federatedCloudSharingDoc = '<a target="_blank" class="icon-info svg shareWithRemoteInfo" href="{docLink}" '
+ + 'title="' + t('core', 'Share with people on other ownClouds using the syntax username@example.com/owncloud') + '"></a>';
+ html += federatedCloudSharingDoc.replace('{docLink}', oc_appconfig.core.federatedCloudShareDoc);
+ }
html += '<span class="shareWithLoading icon-loading-small hidden"></span>';
html += '<ul id="shareWithList">';
html += '</ul>';
@@ -443,6 +453,11 @@ OC.Share={
dropDownEl = $(html);
dropDownEl = dropDownEl.appendTo(appendTo);
+ // trigger remote share info tooltip
+ if(oc_appconfig.core.remoteShareAllowed) {
+ $('.shareWithRemoteInfo').tipsy({gravity: 'e'});
+ }
+
//Get owner avatars
if (oc_config.enable_avatars === true && data !== false && data.reshare !== false && data.reshare.uid_owner !== undefined) {
dropDownEl.find(".avatar").avatar(data.reshare.uid_owner, 32);
diff --git a/core/register_command.php b/core/register_command.php
index 67fdb6f808e..701fb10d1ba 100644
--- a/core/register_command.php
+++ b/core/register_command.php
@@ -26,6 +26,7 @@
/** @var $application Symfony\Component\Console\Application */
$application->add(new OC\Core\Command\Status);
+$application->add(new OC\Core\Command\Check(\OC::$server->getConfig()));
$application->add(new OC\Core\Command\App\CheckCode());
$application->add(new OC\Core\Command\L10n\CreateJs());
diff --git a/core/templates/installation.php b/core/templates/installation.php
index 96e6119cad3..911bc05069f 100644
--- a/core/templates/installation.php
+++ b/core/templates/installation.php
@@ -28,7 +28,7 @@ script('core', [
<?php endif; ?>
<?php if(!$_['htaccessWorking']): ?>
<fieldset class="warning">
- <legend><strong><?php p($l->t('Security Warning'));?></strong></legend>
+ <legend><strong><?php p($l->t('Security warning'));?></strong></legend>
<p><?php p($l->t('Your data directory and files are probably accessible from the internet because the .htaccess file does not work.'));?><br>
<?php print_unescaped($l->t(
'For information how to properly configure your server, please see the <a href="%s" target="_blank">documentation</a>.',
@@ -150,7 +150,7 @@ script('core', [
<?php if(!$_['dbIsSet'] OR count($_['errors']) > 0): ?>
<fieldset id="sqliteInformation" class="warning">
- <legend><?php p($l->t('Performance Warning'));?></legend>
+ <legend><?php p($l->t('Performance warning'));?></legend>
<p><?php p($l->t('SQLite will be used as database.'));?></p>
<p><?php p($l->t('For larger installations we recommend to choose a different database backend.'));?></p>
<p><?php p($l->t('Especially when using the desktop client for file syncing the use of SQLite is discouraged.')); ?></p>
@@ -158,4 +158,10 @@ script('core', [
<?php endif ?>
<div class="buttons"><input type="submit" class="primary" value="<?php p($l->t( 'Finish setup' )); ?>" data-finishing="<?php p($l->t( 'Finishing …' )); ?>"></div>
+
+ <p class="info">
+ <span class="icon-info-white svg"></span>
+ <?php p($l->t('Need help?'));?>
+ <a target="_blank" href="<?php p(link_to_docs('admin-install')); ?>"><?php p($l->t('See the documentation'));?> ↗</a>
+ </p>
</form>
diff --git a/core/templates/layout.user.php b/core/templates/layout.user.php
index 880a276c725..87a6a9216d2 100644
--- a/core/templates/layout.user.php
+++ b/core/templates/layout.user.php
@@ -123,7 +123,7 @@
if(OC_User::isAdminUser(OC_User::getUser())):
?>
<li id="apps-management">
- <a href="<?php print_unescaped(OC_Helper::linkToRoute('settings_apps')); ?>" tabindex="4"
+ <a href="<?php print_unescaped(\OC::$server->getURLGenerator()->linkToRoute('settings.AppSettings.viewApps')); ?>" tabindex="4"
<?php if( $_['appsmanagement_active'] ): ?> class="active"<?php endif; ?>>
<img class="app-icon svg" alt="" src="<?php print_unescaped(OC_Helper::imagePath('settings', 'apps.svg')); ?>">
<div class="icon-loading-dark" style="display:none;"></div>
diff --git a/lib/base.php b/lib/base.php
index be397e52449..042419eff1d 100644
--- a/lib/base.php
+++ b/lib/base.php
@@ -588,35 +588,36 @@ class OC {
ini_set('session.cookie_secure', true);
}
- $errors = OC_Util::checkServer(\OC::$server->getConfig());
- if (count($errors) > 0) {
- if (self::$CLI) {
- // Convert l10n string into regular string for usage in database
- $staticErrors = [];
- foreach ($errors as $error) {
- echo $error['error'] . "\n";
- echo $error['hint'] . "\n\n";
- $staticErrors[] = [
- 'error' => (string) $error['error'],
- 'hint' => (string) $error['hint'],
- ];
- }
+ if (!defined('OC_CONSOLE')) {
+ $errors = OC_Util::checkServer(\OC::$server->getConfig());
+ if (count($errors) > 0) {
+ if (self::$CLI) {
+ // Convert l10n string into regular string for usage in database
+ $staticErrors = [];
+ foreach ($errors as $error) {
+ echo $error['error'] . "\n";
+ echo $error['hint'] . "\n\n";
+ $staticErrors[] = [
+ 'error' => (string)$error['error'],
+ 'hint' => (string)$error['hint'],
+ ];
+ }
- try {
- \OC::$server->getConfig()->setAppValue('core', 'cronErrors', json_encode($staticErrors));
- } catch(\Exception $e) {
- echo('Writing to database failed');
+ try {
+ \OC::$server->getConfig()->setAppValue('core', 'cronErrors', json_encode($staticErrors));
+ } catch (\Exception $e) {
+ echo('Writing to database failed');
+ }
+ exit(1);
+ } else {
+ OC_Response::setStatus(OC_Response::STATUS_SERVICE_UNAVAILABLE);
+ OC_Template::printGuestPage('', 'error', array('errors' => $errors));
+ exit;
}
- exit(1);
- } else {
- OC_Response::setStatus(OC_Response::STATUS_SERVICE_UNAVAILABLE);
- OC_Template::printGuestPage('', 'error', array('errors' => $errors));
- exit;
- }
- } elseif(self::$CLI && \OC::$server->getConfig()->getSystemValue('installed', false)) {
+ } elseif (self::$CLI && \OC::$server->getConfig()->getSystemValue('installed', false)) {
\OC::$server->getConfig()->deleteAppValue('core', 'cronErrors');
+ }
}
-
//try to set the session lifetime
$sessionLifeTime = self::getSessionLifeTime();
@ini_set('gc_maxlifetime', (string)$sessionLifeTime);
diff --git a/lib/private/activitymanager.php b/lib/private/activitymanager.php
index c6cd5a1fe83..26db0c78df2 100644
--- a/lib/private/activitymanager.php
+++ b/lib/private/activitymanager.php
@@ -28,8 +28,34 @@ namespace OC;
use OCP\Activity\IConsumer;
use OCP\Activity\IExtension;
use OCP\Activity\IManager;
+use OCP\IConfig;
+use OCP\IRequest;
+use OCP\IUserSession;
class ActivityManager implements IManager {
+ /** @var IRequest */
+ protected $request;
+
+ /** @var IUserSession */
+ protected $session;
+
+ /** @var IConfig */
+ protected $config;
+
+ /**
+ * constructor of the controller
+ *
+ * @param IRequest $request
+ * @param IUserSession $session
+ * @param IConfig $config
+ */
+ public function __construct(IRequest $request,
+ IUserSession $session,
+ IConfig $config) {
+ $this->request = $request;
+ $this->session = $session;
+ $this->config = $config;
+ }
/**
* @var \Closure[]
@@ -348,4 +374,43 @@ class ActivityManager implements IManager {
return array(' and ((' . implode(') or (', $conditions) . '))', $parameters);
}
+
+ /**
+ * Get the user we need to use
+ *
+ * Either the user is logged in, or we try to get it from the token
+ *
+ * @return string
+ * @throws \UnexpectedValueException If the token is invalid, does not exist or is not unique
+ */
+ public function getCurrentUserId() {
+ if (!$this->session->isLoggedIn()) {
+ return $this->getUserFromToken();
+ } else {
+ return $this->session->getUser()->getUID();
+ }
+ }
+
+ /**
+ * Get the user for the token
+ *
+ * @return string
+ * @throws \UnexpectedValueException If the token is invalid, does not exist or is not unique
+ */
+ protected function getUserFromToken() {
+ $token = (string) $this->request->getParam('token', '');
+ if (strlen($token) !== 30) {
+ throw new \UnexpectedValueException('The token is invalid');
+ }
+
+ $users = $this->config->getUsersForUserValue('activity', 'rsstoken', $token);
+
+ if (sizeof($users) !== 1) {
+ // No unique user found
+ throw new \UnexpectedValueException('The token is invalid');
+ }
+
+ // Token found login as that user
+ return array_shift($users);
+ }
}
diff --git a/lib/private/allconfig.php b/lib/private/allconfig.php
index df75a332a13..63cc92601bb 100644
--- a/lib/private/allconfig.php
+++ b/lib/private/allconfig.php
@@ -253,7 +253,7 @@ class AllConfig implements \OCP\IConfig {
* @param string $userId the userId of the user that we want to store the value under
* @param string $appName the appName that we stored the value under
* @param string $key the key under which the value is being stored
- * @param string $default the default value to be returned if the value isn't set
+ * @param mixed $default the default value to be returned if the value isn't set
* @return string
*/
public function getUserValue($userId, $appName, $key, $default = '') {
diff --git a/lib/private/app.php b/lib/private/app.php
index 4b3d4b82b82..aec67e6efd6 100644
--- a/lib/private/app.php
+++ b/lib/private/app.php
@@ -61,6 +61,7 @@ class OC_App {
static private $loadedApps = array();
static private $altLogin = array();
private static $shippedApps = null;
+ const officialApp = 200;
/**
* clean the appId
@@ -306,8 +307,13 @@ class OC_App {
* @return int
*/
public static function downloadApp($app) {
- $appData= OCSClient::getApplication($app);
- $download= OCSClient::getApplicationDownload($app, 1);
+ $ocsClient = new OCSClient(
+ \OC::$server->getHTTPClientService(),
+ \OC::$server->getConfig(),
+ \OC::$server->getLogger()
+ );
+ $appData = $ocsClient->getApplication($app);
+ $download= $ocsClient->getApplicationDownload($app);
if(isset($download['downloadlink']) and $download['downloadlink']!='') {
// Replace spaces in download link without encoding entire URL
$download['downloadlink'] = str_replace(' ', '%20', $download['downloadlink']);
@@ -507,7 +513,7 @@ class OC_App {
/**
* search for an app in all app-directories
*
- * @param $appId
+ * @param string $appId
* @return mixed (bool|string)
*/
protected static function findAppInDirectories($appId) {
@@ -726,6 +732,8 @@ class OC_App {
/**
* register a personal form to be shown
+ * @param string $app
+ * @param string $page
*/
public static function registerPersonal($app, $page) {
self::$personalForms[] = $app . '/' . $page . '.php';
@@ -780,8 +788,9 @@ class OC_App {
}
/**
- * Lists all apps, this is used in apps.php
+ * List all apps, this is used in apps.php
*
+ * @param bool $onlyLocal
* @return array
*/
public static function listAllApps($onlyLocal = false) {
@@ -819,8 +828,7 @@ class OC_App {
if (isset($info['shipped']) and ($info['shipped'] == 'true')) {
$info['internal'] = true;
- $info['internallabel'] = (string)$l->t('Recommended');
- $info['internalclass'] = 'recommendedapp';
+ $info['level'] = self::officialApp;
$info['removable'] = false;
} else {
$info['internal'] = false;
@@ -845,7 +853,7 @@ class OC_App {
}
}
if ($onlyLocal) {
- $remoteApps = array();
+ $remoteApps = [];
} else {
$remoteApps = OC_App::getAppstoreApps();
}
@@ -865,34 +873,6 @@ class OC_App {
} else {
$combinedApps = $appList;
}
- // bring the apps into the right order with a custom sort function
- usort($combinedApps, function ($a, $b) {
-
- // priority 1: active
- if ($a['active'] != $b['active']) {
- return $b['active'] - $a['active'];
- }
-
- // priority 2: shipped
- $aShipped = (array_key_exists('shipped', $a) && $a['shipped'] === 'true') ? 1 : 0;
- $bShipped = (array_key_exists('shipped', $b) && $b['shipped'] === 'true') ? 1 : 0;
- if ($aShipped !== $bShipped) {
- return ($bShipped - $aShipped);
- }
-
- // priority 3: recommended
- $internalClassA = isset($a['internalclass']) ? $a['internalclass'] : '';
- $internalClassB = isset($b['internalclass']) ? $b['internalclass'] : '';
- if ($internalClassA != $internalClassB) {
- $aTemp = ($internalClassA == 'recommendedapp' ? 1 : 0);
- $bTemp = ($internalClassB == 'recommendedapp' ? 1 : 0);
- return ($bTemp - $aTemp);
- }
-
- // priority 4: alphabetical
- return strcasecmp($a['name'], $b['name']);
-
- });
return $combinedApps;
}
@@ -913,15 +893,24 @@ class OC_App {
}
/**
- * get a list of all apps on apps.owncloud.com
- *
- * @return array|false multi-dimensional array of apps.
- * Keys: id, name, type, typename, personid, license, detailpage, preview, changed, description
+ * Get a list of all apps on the appstore
+ * @param string $filter
+ * @param string $category
+ * @return array|bool multi-dimensional array of apps.
+ * Keys: id, name, type, typename, personid, license, detailpage, preview, changed, description
*/
public static function getAppstoreApps($filter = 'approved', $category = null) {
- $categories = array($category);
+ $categories = [$category];
+
+ $ocsClient = new OCSClient(
+ \OC::$server->getHTTPClientService(),
+ \OC::$server->getConfig(),
+ \OC::$server->getLogger()
+ );
+
+
if (is_null($category)) {
- $categoryNames = OCSClient::getCategories();
+ $categoryNames = $ocsClient->getCategories();
if (is_array($categoryNames)) {
// Check that categories of apps were retrieved correctly
if (!$categories = array_keys($categoryNames)) {
@@ -933,34 +922,36 @@ class OC_App {
}
$page = 0;
- $remoteApps = OCSClient::getApplications($categories, $page, $filter);
- $app1 = array();
+ $remoteApps = $ocsClient->getApplications($categories, $page, $filter);
+ $apps = [];
$i = 0;
$l = \OC::$server->getL10N('core');
foreach ($remoteApps as $app) {
$potentialCleanId = self::getInternalAppIdByOcs($app['id']);
// enhance app info (for example the description)
- $app1[$i] = OC_App::parseAppInfo($app);
- $app1[$i]['author'] = $app['personid'];
- $app1[$i]['ocs_id'] = $app['id'];
- $app1[$i]['internal'] = 0;
- $app1[$i]['active'] = ($potentialCleanId !== false) ? self::isEnabled($potentialCleanId) : false;
- $app1[$i]['update'] = false;
- $app1[$i]['groups'] = false;
- $app1[$i]['score'] = $app['score'];
- $app1[$i]['removable'] = false;
+ $apps[$i] = OC_App::parseAppInfo($app);
+ $apps[$i]['author'] = $app['personid'];
+ $apps[$i]['ocs_id'] = $app['id'];
+ $apps[$i]['internal'] = 0;
+ $apps[$i]['active'] = ($potentialCleanId !== false) ? self::isEnabled($potentialCleanId) : false;
+ $apps[$i]['update'] = false;
+ $apps[$i]['groups'] = false;
+ $apps[$i]['score'] = $app['score'];
+ $apps[$i]['removable'] = false;
if ($app['label'] == 'recommended') {
- $app1[$i]['internallabel'] = (string)$l->t('Recommended');
- $app1[$i]['internalclass'] = 'recommendedapp';
+ $apps[$i]['internallabel'] = (string)$l->t('Recommended');
+ $apps[$i]['internalclass'] = 'recommendedapp';
}
$i++;
}
- if (empty($app1)) {
+
+
+ if (empty($apps)) {
return false;
} else {
- return $app1;
+ return $apps;
}
}
@@ -1084,7 +1075,12 @@ class OC_App {
public static function installApp($app) {
$l = \OC::$server->getL10N('core');
$config = \OC::$server->getConfig();
- $appData=OCSClient::getApplication($app);
+ $ocsClient = new OCSClient(
+ \OC::$server->getHTTPClientService(),
+ $config,
+ \OC::$server->getLogger()
+ );
+ $appData = $ocsClient->getApplication($app);
// check if app is a shipped app or not. OCS apps have an integer as id, shipped apps use a string
if (!is_numeric($app)) {
diff --git a/lib/private/app/appmanager.php b/lib/private/app/appmanager.php
index 2a147d4de6f..c9d4a777c4a 100644
--- a/lib/private/app/appmanager.php
+++ b/lib/private/app/appmanager.php
@@ -203,7 +203,7 @@ class AppManager implements IAppManager {
/**
* Clear the cached list of apps when enabling/disabling an app
*/
- protected function clearAppsCache() {
+ public function clearAppsCache() {
$settingsMemCache = $this->memCacheFactory->create('settings');
$settingsMemCache->clear('listApps');
}
diff --git a/lib/private/encryption/exceptions/decryptionfailedexception.php b/lib/private/encryption/exceptions/decryptionfailedexception.php
index f8b4fdf07fa..406ae12968e 100644
--- a/lib/private/encryption/exceptions/decryptionfailedexception.php
+++ b/lib/private/encryption/exceptions/decryptionfailedexception.php
@@ -1,7 +1,8 @@
<?php
- /**
- * @author Clark Tomlinson <clark@owncloud.com>
- * @since 2/25/15, 9:38 AM
+/**
+ * @author Clark Tomlinson <fallen013@gmail.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
diff --git a/lib/private/encryption/exceptions/emptyencryptiondataexception.php b/lib/private/encryption/exceptions/emptyencryptiondataexception.php
index d3dc9230047..739614b3ec2 100644
--- a/lib/private/encryption/exceptions/emptyencryptiondataexception.php
+++ b/lib/private/encryption/exceptions/emptyencryptiondataexception.php
@@ -1,7 +1,8 @@
<?php
- /**
- * @author Clark Tomlinson <clark@owncloud.com>
- * @since 2/25/15, 9:38 AM
+/**
+ * @author Clark Tomlinson <fallen013@gmail.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
diff --git a/lib/private/encryption/exceptions/encryptionfailedexception.php b/lib/private/encryption/exceptions/encryptionfailedexception.php
index ac489c73254..4195ca0a5a8 100644
--- a/lib/private/encryption/exceptions/encryptionfailedexception.php
+++ b/lib/private/encryption/exceptions/encryptionfailedexception.php
@@ -1,7 +1,8 @@
<?php
- /**
- * @author Clark Tomlinson <clark@owncloud.com>
- * @since 2/25/15, 9:37 AM
+/**
+ * @author Clark Tomlinson <fallen013@gmail.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
diff --git a/lib/private/encryption/exceptions/encryptionheaderkeyexistsexception.php b/lib/private/encryption/exceptions/encryptionheaderkeyexistsexception.php
index 5e8e48efd78..d927939484f 100644
--- a/lib/private/encryption/exceptions/encryptionheaderkeyexistsexception.php
+++ b/lib/private/encryption/exceptions/encryptionheaderkeyexistsexception.php
@@ -1,24 +1,23 @@
<?php
-
/**
- * ownCloud
- *
- * @copyright (C) 2015 ownCloud, Inc.
+ * @author Björn Schießle <schiessle@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
*
- * @author Bjoern Schiessle <schiessle@owncloud.com>
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or any later version.
+ * This 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 library is distributed in the hope that it will be useful,
+ * 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.
+ * 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/>
*
- * 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/>.
*/
namespace OC\Encryption\Exceptions;
diff --git a/lib/private/encryption/exceptions/encryptionheadertolargeexception.php b/lib/private/encryption/exceptions/encryptionheadertolargeexception.php
index cdb5f940800..40c51782a32 100644
--- a/lib/private/encryption/exceptions/encryptionheadertolargeexception.php
+++ b/lib/private/encryption/exceptions/encryptionheadertolargeexception.php
@@ -1,7 +1,8 @@
<?php
- /**
- * @author Clark Tomlinson <clark@owncloud.com>
- * @since 2/25/15, 9:35 AM
+/**
+ * @author Clark Tomlinson <fallen013@gmail.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
diff --git a/lib/private/encryption/exceptions/modulealreadyexistsexception.php b/lib/private/encryption/exceptions/modulealreadyexistsexception.php
index fa1e70a5c36..c72ad7b7ab2 100644
--- a/lib/private/encryption/exceptions/modulealreadyexistsexception.php
+++ b/lib/private/encryption/exceptions/modulealreadyexistsexception.php
@@ -1,24 +1,23 @@
<?php
-
/**
- * ownCloud
- *
- * @copyright (C) 2015 ownCloud, Inc.
+ * @author Björn Schießle <schiessle@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
*
- * @author Bjoern Schiessle <schiessle@owncloud.com>
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or any later version.
+ * This 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 library is distributed in the hope that it will be useful,
+ * 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.
+ * 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/>
*
- * 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/>.
*/
namespace OC\Encryption\Exceptions;
diff --git a/lib/private/encryption/exceptions/moduledoesnotexistsexception.php b/lib/private/encryption/exceptions/moduledoesnotexistsexception.php
index 2c699e8dc2d..d6fbb2b6e51 100644
--- a/lib/private/encryption/exceptions/moduledoesnotexistsexception.php
+++ b/lib/private/encryption/exceptions/moduledoesnotexistsexception.php
@@ -1,24 +1,23 @@
<?php
-
/**
- * ownCloud
- *
- * @copyright (C) 2015 ownCloud, Inc.
+ * @author Björn Schießle <schiessle@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
*
- * @author Bjoern Schiessle <schiessle@owncloud.com>
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or any later version.
+ * This 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 library is distributed in the hope that it will be useful,
+ * 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.
+ * 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/>
*
- * 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/>.
*/
namespace OC\Encryption\Exceptions;
diff --git a/lib/private/encryption/exceptions/unknowncipherexception.php b/lib/private/encryption/exceptions/unknowncipherexception.php
index 188f7403848..91535183169 100644
--- a/lib/private/encryption/exceptions/unknowncipherexception.php
+++ b/lib/private/encryption/exceptions/unknowncipherexception.php
@@ -1,7 +1,8 @@
<?php
- /**
- * @author Clark Tomlinson <clark@owncloud.com>
- * @since 2/25/15, 9:36 AM
+/**
+ * @author Clark Tomlinson <fallen013@gmail.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
diff --git a/lib/private/encryption/file.php b/lib/private/encryption/file.php
index 3600936ed0e..48cd0d1187b 100644
--- a/lib/private/encryption/file.php
+++ b/lib/private/encryption/file.php
@@ -1,24 +1,23 @@
<?php
-
/**
- * ownCloud
- *
- * @copyright (C) 2015 ownCloud, Inc.
+ * @author Björn Schießle <schiessle@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
*
- * @author Bjoern Schiessle <schiessle@owncloud.com>
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or any later version.
+ * This 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 library is distributed in the hope that it will be useful,
+ * 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.
+ * 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/>
*
- * 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/>.
*/
namespace OC\Encryption;
diff --git a/lib/private/encryption/keys/factory.php b/lib/private/encryption/keys/factory.php
index a214b238615..0e2b0292a68 100644
--- a/lib/private/encryption/keys/factory.php
+++ b/lib/private/encryption/keys/factory.php
@@ -1,24 +1,22 @@
<?php
-
/**
- * ownCloud
- *
- * @copyright (C) 2015 ownCloud, Inc.
+ * @author Björn Schießle <schiessle@owncloud.com>
*
- * @author Bjoern Schiessle <schiessle@owncloud.com>
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or any later version.
+ * This 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 library is distributed in the hope that it will be useful,
+ * 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.
+ * 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/>
*
- * 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/>.
*/
namespace OC\Encryption\Keys;
diff --git a/lib/private/encryption/keys/storage.php b/lib/private/encryption/keys/storage.php
index 42610bd0b41..9d978193130 100644
--- a/lib/private/encryption/keys/storage.php
+++ b/lib/private/encryption/keys/storage.php
@@ -1,24 +1,23 @@
<?php
-
/**
- * ownCloud
- *
- * @copyright (C) 2015 ownCloud, Inc.
+ * @author Björn Schießle <schiessle@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
*
- * @author Bjoern Schiessle <schiessle@owncloud.com>
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or any later version.
+ * This 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 library is distributed in the hope that it will be useful,
+ * 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.
+ * 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/>
*
- * 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/>.
*/
namespace OC\Encryption\Keys;
@@ -141,11 +140,11 @@ class Storage implements \OCP\Encryption\Keys\IStorage {
* @param string $uid ID if the user for whom we want to delete the key
* @param string $keyId id of the key
*
- * @return boolean
+ * @return boolean False when the key could not be deleted
*/
public function deleteUserKey($uid, $keyId) {
$path = $this->constructUserKeyPath($keyId, $uid);
- return $this->view->unlink($path);
+ return !$this->view->file_exists($path) || $this->view->unlink($path);
}
/**
@@ -154,22 +153,23 @@ class Storage implements \OCP\Encryption\Keys\IStorage {
* @param string $path path to file
* @param string $keyId id of the key
*
- * @return boolean
+ * @return boolean False when the key could not be deleted
*/
public function deleteFileKey($path, $keyId) {
$keyDir = $this->getFileKeyDir($path);
- return $this->view->unlink($keyDir . $keyId);
+ return !$this->view->file_exists($keyDir . $keyId) || $this->view->unlink($keyDir . $keyId);
}
/**
* delete all file keys for a given file
*
* @param string $path to the file
- * @return boolean
+ * @return boolean False when the key could not be deleted
*/
public function deleteAllFileKeys($path) {
$keyDir = $this->getFileKeyDir($path);
- return $this->view->deleteAll(dirname($keyDir));
+ $path = dirname($keyDir);
+ return !$this->view->file_exists($path) || $this->view->deleteAll($path);
}
/**
@@ -178,11 +178,11 @@ class Storage implements \OCP\Encryption\Keys\IStorage {
*
* @param string $keyId id of the key
*
- * @return boolean
+ * @return boolean False when the key could not be deleted
*/
public function deleteSystemUserKey($keyId) {
$path = $this->constructUserKeyPath($keyId);
- return $this->view->unlink($path);
+ return !$this->view->file_exists($path) || $this->view->unlink($path);
}
diff --git a/lib/private/encryption/manager.php b/lib/private/encryption/manager.php
index 484e0f540b2..74cad0a75bb 100644
--- a/lib/private/encryption/manager.php
+++ b/lib/private/encryption/manager.php
@@ -1,24 +1,23 @@
<?php
-
/**
- * ownCloud
- *
- * @copyright (C) 2015 ownCloud, Inc.
+ * @author Björn Schießle <schiessle@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
*
- * @author Bjoern Schiessle <schiessle@owncloud.com>
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or any later version.
+ * This 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 library is distributed in the hope that it will be useful,
+ * 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.
+ * 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/>
*
- * 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/>.
*/
namespace OC\Encryption;
diff --git a/lib/private/encryption/update.php b/lib/private/encryption/update.php
index 1cfe935e584..7a170a03adc 100644
--- a/lib/private/encryption/update.php
+++ b/lib/private/encryption/update.php
@@ -1,24 +1,23 @@
<?php
-
/**
- * ownCloud
- *
- * @copyright (C) 2015 ownCloud, Inc.
+ * @author Björn Schießle <schiessle@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
*
- * @author Bjoern Schiessle <schiessle@owncloud.com>
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or any later version.
+ * This 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 library is distributed in the hope that it will be useful,
+ * 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.
+ * 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/>
*
- * 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/>.
*/
namespace OC\Encryption;
diff --git a/lib/private/encryption/util.php b/lib/private/encryption/util.php
index 6312d8813e3..e7cf607c7b1 100644
--- a/lib/private/encryption/util.php
+++ b/lib/private/encryption/util.php
@@ -1,24 +1,23 @@
<?php
-
/**
- * ownCloud
- *
- * @copyright (C) 2015 ownCloud, Inc.
+ * @author Björn Schießle <schiessle@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
*
- * @author Bjoern Schiessle <schiessle@owncloud.com>
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or any later version.
+ * This 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 library is distributed in the hope that it will be useful,
+ * 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.
+ * 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/>
*
- * 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/>.
*/
namespace OC\Encryption;
diff --git a/lib/private/files/storage/dav.php b/lib/private/files/storage/dav.php
index 629f68e60f0..9f779208a09 100644
--- a/lib/private/files/storage/dav.php
+++ b/lib/private/files/storage/dav.php
@@ -58,6 +58,11 @@ class DAV extends \OC\Files\Storage\Common {
*/
private $client;
+ /**
+ * @var \OC\MemCache\ArrayCache
+ */
+ private $statCache;
+
/** @var array */
private static $tempFiles = [];
@@ -66,6 +71,7 @@ class DAV extends \OC\Files\Storage\Common {
* @throws \Exception
*/
public function __construct($params) {
+ $this->statCache = new \OC\MemCache\ArrayCache();
if (isset($params['host']) && isset($params['user']) && isset($params['password'])) {
$host = $params['host'];
//remove leading http[s], will be generated in createBaseUri()
@@ -121,6 +127,13 @@ class DAV extends \OC\Files\Storage\Common {
}
}
+ /**
+ * Clear the stat cache
+ */
+ public function clearStatCache() {
+ $this->statCache->clear();
+ }
+
/** {@inheritdoc} */
public function getId() {
return 'webdav::' . $this->user . '@' . $this->host . '/' . $this->root;
@@ -140,16 +153,23 @@ class DAV extends \OC\Files\Storage\Common {
public function mkdir($path) {
$this->init();
$path = $this->cleanPath($path);
- return $this->simpleResponse('MKCOL', $path, null, 201);
+ $result = $this->simpleResponse('MKCOL', $path, null, 201);
+ if ($result) {
+ $this->statCache->set($path, true);
+ }
+ return $result;
}
/** {@inheritdoc} */
public function rmdir($path) {
$this->init();
- $path = $this->cleanPath($path) . '/';
+ $path = $this->cleanPath($path);
// FIXME: some WebDAV impl return 403 when trying to DELETE
// a non-empty folder
- return $this->simpleResponse('DELETE', $path, null, 204);
+ $result = $this->simpleResponse('DELETE', $path . '/', null, 204);
+ $this->statCache->clear($path . '/');
+ $this->statCache->remove($path);
+ return $result;
}
/** {@inheritdoc} */
@@ -157,19 +177,34 @@ class DAV extends \OC\Files\Storage\Common {
$this->init();
$path = $this->cleanPath($path);
try {
- $response = $this->client->propfind($this->encodePath($path), array(), 1);
+ $response = $this->client->propfind(
+ $this->encodePath($path),
+ array(),
+ 1
+ );
$id = md5('webdav' . $this->root . $path);
$content = array();
$files = array_keys($response);
array_shift($files); //the first entry is the current directory
+
+ if (!$this->statCache->hasKey($path)) {
+ $this->statCache->set($path, true);
+ }
foreach ($files as $file) {
- $file = urldecode(basename($file));
+ $file = urldecode($file);
+ // do not store the real entry, we might not have all properties
+ if (!$this->statCache->hasKey($path)) {
+ $this->statCache->set($file, true);
+ }
+ $file = basename($file);
$content[] = $file;
}
\OC\Files\Stream\Dir::register($id, $content);
return opendir('fakedir://' . $id);
} catch (ClientHttpException $e) {
if ($e->getHttpStatus() === 404) {
+ $this->statCache->clear($path . '/');
+ $this->statCache->set($path, false);
return false;
}
$this->convertSabreException($e);
@@ -181,12 +216,57 @@ class DAV extends \OC\Files\Storage\Common {
}
}
+ /**
+ * Propfind call with cache handling.
+ *
+ * First checks if information is cached.
+ * If not, request it from the server then store to cache.
+ *
+ * @param string $path path to propfind
+ *
+ * @return array propfind response
+ *
+ * @throws Exception\NotFound
+ */
+ private function propfind($path) {
+ $path = $this->cleanPath($path);
+ $cachedResponse = $this->statCache->get($path);
+ if ($cachedResponse === false) {
+ // we know it didn't exist
+ throw new Exception\NotFound();
+ }
+ // we either don't know it, or we know it exists but need more details
+ if (is_null($cachedResponse) || $cachedResponse === true) {
+ $this->init();
+ try {
+ $response = $this->client->propfind(
+ $this->encodePath($path),
+ array(
+ '{DAV:}getlastmodified',
+ '{DAV:}getcontentlength',
+ '{DAV:}getcontenttype',
+ '{http://owncloud.org/ns}permissions',
+ '{DAV:}resourcetype',
+ '{DAV:}getetag',
+ )
+ );
+ $this->statCache->set($path, $response);
+ } catch (Exception\NotFound $e) {
+ // remember that this path did not exist
+ $this->statCache->clear($path . '/');
+ $this->statCache->set($path, false);
+ throw $e;
+ }
+ } else {
+ $response = $cachedResponse;
+ }
+ return $response;
+ }
+
/** {@inheritdoc} */
public function filetype($path) {
- $this->init();
- $path = $this->cleanPath($path);
try {
- $response = $this->client->propfind($this->encodePath($path), array('{DAV:}resourcetype'));
+ $response = $this->propfind($path);
$responseType = array();
if (isset($response["{DAV:}resourcetype"])) {
$responseType = $response["{DAV:}resourcetype"]->resourceType;
@@ -207,10 +287,17 @@ class DAV extends \OC\Files\Storage\Common {
/** {@inheritdoc} */
public function file_exists($path) {
- $this->init();
- $path = $this->cleanPath($path);
try {
- $this->client->propfind($this->encodePath($path), array('{DAV:}resourcetype'));
+ $path = $this->cleanPath($path);
+ $cachedState = $this->statCache->get($path);
+ if ($cachedState === false) {
+ // we know the file doesn't exist
+ return false;
+ } else if (!is_null($cachedState)) {
+ return true;
+ }
+ // need to get from server
+ $this->propfind($path);
return true; //no 404 exception
} catch (ClientHttpException $e) {
if ($e->getHttpStatus() === 404) {
@@ -228,7 +315,11 @@ class DAV extends \OC\Files\Storage\Common {
/** {@inheritdoc} */
public function unlink($path) {
$this->init();
- return $this->simpleResponse('DELETE', $path, null, 204);
+ $path = $this->cleanPath($path);
+ $result = $this->simpleResponse('DELETE', $path, null, 204);
+ $this->statCache->clear($path . '/');
+ $this->statCache->remove($path);
+ return $result;
}
/** {@inheritdoc} */
@@ -316,6 +407,7 @@ class DAV extends \OC\Files\Storage\Common {
$this->init();
$path = $this->cleanPath($path);
try {
+ // TODO: cacheable ?
$response = $this->client->propfind($this->encodePath($path), array('{DAV:}quota-available-bytes'));
if (isset($response['{DAV:}quota-available-bytes'])) {
return (int)$response['{DAV:}quota-available-bytes'];
@@ -338,6 +430,7 @@ class DAV extends \OC\Files\Storage\Common {
// if file exists, update the mtime, else create a new empty file
if ($this->file_exists($path)) {
try {
+ $this->statCache->remove($path);
$this->client->proppatch($this->encodePath($path), array('{DAV:}lastmodified' => $mtime));
} catch (ClientHttpException $e) {
if ($e->getHttpStatus() === 501) {
@@ -358,10 +451,24 @@ class DAV extends \OC\Files\Storage\Common {
/**
* @param string $path
+ * @param string $data
+ */
+ public function file_put_contents($path, $data) {
+ $path = $this->cleanPath($path);
+ $result = parent::file_put_contents($path, $data);
+ $this->statCache->remove($path);
+ return $result;
+ }
+
+ /**
+ * @param string $path
* @param string $target
*/
protected function uploadFile($path, $target) {
$this->init();
+ // invalidate
+ $target = $this->cleanPath($target);
+ $this->statCache->remove($target);
$source = fopen($path, 'r');
$curl = curl_init();
@@ -394,10 +501,21 @@ class DAV extends \OC\Files\Storage\Common {
/** {@inheritdoc} */
public function rename($path1, $path2) {
$this->init();
- $path1 = $this->encodePath($this->cleanPath($path1));
- $path2 = $this->createBaseUri() . $this->encodePath($this->cleanPath($path2));
+ $path1 = $this->cleanPath($path1);
+ $path2 = $this->cleanPath($path2);
try {
- $this->client->request('MOVE', $path1, null, array('Destination' => $path2));
+ $this->client->request(
+ 'MOVE',
+ $this->encodePath($path1),
+ null,
+ array(
+ 'Destination' => $this->createBaseUri() . $this->encodePath($path2)
+ )
+ );
+ $this->statCache->clear($path1 . '/');
+ $this->statCache->clear($path2 . '/');
+ $this->statCache->set($path1, false);
+ $this->statCache->set($path2, true);
$this->removeCachedFile($path1);
$this->removeCachedFile($path2);
return true;
@@ -418,6 +536,8 @@ class DAV extends \OC\Files\Storage\Common {
$path2 = $this->createBaseUri() . $this->encodePath($this->cleanPath($path2));
try {
$this->client->request('COPY', $path1, null, array('Destination' => $path2));
+ $this->statCache->clear($path2 . '/');
+ $this->statCache->set($path2, true);
$this->removeCachedFile($path2);
return true;
} catch (ClientHttpException $e) {
@@ -432,10 +552,8 @@ class DAV extends \OC\Files\Storage\Common {
/** {@inheritdoc} */
public function stat($path) {
- $this->init();
- $path = $this->cleanPath($path);
try {
- $response = $this->client->propfind($this->encodePath($path), array('{DAV:}getlastmodified', '{DAV:}getcontentlength'));
+ $response = $this->propfind($path);
return array(
'mtime' => strtotime($response['{DAV:}getlastmodified']),
'size' => (int)isset($response['{DAV:}getcontentlength']) ? $response['{DAV:}getcontentlength'] : 0,
@@ -455,10 +573,8 @@ class DAV extends \OC\Files\Storage\Common {
/** {@inheritdoc} */
public function getMimeType($path) {
- $this->init();
- $path = $this->cleanPath($path);
try {
- $response = $this->client->propfind($this->encodePath($path), array('{DAV:}getcontenttype', '{DAV:}resourcetype'));
+ $response = $this->propfind($path);
$responseType = array();
if (isset($response["{DAV:}resourcetype"])) {
$responseType = $response["{DAV:}resourcetype"]->resourceType;
@@ -489,7 +605,7 @@ class DAV extends \OC\Files\Storage\Common {
* @return string
*/
public function cleanPath($path) {
- if ($path === "") {
+ if ($path === '') {
return $path;
}
$path = \OC\Files\Filesystem::normalizePath($path);
@@ -524,6 +640,8 @@ class DAV extends \OC\Files\Storage\Common {
return $response['statusCode'] == $expected;
} catch (ClientHttpException $e) {
if ($e->getHttpStatus() === 404 && $method === 'DELETE') {
+ $this->statCache->clear($path . '/');
+ $this->statCache->set($path, false);
return false;
}
@@ -613,11 +731,9 @@ class DAV extends \OC\Files\Storage\Common {
$this->init();
$path = $this->cleanPath($path);
try {
- $response = $this->client->propfind($this->encodePath($path), array(
- '{DAV:}getlastmodified',
- '{DAV:}getetag',
- '{http://owncloud.org/ns}permissions'
- ));
+ // force refresh for $path
+ $this->statCache->remove($path);
+ $response = $this->propfind($path);
if (isset($response['{DAV:}getetag'])) {
$cachedData = $this->getCache()->get($path);
$etag = trim($response['{DAV:}getetag'], '"');
diff --git a/lib/private/files/storage/local.php b/lib/private/files/storage/local.php
index cf82e218969..6bd9b4401d6 100644
--- a/lib/private/files/storage/local.php
+++ b/lib/private/files/storage/local.php
@@ -228,6 +228,19 @@ if (\OC_Util::runningOnWindows()) {
$this->unlink($path2);
}
+ if ($this->is_dir($path1)) {
+ // we cant move folders across devices, use copy instead
+ $stat1 = stat(dirname($this->getSourcePath($path1)));
+ $stat2 = stat(dirname($this->getSourcePath($path2)));
+ if ($stat1['dev'] !== $stat2['dev']) {
+ $result = $this->copy($path1, $path2);
+ if ($result) {
+ $result &= $this->rmdir($path1);
+ }
+ return $result;
+ }
+ }
+
return rename($this->getSourcePath($path1), $this->getSourcePath($path2));
}
diff --git a/lib/private/files/storage/wrapper/encryption.php b/lib/private/files/storage/wrapper/encryption.php
index 5245fe4cc45..4136e008af9 100644
--- a/lib/private/files/storage/wrapper/encryption.php
+++ b/lib/private/files/storage/wrapper/encryption.php
@@ -1,24 +1,23 @@
<?php
-
/**
- * ownCloud
- *
- * @copyright (C) 2015 ownCloud, Inc.
+ * @author Björn Schießle <schiessle@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
*
- * @author Bjoern Schiessle <schiessle@owncloud.com>
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or any later version.
+ * This 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 library is distributed in the hope that it will be useful,
+ * 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.
+ * 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/>
*
- * 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/>.
*/
namespace OC\Files\Storage\Wrapper;
@@ -227,6 +226,7 @@ class Encryption extends Wrapper {
*/
public function fopen($path, $mode) {
+ $encryptionEnabled = $this->encryptionManager->isEnabled();
$shouldEncrypt = false;
$encryptionModule = null;
$header = $this->getHeader($path);
@@ -259,10 +259,11 @@ class Encryption extends Wrapper {
) {
if (!empty($encryptionModuleId)) {
$encryptionModule = $this->encryptionManager->getEncryptionModule($encryptionModuleId);
- } else {
+ $shouldEncrypt = $encryptionModule->shouldEncrypt($fullPath);
+ } elseif ($encryptionEnabled) {
$encryptionModule = $this->encryptionManager->getDefaultEncryptionModule();
+ $shouldEncrypt = $encryptionModule->shouldEncrypt($fullPath);
}
- $shouldEncrypt = $encryptionModule->shouldEncrypt($fullPath);
} else {
// only get encryption module if we found one in the header
if (!empty($encryptionModuleId)) {
@@ -272,12 +273,11 @@ class Encryption extends Wrapper {
}
} catch (ModuleDoesNotExistsException $e) {
$this->logger->warning('Encryption module "' . $encryptionModuleId .
- '" not found, file will be stored unencrypted');
+ '" not found, file will be stored unencrypted (' . $e->getMessage() . ')');
}
// encryption disabled on write of new file and write to existing unencrypted file -> don't encrypt
- $encEnabled = $this->encryptionManager->isEnabled();
- if (!$encEnabled || !$this->mount->getOption('encrypt', true)) {
+ if (!$encryptionEnabled || !$this->mount->getOption('encrypt', true)) {
if (!$targetExists || !$targetIsEncrypted) {
$shouldEncrypt = false;
}
diff --git a/lib/private/files/stream/encryption.php b/lib/private/files/stream/encryption.php
index b4e06c99943..9ba98e61d3e 100644
--- a/lib/private/files/stream/encryption.php
+++ b/lib/private/files/stream/encryption.php
@@ -1,24 +1,24 @@
<?php
-
/**
- * ownCloud - Encryption stream wrapper
- *
- * @copyright (C) 2015 ownCloud, Inc.
+ * @author Björn Schießle <schiessle@owncloud.com>
+ * @author jknockaert <jasper@knockaert.nl>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
*
- * @author Bjoern Schiessle <schiessle@owncloud.com>
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or any later version.
+ * This 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 library is distributed in the hope that it will be useful,
+ * 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.
+ * 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/>
*
- * 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/>.
*/
namespace OC\Files\Stream;
diff --git a/lib/private/files/view.php b/lib/private/files/view.php
index 0f371bbc5ea..ab7a7d3db9a 100644
--- a/lib/private/files/view.php
+++ b/lib/private/files/view.php
@@ -762,7 +762,9 @@ class View {
if (is_resource($dh)) {
while (($file = readdir($dh)) !== false) {
if (!Filesystem::isIgnoredDir($file)) {
- $result = $this->copy($path1 . '/' . $file, $path2 . '/' . $file, $preserveMtime);
+ if (!$this->copy($path1 . '/' . $file, $path2 . '/' . $file, $preserveMtime)) {
+ $result = false;
+ }
}
}
}
diff --git a/lib/private/installer.php b/lib/private/installer.php
index e30344b1b10..41f13f0f5f9 100644
--- a/lib/private/installer.php
+++ b/lib/private/installer.php
@@ -222,8 +222,13 @@ class OC_Installer{
* @throws Exception
*/
public static function updateAppByOCSId($ocsId) {
- $appData = OCSClient::getApplication($ocsId);
- $download = OCSClient::getApplicationDownload($ocsId, 1);
+ $ocsClient = new OCSClient(
+ \OC::$server->getHTTPClientService(),
+ \OC::$server->getConfig(),
+ \OC::$server->getLogger()
+ );
+ $appData = $ocsClient->getApplication($ocsId);
+ $download = $ocsClient->getApplicationDownload($ocsId);
if (isset($download['downloadlink']) && trim($download['downloadlink']) !== '') {
$download['downloadlink'] = str_replace(' ', '%20', $download['downloadlink']);
@@ -385,8 +390,12 @@ class OC_Installer{
$ocsid=OC_Appconfig::getValue( $app, 'ocsid', '');
if($ocsid<>'') {
-
- $ocsdata=OCSClient::getApplication($ocsid);
+ $ocsClient = new OCSClient(
+ \OC::$server->getHTTPClientService(),
+ \OC::$server->getConfig(),
+ \OC::$server->getLogger()
+ );
+ $ocsdata = $ocsClient->getApplication($ocsid);
$ocsversion= (string) $ocsdata['version'];
$currentversion=OC_App::getAppVersion($app);
if (version_compare($ocsversion, $currentversion, '>')) {
diff --git a/lib/private/ocsclient.php b/lib/private/ocsclient.php
index f69426ddafe..84469cb5e0d 100644
--- a/lib/private/ocsclient.php
+++ b/lib/private/ocsclient.php
@@ -32,36 +32,75 @@
namespace OC;
+use OCP\Http\Client\IClientService;
+use OCP\IConfig;
+use OCP\ILogger;
+
/**
- * This class provides an easy way for apps to store config values in the
- * database.
+ * Class OCSClient is a class for communication with the ownCloud appstore
+ *
+ * @package OC
*/
-
class OCSClient {
+ /** @var IClientService */
+ private $httpClientService;
+ /** @var IConfig */
+ private $config;
+ /** @var ILogger */
+ private $logger;
+
+ /**
+ * @param IClientService $httpClientService
+ * @param IConfig $config
+ * @param ILogger $logger
+ */
+ public function __construct(IClientService $httpClientService,
+ IConfig $config,
+ ILogger $logger) {
+ $this->httpClientService = $httpClientService;
+ $this->config = $config;
+ $this->logger = $logger;
+ }
/**
* Returns whether the AppStore is enabled (i.e. because the AppStore is disabled for EE)
*
* @return bool
*/
- public static function isAppStoreEnabled() {
- if (\OC::$server->getConfig()->getSystemValue('appstoreenabled', true) === false ) {
- return false;
- }
-
- return true;
+ public function isAppStoreEnabled() {
+ return $this->config->getSystemValue('appstoreenabled', true) === true;
}
/**
* Get the url of the OCS AppStore server.
*
* @return string of the AppStore server
- *
- * This function returns the url of the OCS AppStore server. It´s possible
- * to set it in the config file or it will fallback to the default
*/
- private static function getAppStoreURL() {
- return \OC::$server->getConfig()->getSystemValue('appstoreurl', 'https://api.owncloud.com/v1');
+ private function getAppStoreUrl() {
+ return $this->config->getSystemValue('appstoreurl', 'https://api.owncloud.com/v1');
+ }
+
+ /**
+ * @param string $body
+ * @param string $action
+ * @return null|\SimpleXMLElement
+ */
+ private function loadData($body, $action) {
+ $loadEntities = libxml_disable_entity_loader(true);
+ $data = @simplexml_load_string($body);
+ libxml_disable_entity_loader($loadEntities);
+
+ if($data === false) {
+ $this->logger->error(
+ sprintf('Could not get %s, content was no valid XML', $action),
+ [
+ 'app' => 'core',
+ ]
+ );
+ return null;
+ }
+
+ return $data;
}
/**
@@ -71,36 +110,41 @@ class OCSClient {
* @note returns NULL if config value appstoreenabled is set to false
* This function returns a list of all the application categories on the OCS server
*/
- public static function getCategories() {
- if (!self::isAppStoreEnabled()) {
+ public function getCategories() {
+ if (!$this->isAppStoreEnabled()) {
return null;
}
- $url = self::getAppStoreURL() . '/content/categories';
- $client = \OC::$server->getHTTPClientService()->newClient();
+ $client = $this->httpClientService->newClient();
try {
- $response = $client->get($url, ['timeout' => 5]);
+ $response = $client->get(
+ $this->getAppStoreUrl() . '/content/categories',
+ [
+ 'timeout' => 5,
+ ]
+ );
} catch(\Exception $e) {
+ $this->logger->error(
+ sprintf('Could not get categories: %s', $e->getMessage()),
+ [
+ 'app' => 'core',
+ ]
+ );
return null;
}
- if($response->getStatusCode() !== 200) {
+ $data = $this->loadData($response->getBody(), 'categories');
+ if($data === null) {
return null;
}
- $loadEntities = libxml_disable_entity_loader(true);
- $data = simplexml_load_string($response->getBody());
- libxml_disable_entity_loader($loadEntities);
-
$tmp = $data->data;
$cats = [];
foreach ($tmp->category as $value) {
-
$id = (int)$value->id;
$name = (string)$value->name;
$cats[$id] = $name;
-
}
return $cats;
@@ -108,50 +152,54 @@ class OCSClient {
/**
* Get all the applications from the OCS server
- *
- * @return array|null an array of application data or null
- *
- * This function returns a list of all the applications on the OCS server
- * @param array|string $categories
+ * @param array $categories
* @param int $page
* @param string $filter
+ * @return array An array of application data
*/
- public static function getApplications($categories, $page, $filter) {
- if (!self::isAppStoreEnabled()) {
- return (array());
+ public function getApplications(array $categories, $page, $filter) {
+ if (!$this->isAppStoreEnabled()) {
+ return [];
}
- if (is_array($categories)) {
- $categoriesString = implode('x', $categories);
- } else {
- $categoriesString = $categories;
- }
-
- $version = '&version=' . implode('x', \OC_Util::getVersion());
- $filterUrl = '&filter=' . urlencode($filter);
- $url = self::getAppStoreURL() . '/content/data?categories=' . urlencode($categoriesString)
- . '&sortmode=new&page=' . urlencode($page) . '&pagesize=100' . $filterUrl . $version;
- $apps = [];
-
- $client = \OC::$server->getHTTPClientService()->newClient();
+ $client = $this->httpClientService->newClient();
try {
- $response = $client->get($url, ['timeout' => 5]);
+ $response = $client->get(
+ $this->getAppStoreUrl() . '/content/data',
+ [
+ 'timeout' => 5,
+ 'query' => [
+ 'version' => implode('x', \OC_Util::getVersion()),
+ 'filter' => $filter,
+ 'categories' => implode('x', $categories),
+ 'sortmode' => 'new',
+ 'page' => $page,
+ 'pagesize' => 100,
+ 'approved' => $filter
+ ],
+ ]
+ );
} catch(\Exception $e) {
- return null;
+ $this->logger->error(
+ sprintf('Could not get applications: %s', $e->getMessage()),
+ [
+ 'app' => 'core',
+ ]
+ );
+ return [];
}
- if($response->getStatusCode() !== 200) {
- return null;
+ $data = $this->loadData($response->getBody(), 'applications');
+ if($data === null) {
+ return [];
}
- $loadEntities = libxml_disable_entity_loader(true);
- $data = simplexml_load_string($response->getBody());
- libxml_disable_entity_loader($loadEntities);
-
$tmp = $data->data->content;
$tmpCount = count($tmp);
+
+ $apps = [];
for ($i = 0; $i < $tmpCount; $i++) {
- $app = array();
+ $app = [];
$app['id'] = (string)$tmp[$i]->id;
$app['name'] = (string)$tmp[$i]->name;
$app['label'] = (string)$tmp[$i]->label;
@@ -159,6 +207,7 @@ class OCSClient {
$app['type'] = (string)$tmp[$i]->typeid;
$app['typename'] = (string)$tmp[$i]->typename;
$app['personid'] = (string)$tmp[$i]->personid;
+ $app['profilepage'] = (string)$tmp[$i]->profilepage;
$app['license'] = (string)$tmp[$i]->license;
$app['detailpage'] = (string)$tmp[$i]->detailpage;
$app['preview'] = (string)$tmp[$i]->smallpreviewpic1;
@@ -167,9 +216,11 @@ class OCSClient {
$app['description'] = (string)$tmp[$i]->description;
$app['score'] = (string)$tmp[$i]->score;
$app['downloads'] = (int)$tmp[$i]->downloads;
+ $app['level'] = (int)$tmp[$i]->approved;
$apps[] = $app;
}
+
return $apps;
}
@@ -182,84 +233,94 @@ class OCSClient {
*
* This function returns an applications from the OCS server
*/
- public static function getApplication($id) {
- if (!self::isAppStoreEnabled()) {
+ public function getApplication($id) {
+ if (!$this->isAppStoreEnabled()) {
return null;
}
- $url = self::getAppStoreURL() . '/content/data/' . urlencode($id);
- $client = \OC::$server->getHTTPClientService()->newClient();
+
+ $client = $this->httpClientService->newClient();
try {
- $response = $client->get($url, ['timeout' => 5]);
+ $response = $client->get(
+ $this->getAppStoreUrl() . '/content/data/' . urlencode($id),
+ [
+ 'timeout' => 5,
+ ]
+ );
} catch(\Exception $e) {
+ $this->logger->error(
+ sprintf('Could not get application: %s', $e->getMessage()),
+ [
+ 'app' => 'core',
+ ]
+ );
return null;
}
- if($response->getStatusCode() !== 200) {
+ $data = $this->loadData($response->getBody(), 'application');
+ if($data === null) {
return null;
}
- $loadEntities = libxml_disable_entity_loader(true);
- $data = simplexml_load_string($response->getBody());
- libxml_disable_entity_loader($loadEntities);
-
$tmp = $data->data->content;
- if (is_null($tmp)) {
- \OC_Log::write('core', 'Invalid OCS content returned for app ' . $id, \OC_Log::FATAL);
- return null;
- }
+
$app = [];
- $app['id'] = $tmp->id;
- $app['name'] = $tmp->name;
- $app['version'] = $tmp->version;
- $app['type'] = $tmp->typeid;
- $app['label'] = $tmp->label;
- $app['typename'] = $tmp->typename;
- $app['personid'] = $tmp->personid;
- $app['detailpage'] = $tmp->detailpage;
- $app['preview1'] = $tmp->smallpreviewpic1;
- $app['preview2'] = $tmp->smallpreviewpic2;
- $app['preview3'] = $tmp->smallpreviewpic3;
+ $app['id'] = (int)$tmp->id;
+ $app['name'] = (string)$tmp->name;
+ $app['version'] = (string)$tmp->version;
+ $app['type'] = (string)$tmp->typeid;
+ $app['label'] = (string)$tmp->label;
+ $app['typename'] = (string)$tmp->typename;
+ $app['personid'] = (string)$tmp->personid;
+ $app['profilepage'] = (string)$tmp->profilepage;
+ $app['detailpage'] = (string)$tmp->detailpage;
+ $app['preview1'] = (string)$tmp->smallpreviewpic1;
+ $app['preview2'] = (string)$tmp->smallpreviewpic2;
+ $app['preview3'] = (string)$tmp->smallpreviewpic3;
$app['changed'] = strtotime($tmp->changed);
- $app['description'] = $tmp->description;
- $app['detailpage'] = $tmp->detailpage;
- $app['score'] = $tmp->score;
+ $app['description'] = (string)$tmp->description;
+ $app['detailpage'] = (string)$tmp->detailpage;
+ $app['score'] = (int)$tmp->score;
return $app;
}
/**
* Get the download url for an application from the OCS server
- *
+ * @param $id
* @return array|null an array of application data or null
- *
- * This function returns an download url for an applications from the OCS server
- * @param string $id
- * @param integer $item
*/
- public static function getApplicationDownload($id, $item) {
- if (!self::isAppStoreEnabled()) {
+ public function getApplicationDownload($id) {
+ if (!$this->isAppStoreEnabled()) {
return null;
}
- $url = self::getAppStoreURL() . '/content/download/' . urlencode($id) . '/' . urlencode($item);
- $client = \OC::$server->getHTTPClientService()->newClient();
+ $url = $this->getAppStoreUrl() . '/content/download/' . urlencode($id) . '/1';
+ $client = $this->httpClientService->newClient();
try {
- $response = $client->get($url, ['timeout' => 5]);
+ $response = $client->get(
+ $url,
+ [
+ 'timeout' => 5,
+ ]
+ );
} catch(\Exception $e) {
+ $this->logger->error(
+ sprintf('Could not get application download URL: %s', $e->getMessage()),
+ [
+ 'app' => 'core',
+ ]
+ );
return null;
}
- if($response->getStatusCode() !== 200) {
+ $data = $this->loadData($response->getBody(), 'application download URL');
+ if($data === null) {
return null;
}
- $loadEntities = libxml_disable_entity_loader(true);
- $data = simplexml_load_string($response->getBody());
- libxml_disable_entity_loader($loadEntities);
-
$tmp = $data->data->content;
- $app = array();
+ $app = [];
if (isset($tmp->downloadlink)) {
- $app['downloadlink'] = $tmp->downloadlink;
+ $app['downloadlink'] = (string)$tmp->downloadlink;
} else {
$app['downloadlink'] = '';
}
diff --git a/lib/private/preview.php b/lib/private/preview.php
index f8e90cafbc1..eab60e10862 100644
--- a/lib/private/preview.php
+++ b/lib/private/preview.php
@@ -3,11 +3,11 @@
* @author Björn Schießle <schiessle@owncloud.com>
* @author Frank Karlitschek <frank@owncloud.org>
* @author Georg Ehrke <georg@owncloud.com>
- * @author Georg Ehrke <georg@ownCloud.com>
* @author Joas Schilling <nickvergessen@owncloud.com>
* @author Jörn Friedrich Dreyer <jfd@butonic.de>
* @author Lukas Reschke <lukas@owncloud.com>
* @author Morris Jobke <hey@morrisjobke.de>
+ * @author Olivier Paroz <owncloud@interfasys.ch>
* @author Robin Appelman <icewind@owncloud.com>
* @author Robin McCorkell <rmccorkell@karoshi.org.uk>
* @author Thomas Müller <thomas.mueller@tmit.eu>
@@ -95,9 +95,9 @@ class Preview {
$this->userView = new \OC\Files\View('/' . $user);
//set config
- $this->configMaxX = \OC_Config::getValue('preview_max_x', null);
- $this->configMaxY = \OC_Config::getValue('preview_max_y', null);
- $this->maxScaleFactor = \OC_Config::getValue('preview_max_scale_factor', 2);
+ $this->configMaxX = \OC::$server->getConfig()->getSystemValue('preview_max_x', 2048);
+ $this->configMaxY = \OC::$server->getConfig()->getSystemValue('preview_max_y', 2048);
+ $this->maxScaleFactor = \OC::$server->getConfig()->getSystemValue('preview_max_scale_factor', 2);
//save parameters
$this->setFile($file);
@@ -246,7 +246,7 @@ class Preview {
$configMaxX = $this->getConfigMaxX();
if (!is_null($configMaxX)) {
if ($maxX > $configMaxX) {
- \OC_Log::write('core', 'maxX reduced from ' . $maxX . ' to ' . $configMaxX, \OC_Log::DEBUG);
+ \OCP\Util::writeLog('core', 'maxX reduced from ' . $maxX . ' to ' . $configMaxX, \OCP\Util::DEBUG);
$maxX = $configMaxX;
}
}
@@ -267,7 +267,7 @@ class Preview {
$configMaxY = $this->getConfigMaxY();
if (!is_null($configMaxY)) {
if ($maxY > $configMaxY) {
- \OC_Log::write('core', 'maxX reduced from ' . $maxY . ' to ' . $configMaxY, \OC_Log::DEBUG);
+ \OCP\Util::writeLog('core', 'maxX reduced from ' . $maxY . ' to ' . $configMaxY, \OCP\Util::DEBUG);
$maxY = $configMaxY;
}
}
@@ -304,12 +304,12 @@ class Preview {
public function isFileValid() {
$file = $this->getFile();
if ($file === '') {
- \OC_Log::write('core', 'No filename passed', \OC_Log::DEBUG);
+ \OCP\Util::writeLog('core', 'No filename passed', \OCP\Util::DEBUG);
return false;
}
if (!$this->fileView->file_exists($file)) {
- \OC_Log::write('core', 'File:"' . $file . '" not found', \OC_Log::DEBUG);
+ \OCP\Util::writeLog('core', 'File:"' . $file . '" not found', \OCP\Util::DEBUG);
return false;
}
@@ -321,9 +321,7 @@ class Preview {
* @return bool
*/
public function deletePreview() {
- $file = $this->getFile();
-
- $fileInfo = $this->getFileInfo($file);
+ $fileInfo = $this->getFileInfo();
if($fileInfo !== null && $fileInfo !== false) {
$fileId = $fileInfo->getId();
@@ -357,7 +355,8 @@ class Preview {
}
/**
- * check if thumbnail or bigger version of thumbnail of file is cached
+ * Checks if thumbnail or bigger version of thumbnail of file is already cached
+ *
* @param int $fileId fileId of the original image
* @return string|false path to thumbnail if it exists or false
*/
@@ -366,9 +365,11 @@ class Preview {
return false;
}
+ // This gives us a calculated path to a preview of asked dimensions
+ // thumbnailFolder/fileId/my_image-<maxX>-<maxY>.png
$preview = $this->buildCachePath($fileId);
- //does a preview with the wanted height and width already exist?
+ // This checks if a preview exists at that location
if ($this->userView->file_exists($preview)) {
return $preview;
}
@@ -377,34 +378,39 @@ class Preview {
}
/**
- * check if a bigger version of thumbnail of file is cached
+ * Checks if a bigger version of a file preview is cached and if not
+ * return the preview of max allowed dimensions
+ *
* @param int $fileId fileId of the original image
+ *
* @return string|false path to bigger thumbnail if it exists or false
- */
+ */
private function isCachedBigger($fileId) {
if (is_null($fileId)) {
return false;
}
- // in order to not loose quality we better generate aspect preserving previews from the original file
- if ($this->keepAspect) {
- return false;
- }
-
$maxX = $this->getMaxX();
//array for usable cached thumbnails
+ // FIXME: Checking only the width could lead to issues
$possibleThumbnails = $this->getPossibleThumbnails($fileId);
foreach ($possibleThumbnails as $width => $path) {
- if ($width < $maxX) {
+ if ($width === 'max' || $width < $maxX) {
continue;
} else {
return $path;
}
}
+ // At this stage, we didn't find a preview, so if the folder is not empty,
+ // we return the max preview we generated on the first run
+ if ($possibleThumbnails) {
+ return $possibleThumbnails['max'];
+ }
+
return false;
}
@@ -421,7 +427,7 @@ class Preview {
$previewPath = $this->getPreviewPath($fileId);
- $wantedAspectRatio = (float) ($this->getMaxX() / $this->getMaxY());
+ $wantedAspectRatio = (float)($this->getMaxX() / $this->getMaxY());
//array for usable cached thumbnails
$possibleThumbnails = array();
@@ -429,6 +435,11 @@ class Preview {
$allThumbnails = $this->userView->getDirectoryContent($previewPath);
foreach ($allThumbnails as $thumbnail) {
$name = rtrim($thumbnail['name'], '.png');
+ // Always add the max preview to the array
+ if (strpos($name, 'max')) {
+ $possibleThumbnails['max'] = $thumbnail['path'];
+ continue;
+ }
list($x, $y, $aspectRatio) = $this->getDimensionsFromFilename($name);
if (abs($aspectRatio - $wantedAspectRatio) >= 0.000001
@@ -482,7 +493,11 @@ class Preview {
}
/**
- * return a preview of a file
+ * Returns a preview of a file
+ *
+ * The cache is searched first and if nothing usable was found then a preview is
+ * generated by one of the providers
+ *
* @return \OCP\IImage
*/
public function getPreview() {
@@ -491,76 +506,22 @@ class Preview {
}
$this->preview = null;
- $file = $this->getFile();
- $maxX = $this->getMaxX();
- $maxY = $this->getMaxY();
- $scalingUp = $this->getScalingUp();
-
- $fileInfo = $this->getFileInfo($file);
- if($fileInfo === null || $fileInfo === false) {
+ $fileInfo = $this->getFileInfo();
+ if ($fileInfo === null || $fileInfo === false) {
return new \OC_Image();
}
- $fileId = $fileInfo->getId();
+ $fileId = $fileInfo->getId();
$cached = $this->isCached($fileId);
if ($cached) {
- $stream = $this->userView->fopen($cached, 'r');
- $this->preview = null;
- if ($stream) {
- $image = new \OC_Image();
- $image->loadFromFileHandle($stream);
- $this->preview = $image->valid() ? $image : null;
-
- $this->resizeAndCrop();
- fclose($stream);
- }
+ $this->getCachedPreview($fileId, $cached);
}
if (is_null($this->preview)) {
- $preview = null;
-
- $previewProviders = \OC::$server->getPreviewManager()->getProviders();
- foreach ($previewProviders as $supportedMimeType => $providers) {
- if (!preg_match($supportedMimeType, $this->mimeType)) {
- continue;
- }
-
- foreach ($providers as $closure) {
- $provider = $closure();
- if (!($provider instanceof \OCP\Preview\IProvider)) {
- continue;
- }
-
- \OC_Log::write('core', 'Generating preview for "' . $file . '" with "' . get_class($provider) . '"', \OC_Log::DEBUG);
-
- /** @var $provider Provider */
- $preview = $provider->getThumbnail($file, $maxX, $maxY, $scalingUp, $this->fileView);
-
- if (!($preview instanceof \OCP\IImage)) {
- continue;
- }
-
- $this->preview = $preview;
- $this->resizeAndCrop();
-
- $previewPath = $this->getPreviewPath($fileId);
- $cachePath = $this->buildCachePath($fileId);
-
- if ($this->userView->is_dir($this->getThumbnailsFolder() . '/') === false) {
- $this->userView->mkdir($this->getThumbnailsFolder() . '/');
- }
-
- if ($this->userView->is_dir($previewPath) === false) {
- $this->userView->mkdir($previewPath);
- }
-
- $this->userView->file_put_contents($cachePath, $preview->data());
-
- break 2;
- }
- }
+ $this->generatePreview($fileId);
}
+ // We still don't have a preview, so we generate an empty object which can't be displayed
if (is_null($this->preview)) {
$this->preview = new \OC_Image();
}
@@ -588,18 +549,58 @@ class Preview {
}
/**
+ * Retrieves the preview from the cache and resizes it if necessary
+ *
+ * @param int $fileId fileId of the original image
+ * @param string $cached the path to the cached preview
+ */
+ private function getCachedPreview($fileId, $cached) {
+ $stream = $this->userView->fopen($cached, 'r');
+ $this->preview = null;
+ if ($stream) {
+ $image = new \OC_Image();
+ $image->loadFromFileHandle($stream);
+
+ $this->preview = $image->valid() ? $image : null;
+
+ $maxX = (int)$this->getMaxX();
+ $maxY = (int)$this->getMaxY();
+ $previewX = (int)$this->preview->width();
+ $previewY = (int)$this->preview->height();
+
+ if ($previewX !== $maxX && $previewY !== $maxY) {
+ $this->resizeAndStore($fileId);
+ }
+
+ fclose($stream);
+ }
+ }
+
+ /**
+ * Resizes, crops, fixes orientation and stores in the cache
+ *
+ * @param int $fileId fileId of the original image
+ */
+ private function resizeAndStore($fileId) {
+ // Resize and store
+ $this->resizeAndCrop();
+ // We save a copy in the cache to speed up future calls
+ $cachePath = $this->buildCachePath($fileId);
+ $this->userView->file_put_contents($cachePath, $this->preview->data());
+ }
+
+ /**
* resize, crop and fix orientation
- * @return void
+ *
+ * @param bool $max
*/
- private function resizeAndCrop() {
+ private function resizeAndCrop($max = false) {
$image = $this->preview;
- $x = $this->getMaxX();
- $y = $this->getMaxY();
- $scalingUp = $this->getScalingUp();
- $maxScaleFactor = $this->getMaxScaleFactor();
+
+ list($x, $y, $scalingUp, $maxScaleFactor) = $this->getResizeData($max);
if (!($image instanceof \OCP\IImage)) {
- \OC_Log::write('core', '$this->preview is not an instance of \OCP\IImage', \OC_Log::DEBUG);
+ \OCP\Util::writeLog('core', '$this->preview is not an instance of OC_Image', \OCP\Util::DEBUG);
return;
}
@@ -617,6 +618,7 @@ class Preview {
}
}
+ // The preview already has the asked dimensions
if ($x === $realX && $y === $realY) {
$this->preview = $image;
return;
@@ -639,7 +641,7 @@ class Preview {
if (!is_null($maxScaleFactor)) {
if ($factor > $maxScaleFactor) {
- \OC_Log::write('core', 'scale factor reduced from ' . $factor . ' to ' . $maxScaleFactor, \OC_Log::DEBUG);
+ \OCP\Util::writeLog('core', 'scale factor reduced from ' . $factor . ' to ' . $maxScaleFactor, \OCP\Util::DEBUG);
$factor = $maxScaleFactor;
}
}
@@ -649,11 +651,13 @@ class Preview {
$image->preciseResize($newXSize, $newYSize);
+ // The preview has been upscaled and now has the asked dimensions
if ($newXSize === $x && $newYSize === $y) {
$this->preview = $image;
return;
}
+ // One dimension of the upscaled preview is too big
if ($newXSize >= $x && $newYSize >= $y) {
$cropX = floor(abs($x - $newXSize) * 0.5);
//don't crop previews on the Y axis, this sucks if it's a document.
@@ -666,6 +670,7 @@ class Preview {
return;
}
+ // One dimension of the upscaled preview is too small and we're allowed to scale up
if (($newXSize < $x || $newYSize < $y) && $scalingUp) {
if ($newXSize > $x) {
$cropX = floor(($newXSize - $x) * 0.5);
@@ -698,11 +703,161 @@ class Preview {
$image = new \OC_Image($backgroundLayer);
$this->preview = $image;
+
return;
}
}
/**
+ * Returns data to be used to resize a preview
+ *
+ * @param $max
+ *
+ * @return array
+ */
+ private function getResizeData($max) {
+ if (!$max) {
+ $x = $this->getMaxX();
+ $y = $this->getMaxY();
+ $scalingUp = $this->getScalingUp();
+ $maxScaleFactor = $this->getMaxScaleFactor();
+ } else {
+ $x = $this->configMaxX;
+ $y = $this->configMaxY;
+ $scalingUp = false;
+ $maxScaleFactor =1;
+ }
+
+ return [$x, $y, $scalingUp, $maxScaleFactor];
+ }
+
+ /**
+ * Returns the path to a preview based on its dimensions and aspect
+ *
+ * @param int $fileId
+ *
+ * @return string
+ */
+ private function buildCachePath($fileId) {
+ $maxX = $this->getMaxX();
+ $maxY = $this->getMaxY();
+
+ $previewPath = $this->getPreviewPath($fileId);
+ $previewPath = $previewPath . strval($maxX) . '-' . strval($maxY);
+ if ($this->keepAspect) {
+ $previewPath .= '-with-aspect';
+ }
+ $previewPath .= '.png';
+
+ return $previewPath;
+ }
+
+ /**
+ * @param int $fileId
+ *
+ * @return string
+ */
+ private function getPreviewPath($fileId) {
+ return $this->getThumbnailsFolder() . '/' . $fileId . '/';
+ }
+
+ /**
+ * Asks the provider to send a preview of the file of maximum dimensions
+ * and after saving it in the cache, it is then resized to the asked dimensions
+ *
+ * This is only called once in order to generate a large PNG of dimensions defined in the
+ * configuration file. We'll be able to quickly resize it later on.
+ * We never upscale the original conversion as this will be done later by the resizing operation
+ *
+ * @param int $fileId fileId of the original image
+ */
+ private function generatePreview($fileId) {
+ $file = $this->getFile();
+ $preview = null;
+
+ $previewProviders = \OC::$server->getPreviewManager()->getProviders();
+ foreach ($previewProviders as $supportedMimeType => $providers) {
+ if (!preg_match($supportedMimeType, $this->mimeType)) {
+ continue;
+ }
+
+ foreach ($providers as $closure) {
+ $provider = $closure();
+ if (!($provider instanceof \OCP\Preview\IProvider)) {
+ continue;
+ }
+
+ \OCP\Util::writeLog(
+ 'core', 'Generating preview for "' . $file . '" with "' . get_class($provider)
+ . '"', \OCP\Util::DEBUG
+ );
+
+ /** @var $provider Provider */
+ $preview = $provider->getThumbnail(
+ $file, $this->configMaxX, $this->configMaxY, $scalingUp = false, $this->fileView
+ );
+
+ if (!($preview instanceof \OCP\IImage)) {
+ continue;
+ }
+
+ $this->preview = $preview;
+ $previewPath = $this->getPreviewPath($fileId);
+
+ if ($this->userView->is_dir($this->getThumbnailsFolder() . '/') === false) {
+ $this->userView->mkdir($this->getThumbnailsFolder() . '/');
+ }
+
+ if ($this->userView->is_dir($previewPath) === false) {
+ $this->userView->mkdir($previewPath);
+ }
+
+ // This stores our large preview so that it can be used in subsequent resizing requests
+ $this->storeMaxPreview($previewPath);
+
+ break 2;
+ }
+ }
+
+ // The providers have been kind enough to give us a preview
+ if ($preview) {
+ $this->resizeAndStore($fileId);
+ }
+ }
+
+ /**
+ * Stores the max preview in the cache
+ *
+ * @param string $previewPath path to the preview
+ */
+ private function storeMaxPreview($previewPath) {
+ $maxPreview = false;
+ $preview = $this->preview;
+
+ $allThumbnails = $this->userView->getDirectoryContent($previewPath);
+ // This is so that the cache doesn't need emptying when upgrading
+ // Can be replaced by an upgrade script...
+ foreach ($allThumbnails as $thumbnail) {
+ $name = rtrim($thumbnail['name'], '.png');
+ if (strpos($name, 'max')) {
+ $maxPreview = true;
+ break;
+ }
+ }
+ // We haven't found the max preview, so we create it
+ if (!$maxPreview) {
+ // Most providers don't resize their thumbnails yet
+ $this->resizeAndCrop(true);
+
+ $maxX = $preview->width();
+ $maxY = $preview->height();
+ $previewPath = $previewPath . strval($maxX) . '-' . strval($maxY);
+ $previewPath .= '-max.png';
+ $this->userView->file_put_contents($previewPath, $preview->data());
+ }
+ }
+
+ /**
* @param array $args
*/
public static function post_write($args) {
@@ -791,30 +946,4 @@ class Preview {
$preview->deleteAllPreviews();
}
- /**
- * @param int $fileId
- * @return string
- */
- private function buildCachePath($fileId) {
- $maxX = $this->getMaxX();
- $maxY = $this->getMaxY();
-
- $previewPath = $this->getPreviewPath($fileId);
- $preview = $previewPath . strval($maxX) . '-' . strval($maxY);
- if ($this->keepAspect) {
- $preview .= '-with-aspect';
- }
- $preview .= '.png';
-
- return $preview;
- }
-
-
- /**
- * @param int $fileId
- * @return string
- */
- private function getPreviewPath($fileId) {
- return $this->getThumbnailsFolder() . '/' . $fileId . '/';
- }
}
diff --git a/lib/private/server.php b/lib/private/server.php
index 661aaf6786d..6df7722973e 100644
--- a/lib/private/server.php
+++ b/lib/private/server.php
@@ -228,8 +228,12 @@ class Server extends SimpleContainer implements IServerContainer {
new ArrayCache()
);
});
- $this->registerService('ActivityManager', function ($c) {
- return new ActivityManager();
+ $this->registerService('ActivityManager', function (Server $c) {
+ return new ActivityManager(
+ $c->getRequest(),
+ $c->getUserSession(),
+ $c->getConfig()
+ );
});
$this->registerService('AvatarManager', function ($c) {
return new AvatarManager();
@@ -396,6 +400,13 @@ class Server extends SimpleContainer implements IServerContainer {
new \OC_Defaults()
);
});
+ $this->registerService('OcsClient', function(Server $c) {
+ return new OCSClient(
+ $this->getHTTPClientService(),
+ $this->getConfig(),
+ $this->getLogger()
+ );
+ });
}
/**
@@ -435,7 +446,7 @@ class Server extends SimpleContainer implements IServerContainer {
* currently being processed is returned from this method.
* In case the current execution was not initiated by a web request null is returned
*
- * @return \OCP\IRequest|null
+ * @return \OCP\IRequest
*/
function getRequest() {
return $this->query('Request');
@@ -837,6 +848,13 @@ class Server extends SimpleContainer implements IServerContainer {
}
/**
+ * @return \OC\OCSClient
+ */
+ public function getOcsClient() {
+ return $this->query('OcsClient');
+ }
+
+ /**
* @return \OCP\IDateTimeZone
*/
public function getDateTimeZone() {
diff --git a/lib/private/share/helper.php b/lib/private/share/helper.php
index 5345c8a018f..65167dd7549 100644
--- a/lib/private/share/helper.php
+++ b/lib/private/share/helper.php
@@ -50,34 +50,19 @@ class Helper extends \OC\Share\Constants {
}
return $backend->generateTarget($itemSource, false);
} else {
- if ($itemType == 'file' || $itemType == 'folder') {
- $column = 'file_target';
- $columnSource = 'file_source';
- } else {
- $column = 'item_target';
- $columnSource = 'item_source';
- }
if ($shareType == self::SHARE_TYPE_USER) {
// Share with is a user, so set share type to user and groups
$shareType = self::$shareTypeUserAndGroups;
}
- $exclude = array();
-
- $result = \OCP\Share::getItemsSharedWithUser($itemType, $shareWith);
- foreach ($result as $row) {
- if ($row['permissions'] > 0) {
- $exclude[] = $row[$column];
- }
- }
// Check if suggested target exists first
if (!isset($suggestedTarget)) {
$suggestedTarget = $itemSource;
}
if ($shareType == self::SHARE_TYPE_GROUP) {
- $target = $backend->generateTarget($suggestedTarget, false, $exclude);
+ $target = $backend->generateTarget($suggestedTarget, false);
} else {
- $target = $backend->generateTarget($suggestedTarget, $shareWith, $exclude);
+ $target = $backend->generateTarget($suggestedTarget, $shareWith);
}
return $target;
diff --git a/lib/private/share/share.php b/lib/private/share/share.php
index 98c612d5eb6..729dbe79d38 100644
--- a/lib/private/share/share.php
+++ b/lib/private/share/share.php
@@ -37,6 +37,10 @@
namespace OC\Share;
+use OCP\IUserSession;
+use OC\DB\Connection;
+use OCP\IConfig;
+
/**
* This class provides the ability for apps to share their content between users.
* Apps must create a backend class that implements OCP\Share_Backend and register it with this class.
@@ -1151,6 +1155,78 @@ class Share extends \OC\Share\Constants {
}
/**
+ * Retrieve the owner of a connection
+ *
+ * @param Connection $connection
+ * @param int $shareId
+ * @throws \Exception
+ * @return string uid of share owner
+ */
+ private static function getShareOwner(Connection $connection, $shareId) {
+ $qb = $connection->createQueryBuilder();
+
+ $qb->select('`uid_owner`')
+ ->from('`*PREFIX*share`')
+ ->where('`id` = :shareId')
+ ->setParameter(':shareId', $shareId);
+ $result = $qb->execute();
+ $result = $result->fetch();
+
+ if (empty($result)) {
+ throw new \Exception('Share not found');
+ }
+
+ return $result['uid_owner'];
+ }
+
+ /**
+ * Set expiration date for a share
+ *
+ * @param IUserSession $userSession
+ * @param Connection $connection
+ * @param IConfig $config
+ * @param int $shareId
+ * @param string $password
+ * @throws \Exception
+ * @return boolean
+ */
+ public static function setPassword(IUserSession $userSession,
+ Connection $connection,
+ IConfig $config,
+ $shareId, $password) {
+ $user = $userSession->getUser();
+ if (is_null($user)) {
+ throw new \Exception("User not logged in");
+ }
+
+ $uid = self::getShareOwner($connection, $shareId);
+
+ if ($uid !== $user->getUID()) {
+ throw new \Exception('Cannot update share of a different user');
+ }
+
+ if ($password === '') {
+ $password = null;
+ }
+
+ //If passwords are enforced the password can't be null
+ if (self::enforcePassword($config) && is_null($password)) {
+ throw new \Exception('Cannot remove password');
+ }
+
+ $qb = $connection->createQueryBuilder();
+ $qb->update('`*PREFIX*share`')
+ ->set('`share_with`', ':pass')
+ ->where('`id` = :shareId')
+ ->setParameter(':pass', is_null($password) ? 'NULL' : $qb->expr()->literal(\OC::$server->getHasher()->hash($password)))
+ ->setParameter(':shareId', $shareId);
+
+ $qb->execute();
+
+ return true;
+ }
+
+ /**
* Checks whether a share has expired, calls unshareItem() if yes.
* @param array $item Share data (usually database row)
* @return boolean True if item was expired, false otherwise.
@@ -1829,7 +1905,11 @@ class Share extends \OC\Share\Constants {
$isGroupShare = false;
if ($shareType == self::SHARE_TYPE_GROUP) {
$isGroupShare = true;
- $users = \OC_Group::usersInGroup($shareWith['group']);
+ if (isset($shareWith['users'])) {
+ $users = $shareWith['users'];
+ } else {
+ $users = \OC_Group::usersInGroup($shareWith['group']);
+ }
// remove current user from list
if (in_array(\OCP\User::getUser(), $users)) {
unset($users[array_search(\OCP\User::getUser(), $users)]);
@@ -1940,7 +2020,8 @@ class Share extends \OC\Share\Constants {
$fileTarget = null;
}
- if ($itemTarget === $groupItemTarget && (isset($fileSource) && $fileTarget === $groupItemTarget)) {
+ if (($itemTarget === $groupItemTarget) &&
+ (!isset($fileSource) || $fileTarget === $groupFileTarget)) {
continue;
}
}
@@ -2429,4 +2510,12 @@ class Share extends \OC\Share\Constants {
return false;
}
+ /**
+ * @param IConfig $config
+ * @return bool
+ */
+ public static function enforcePassword(IConfig $config) {
+ $enforcePassword = $config->getAppValue('core', 'shareapi_enforce_links_password', 'no');
+ return ($enforcePassword === "yes") ? true : false;
+ }
}
diff --git a/lib/private/tags.php b/lib/private/tags.php
index 6a66cbde4b3..975b6dbfe0d 100644
--- a/lib/private/tags.php
+++ b/lib/private/tags.php
@@ -133,8 +133,6 @@ class Tags implements \OCP\ITags {
if(count($defaultTags) > 0 && count($this->tags) === 0) {
$this->addMultiple($defaultTags, true);
}
- \OCP\Util::writeLog('core', __METHOD__.', tags: ' . print_r($this->tags, true),
- \OCP\Util::DEBUG);
}
/**
diff --git a/lib/private/templatelayout.php b/lib/private/templatelayout.php
index ee1412fba74..448276ca7fe 100644
--- a/lib/private/templatelayout.php
+++ b/lib/private/templatelayout.php
@@ -107,7 +107,7 @@ class OC_TemplateLayout extends OC_Template {
$userDisplayName = OC_User::getDisplayName();
$this->assign('user_displayname', $userDisplayName);
$this->assign('user_uid', OC_User::getUser());
- $this->assign('appsmanagement_active', strpos(\OC::$server->getRequest()->getRequestUri(), OC_Helper::linkToRoute('settings_apps')) === 0 );
+ $this->assign('appsmanagement_active', strpos(\OC::$server->getRequest()->getRequestUri(), \OC::$server->getURLGenerator()->linkToRoute('settings.AppSettings.viewApps')) === 0 );
$this->assign('enableAvatars', $this->config->getSystemValue('enable_avatars', true));
$this->assign('userAvatarSet', \OC_Helper::userAvatarSet(OC_User::getUser()));
} else if ($renderAs == 'error') {
diff --git a/lib/private/util.php b/lib/private/util.php
index 3fd0f844684..102dc8c59db 100644
--- a/lib/private/util.php
+++ b/lib/private/util.php
@@ -566,6 +566,19 @@ class OC_Util {
$webServerRestart = true;
}
+ // Check if server running on Windows platform
+ if(OC_Util::runningOnWindows()) {
+ $errors[] = [
+ 'error' => $l->t('Microsoft Windows Platform is not supported'),
+ 'hint' => $l->t('Running ownCloud Server on the Microsoft Windows platform is not supported. We suggest you ' .
+ 'use a Linux server in a virtual machine if you have no option for migrating the server itself. ' .
+ 'Find Linux packages as well as easy to deploy virtual machine images on <a href="%s">%s</a>. ' .
+ 'For migrating existing installations to Linux you can find some tips and a migration script ' .
+ 'in <a href="%s">our documentation</a>.',
+ ['https://owncloud.org/install/', 'owncloud.org/install/', 'https://owncloud.org/?p=8045'])
+ ];
+ }
+
// Check if config folder is writable.
if (!is_writable(OC::$configDir) or !is_readable(OC::$configDir)) {
$errors[] = array(
diff --git a/lib/public/activity/imanager.php b/lib/public/activity/imanager.php
index f7885860c4a..2e55c8b45b2 100644
--- a/lib/public/activity/imanager.php
+++ b/lib/public/activity/imanager.php
@@ -136,4 +136,14 @@ interface IManager {
* @return array
*/
function getQueryForFilter($filter);
+
+ /**
+ * Get the user we need to use
+ *
+ * Either the user is logged in, or we try to get it from the token
+ *
+ * @return string
+ * @throws \UnexpectedValueException If the token is invalid, does not exist or is not unique
+ */
+ public function getCurrentUserId();
}
diff --git a/lib/public/app/iappmanager.php b/lib/public/app/iappmanager.php
index f50a7f64174..69b8c335d67 100644
--- a/lib/public/app/iappmanager.php
+++ b/lib/public/app/iappmanager.php
@@ -78,4 +78,9 @@ interface IAppManager {
* @return string[]
*/
public function getInstalledApps();
+
+ /**
+ * Clear the cached list of apps when enabling/disabling an app
+ */
+ public function clearAppsCache();
}
diff --git a/lib/public/encryption/exceptions/genericencryptionexception.php b/lib/public/encryption/exceptions/genericencryptionexception.php
index b7addd3b0c1..c488d4df162 100644
--- a/lib/public/encryption/exceptions/genericencryptionexception.php
+++ b/lib/public/encryption/exceptions/genericencryptionexception.php
@@ -1,7 +1,8 @@
<?php
- /**
- * @author Clark Tomlinson <clark@owncloud.com>
- * @since 2/25/15, 9:30 AM
+/**
+ * @author Clark Tomlinson <fallen013@gmail.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
+ *
* @copyright Copyright (c) 2015, ownCloud, Inc.
* @license AGPL-3.0
*
@@ -24,7 +25,12 @@ namespace OCP\Encryption\Exceptions;
class GenericEncryptionException extends \Exception {
- public function __construct($message = "", $code = 0, \Exception $previous = null) {
+ /**
+ * @param string $message
+ * @param int $code
+ * @param \Exception $previous
+ */
+ public function __construct($message = '', $code = 0, \Exception $previous = null) {
if (empty($message)) {
$message = 'Unspecified encryption exception';
}
diff --git a/lib/public/encryption/iencryptionmodule.php b/lib/public/encryption/iencryptionmodule.php
index 7265fee1259..c1ce7d99d78 100644
--- a/lib/public/encryption/iencryptionmodule.php
+++ b/lib/public/encryption/iencryptionmodule.php
@@ -1,24 +1,22 @@
<?php
-
/**
- * ownCloud - public interface of ownCloud for encryption modules
- *
- * @copyright (C) 2015 ownCloud, Inc.
+ * @author Björn Schießle <schiessle@owncloud.com>
*
- * @author Bjoern Schiessle <schiessle@owncloud.com>
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or any later version.
+ * This 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 library is distributed in the hope that it will be useful,
+ * 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.
+ * 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/>
*
- * 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/>.
*/
namespace OCP\Encryption;
@@ -51,7 +49,7 @@ interface IEncryptionModule {
* written to the header, in case of a write operation
* or if no additional data is needed return a empty array
*/
- public function begin($path, $user, $header, $accessList);
+ public function begin($path, $user, array $header, array $accessList);
/**
* last chunk received. This is the place where you can perform some final
@@ -88,7 +86,7 @@ interface IEncryptionModule {
* @param array $accessList who has access to the file contains the key 'users' and 'public'
* @return boolean
*/
- public function update($path, $uid, $accessList);
+ public function update($path, $uid, array $accessList);
/**
* should the file be encrypted or not
diff --git a/lib/public/encryption/ifile.php b/lib/public/encryption/ifile.php
index 464f41509d2..cc1e8f426b2 100644
--- a/lib/public/encryption/ifile.php
+++ b/lib/public/encryption/ifile.php
@@ -1,24 +1,23 @@
<?php
-
/**
- * ownCloud
- *
- * @copyright (C) 2015 ownCloud, Inc.
+ * @author Björn Schießle <schiessle@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
*
- * @author Bjoern Schiessle <schiessle@owncloud.com>
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or any later version.
+ * This 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 library is distributed in the hope that it will be useful,
+ * 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.
+ * 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/>
*
- * 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/>.
*/
namespace OCP\Encryption;
diff --git a/lib/public/encryption/imanager.php b/lib/public/encryption/imanager.php
index 2691604ac37..3dcdbf5d03a 100644
--- a/lib/public/encryption/imanager.php
+++ b/lib/public/encryption/imanager.php
@@ -1,24 +1,22 @@
<?php
-
/**
- * ownCloud - manage encryption modules
- *
- * @copyright (C) 2015 ownCloud, Inc.
+ * @author Björn Schießle <schiessle@owncloud.com>
*
- * @author Bjoern Schiessle <schiessle@owncloud.com>
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or any later version.
+ * This 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 library is distributed in the hope that it will be useful,
+ * 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.
+ * 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/>
*
- * 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/>.
*/
namespace OCP\Encryption;
@@ -75,7 +73,7 @@ interface IManager {
* get default encryption module
*
* @return \OCP\Encryption\IEncryptionModule
- * @throws Exceptions\ModuleDoesNotExistsException
+ * @throws ModuleDoesNotExistsException
*/
public function getDefaultEncryptionModule();
diff --git a/lib/public/encryption/keys/istorage.php b/lib/public/encryption/keys/istorage.php
index 2d1672face5..c6933e7afab 100644
--- a/lib/public/encryption/keys/istorage.php
+++ b/lib/public/encryption/keys/istorage.php
@@ -1,24 +1,23 @@
<?php
-
/**
- * ownCloud
- *
- * @copyright (C) 2015 ownCloud, Inc.
+ * @author Björn Schießle <schiessle@owncloud.com>
+ * @author Thomas Müller <thomas.mueller@tmit.eu>
*
- * @author Bjoern Schiessle <schiessle@owncloud.com>
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
*
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or any later version.
+ * This 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 library is distributed in the hope that it will be useful,
+ * 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.
+ * 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/>
*
- * 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/>.
*/
namespace OCP\Encryption\Keys;
@@ -90,7 +89,7 @@ interface IStorage {
* @param string $uid ID if the user for whom we want to delete the key
* @param string $keyId id of the key
*
- * @return boolean
+ * @return boolean False when the key could not be deleted
*/
public function deleteUserKey($uid, $keyId);
@@ -100,7 +99,7 @@ interface IStorage {
* @param string $path path to file
* @param string $keyId id of the key
*
- * @return boolean
+ * @return boolean False when the key could not be deleted
*/
public function deleteFileKey($path, $keyId);
@@ -108,7 +107,7 @@ interface IStorage {
* delete all file keys for a given file
*
* @param string $path to the file
- * @return boolean
+ * @return boolean False when the keys could not be deleted
*/
public function deleteAllFileKeys($path);
@@ -118,7 +117,7 @@ interface IStorage {
*
* @param string $keyId id of the key
*
- * @return boolean
+ * @return boolean False when the key could not be deleted
*/
public function deleteSystemUserKey($keyId);
diff --git a/lib/public/iconfig.php b/lib/public/iconfig.php
index c63ba1a90a6..f28a114a2bb 100644
--- a/lib/public/iconfig.php
+++ b/lib/public/iconfig.php
@@ -133,7 +133,7 @@ interface IConfig {
* @param string $userId the userId of the user that we want to store the value under
* @param string $appName the appName that we stored the value under
* @param string $key the key under which the value is being stored
- * @param string $default the default value to be returned if the value isn't set
+ * @param mixed $default the default value to be returned if the value isn't set
* @return string
*/
public function getUserValue($userId, $appName, $key, $default = '');
diff --git a/lib/public/iservercontainer.php b/lib/public/iservercontainer.php
index 509e5894d47..dd0d2f417cf 100644
--- a/lib/public/iservercontainer.php
+++ b/lib/public/iservercontainer.php
@@ -2,6 +2,7 @@
/**
* @author Bart Visscher <bartv@thisnet.nl>
* @author Bernhard Posselt <dev@bernhard-posselt.com>
+ * @author Björn Schießle <schiessle@owncloud.com>
* @author Christopher Schäpers <kondou@ts.unde.re>
* @author Jörn Friedrich Dreyer <jfd@butonic.de>
* @author Lukas Reschke <lukas@owncloud.com>
@@ -59,7 +60,7 @@ interface IServerContainer {
* is returned from this method.
* In case the current execution was not initiated by a web request null is returned
*
- * @return \OCP\IRequest|null
+ * @return \OCP\IRequest
*/
function getRequest();
diff --git a/lib/public/share.php b/lib/public/share.php
index d27c1825d62..fcdcc6a6d2c 100644
--- a/lib/public/share.php
+++ b/lib/public/share.php
@@ -318,6 +318,20 @@ class Share extends \OC\Share\Constants {
}
/**
+ * Set expiration date for a share
+ * @param int $shareId
+ * @param string $password
+ * @return boolean
+ */
+ public static function setPassword($shareId, $password) {
+ $userSession = \OC::$server->getUserSession();
+ $connection = \OC::$server->getDatabaseConnection();
+ $config = \OC::$server->getConfig();
+ return \OC\Share\Share::setPassword($userSession, $connection, $config, $shareId, $password);
+ }
+
+
+ /**
* Get the backend class for the specified item type
* @param string $itemType
* @return Share_Backend
diff --git a/ocs/v1.php b/ocs/v1.php
index 5bba65d9a1a..398a128c64b 100644
--- a/ocs/v1.php
+++ b/ocs/v1.php
@@ -27,7 +27,7 @@
require_once '../lib/base.php';
-if (\OCP\Util::needUpgrade()) {
+if (\OCP\Util::needUpgrade() || \OC::$server->getSystemConfig()->getValue('maintenance', false)) {
// since the behavior of apps or remotes are unpredictable during
// an upgrade, return a 503 directly
OC_Response::setStatus(OC_Response::STATUS_SERVICE_UNAVAILABLE);
diff --git a/settings/application.php b/settings/application.php
index eb8f0f7a999..be127da31ac 100644
--- a/settings/application.php
+++ b/settings/application.php
@@ -71,7 +71,10 @@ class Application extends App {
$c->query('Request'),
$c->query('L10N'),
$c->query('Config'),
- $c->query('ICacheFactory')
+ $c->query('ICacheFactory'),
+ $c->query('INavigationManager'),
+ $c->query('IAppManager'),
+ $c->query('OcsClient')
);
});
$container->registerService('SecuritySettingsController', function(IContainer $c) {
@@ -192,6 +195,15 @@ class Application extends App {
$container->registerService('ClientService', function(IContainer $c) {
return $c->query('ServerContainer')->getHTTPClientService();
});
+ $container->registerService('INavigationManager', function(IContainer $c) {
+ return $c->query('ServerContainer')->getNavigationManager();
+ });
+ $container->registerService('IAppManager', function(IContainer $c) {
+ return $c->query('ServerContainer')->getAppManager();
+ });
+ $container->registerService('OcsClient', function(IContainer $c) {
+ return $c->query('ServerContainer')->getOcsClient();
+ });
$container->registerService('Util', function(IContainer $c) {
return new \OC_Util();
});
diff --git a/settings/apps.php b/settings/apps.php
deleted file mode 100644
index 7245b6610e0..00000000000
--- a/settings/apps.php
+++ /dev/null
@@ -1,42 +0,0 @@
-<?php
-/**
- * @author Bart Visscher <bartv@thisnet.nl>
- * @author Frank Karlitschek <frank@owncloud.org>
- * @author Jan-Christoph Borchardt <hey@jancborchardt.net>
- * @author Lukas Reschke <lukas@owncloud.com>
- * @author Morris Jobke <hey@morrisjobke.de>
- * @author Robin Appelman <icewind@owncloud.com>
- * @author Thomas Müller <thomas.mueller@tmit.eu>
- *
- * @copyright Copyright (c) 2015, ownCloud, Inc.
- * @license AGPL-3.0
- *
- * This code is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License, version 3,
- * as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License, version 3,
- * along with this program. If not, see <http://www.gnu.org/licenses/>
- *
- */
-
-OC_Util::checkAdminUser();
-\OC::$server->getSession()->close();
-
-// Load the files we need
-\OC_Util::addVendorScript('handlebars/handlebars');
-\OCP\Util::addScript("settings", "settings");
-\OCP\Util::addStyle("settings", "settings");
-\OC_Util::addVendorScript('select2/select2');
-\OC_Util::addVendorStyle('select2/select2');
-\OCP\Util::addScript("settings", "apps");
-\OC_App::setActiveNavigationEntry( "core_apps" );
-
-$tmpl = new OC_Template( "settings", "apps", "user" );
-$tmpl->printPage();
-
diff --git a/settings/controller/appsettingscontroller.php b/settings/controller/appsettingscontroller.php
index 9a85f6d3b97..f1b62bb1d38 100644
--- a/settings/controller/appsettingscontroller.php
+++ b/settings/controller/appsettingscontroller.php
@@ -27,8 +27,13 @@ namespace OC\Settings\Controller;
use OC\App\DependencyAnalyzer;
use OC\App\Platform;
use OC\OCSClient;
+use OCP\App\IAppManager;
use \OCP\AppFramework\Controller;
+use OCP\AppFramework\Http\ContentSecurityPolicy;
+use OCP\AppFramework\Http\DataResponse;
+use OCP\AppFramework\Http\TemplateResponse;
use OCP\ICacheFactory;
+use OCP\INavigationManager;
use OCP\IRequest;
use OCP\IL10N;
use OCP\IConfig;
@@ -44,6 +49,12 @@ class AppSettingsController extends Controller {
private $config;
/** @var \OCP\ICache */
private $cache;
+ /** @var INavigationManager */
+ private $navigationManager;
+ /** @var IAppManager */
+ private $appManager;
+ /** @var OCSClient */
+ private $ocsClient;
/**
* @param string $appName
@@ -51,16 +62,53 @@ class AppSettingsController extends Controller {
* @param IL10N $l10n
* @param IConfig $config
* @param ICacheFactory $cache
+ * @param INavigationManager $navigationManager
+ * @param IAppManager $appManager
+ * @param OCSClient $ocsClient
*/
public function __construct($appName,
IRequest $request,
IL10N $l10n,
IConfig $config,
- ICacheFactory $cache) {
+ ICacheFactory $cache,
+ INavigationManager $navigationManager,
+ IAppManager $appManager,
+ OCSClient $ocsClient) {
parent::__construct($appName, $request);
$this->l10n = $l10n;
$this->config = $config;
$this->cache = $cache->create($appName);
+ $this->navigationManager = $navigationManager;
+ $this->appManager = $appManager;
+ $this->ocsClient = $ocsClient;
+ }
+
+ /**
+ * Enables or disables the display of experimental apps
+ * @param bool $state
+ * @return DataResponse
+ */
+ public function changeExperimentalConfigState($state) {
+ $this->config->setSystemValue('appstore.experimental.enabled', $state);
+ $this->appManager->clearAppsCache();
+ return new DataResponse();
+ }
+
+ /**
+ * @NoCSRFRequired
+ * @return TemplateResponse
+ */
+ public function viewApps() {
+ $params = [];
+ $params['experimentalEnabled'] = $this->config->getSystemValue('appstore.experimental.enabled', false);
+ $this->navigationManager->setActiveEntry('core_apps');
+
+ $templateResponse = new TemplateResponse($this->appName, 'apps', $params, 'user');
+ $policy = new ContentSecurityPolicy();
+ $policy->addAllowedImageDomain('https://apps.owncloud.com');
+ $templateResponse->setContentSecurityPolicy($policy);
+
+ return $templateResponse;
}
/**
@@ -77,16 +125,15 @@ class AppSettingsController extends Controller {
['id' => 1, 'displayName' => (string)$this->l10n->t('Not enabled')],
];
- if(OCSClient::isAppStoreEnabled()) {
- $categories[] = ['id' => 2, 'displayName' => (string)$this->l10n->t('Recommended')];
+ if($this->ocsClient->isAppStoreEnabled()) {
// apps from external repo via OCS
- $ocs = OCSClient::getCategories();
+ $ocs = $this->ocsClient->getCategories();
if ($ocs) {
foreach($ocs as $k => $v) {
- $categories[] = array(
+ $categories[] = [
'id' => $k,
'displayName' => str_replace('ownCloud ', '', $v)
- );
+ ];
}
}
}
@@ -97,7 +144,8 @@ class AppSettingsController extends Controller {
}
/**
- * Get all available categories
+ * Get all available apps in a category
+ *
* @param int $category
* @return array
*/
@@ -134,16 +182,9 @@ class AppSettingsController extends Controller {
});
break;
default:
- if ($category === 2) {
- $apps = \OC_App::getAppstoreApps('approved');
- if ($apps) {
- $apps = array_filter($apps, function ($app) {
- return isset($app['internalclass']) && $app['internalclass'] === 'recommendedapp';
- });
- }
- } else {
- $apps = \OC_App::getAppstoreApps('approved', $category);
- }
+ $filter = $this->config->getSystemValue('appstore.experimental.enabled', false) ? 'all' : 'approved';
+
+ $apps = \OC_App::getAppstoreApps($filter, $category);
if (!$apps) {
$apps = array();
} else {
diff --git a/settings/css/settings.css b/settings/css/settings.css
index 3bc0a442f06..747e370617e 100644
--- a/settings/css/settings.css
+++ b/settings/css/settings.css
@@ -203,22 +203,52 @@ input.userFilter {width: 200px;}
background: #fbb;
}
-.recommendedapp {
- font-size: 11px;
- background-position: left center;
- padding-left: 18px;
- vertical-align: top;
+span.version {
+ margin-left: 1em;
+ margin-right: 1em;
+ color: #555;
}
-span.version { margin-left:1em; margin-right:1em; color:#555; }
#app-navigation .app-external,
-.app-version,
-.recommendedapp {
+.app-version {
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)";
filter: alpha(opacity=50);
opacity: .5;
}
+.app-level {
+ margin-top: 8px;
+}
+.app-level span {
+ color: #555;
+ background-color: transparent;
+ border: 1px solid #555;
+ border-radius: 3px;
+ padding: 3px 6px;
+}
+.app-level .official {
+ border-color: #37ce02;
+ background-position: left center;
+ background-position: 5px center;
+ padding-left: 25px;
+}
+.app-level .approved {
+ border-color: #e8c805;
+}
+.app-level .experimental {
+ background-color: #ce3702;
+ border-color: #ce3702;
+ color: #fff;
+}
+.apps-experimental {
+ color: #ce3702;
+}
+
+.app-score {
+ position: relative;
+ top: 4px;
+}
+
#apps-list {
position: relative;
height: 100%;
@@ -226,6 +256,9 @@ span.version { margin-left:1em; margin-right:1em; color:#555; }
.section {
position: relative;
}
+.section h2.app-name {
+ margin-bottom: 8px;
+}
.app-image {
float: left;
padding-right: 10px;
@@ -245,7 +278,7 @@ span.version { margin-left:1em; margin-right:1em; color:#555; }
.app-name,
.app-version,
.app-score,
-.recommendedapp {
+.app-level {
display: inline-block;
}
@@ -270,13 +303,17 @@ span.version { margin-left:1em; margin-right:1em; color:#555; }
white-space: pre-line;
}
-#app-category-2 {
+#app-category-1 {
border-bottom: 1px solid #e8e8e8;
}
+/* capitalize "Other" category */
+#app-category-925 {
+ text-transform: capitalize;
+}
.app-dependencies {
margin-top: 10px;
- color: #c33;
+ color: #ce3702;
}
.missing-dependencies {
@@ -311,8 +348,9 @@ table.grid td.date{
#security-warning li {
list-style: initial;
margin: 10px 0;
- color: #c33;
+ color: #ce3702;
}
+
#shareAPI p { padding-bottom: 0.8em; }
#shareAPI input#shareapiExpireAfterNDays {width: 25px;}
#shareAPI .indent {
@@ -321,6 +359,13 @@ table.grid td.date{
#shareAPI .double-indent {
padding-left: 56px;
}
+#fileSharingSettings h3 {
+ display: inline-block;
+}
+
+.icon-info {
+ padding: 11px 20px;
+}
.mail_settings p label:first-child {
display: inline-block;
@@ -362,12 +407,12 @@ table.grid td.date{
}
span.success {
- background: #37ce02;
- border-radius: 3px;
+ background: #37ce02;
+ border-radius: 3px;
}
span.error {
- background: #ce3702;
+ background: #ce3702;
}
diff --git a/settings/js/apps.js b/settings/js/apps.js
index 3db84e8acd5..1bd7ffdf790 100644
--- a/settings/js/apps.js
+++ b/settings/js/apps.js
@@ -9,6 +9,17 @@ Handlebars.registerHelper('score', function() {
}
return new Handlebars.SafeString('');
});
+Handlebars.registerHelper('level', function() {
+ if(typeof this.level !== 'undefined') {
+ if(this.level === 200) {
+ return new Handlebars.SafeString('<span class="official icon-checkmark">Official</span>');
+ } else if(this.level === 100) {
+ return new Handlebars.SafeString('<span class="approved">Approved</span>');
+ } else {
+ return new Handlebars.SafeString('<span class="experimental">Experimental</span>');
+ }
+ }
+});
OC.Settings = OC.Settings || {};
OC.Settings.Apps = OC.Settings.Apps || {
@@ -73,7 +84,6 @@ OC.Settings.Apps = OC.Settings.Apps || {
this._loadCategoryCall = $.ajax(OC.generateUrl('settings/apps/list?category={categoryId}', {
categoryId: categoryId
}), {
- data:{},
type:'GET',
success: function (apps) {
OC.Settings.Apps.State.apps = _.indexBy(apps.apps, 'id');
@@ -81,13 +91,27 @@ OC.Settings.Apps = OC.Settings.Apps || {
var template = Handlebars.compile(source);
if (apps.apps.length) {
+ apps.apps.sort(function(a,b) {
+ return b.level - a.level;
+ });
+
+ var firstExperimental = false;
_.each(apps.apps, function(app) {
- OC.Settings.Apps.renderApp(app, template, null);
+ if(app.level === 0 && firstExperimental === false) {
+ firstExperimental = true;
+ OC.Settings.Apps.renderApp(app, template, null, true);
+ } else {
+ OC.Settings.Apps.renderApp(app, template, null, false);
+ }
});
} else {
$('#apps-list').addClass('hidden');
$('#apps-list-empty').removeClass('hidden');
}
+
+ $('.app-level .official').tipsy({fallback: t('core', 'Official apps are developed by and within the ownCloud community. They offer functionality central to ownCloud and are ready for production use.')});
+ $('.app-level .approved').tipsy({fallback: t('core', 'Approved apps are developed by trusted developers and have passed a cursory security check. They are actively maintained in an open code repository and their maintainers deem them to be stable for casual to normal use.')});
+ $('.app-level .experimental').tipsy({fallback: t('core', 'This app is not checked for security issues and is new or known to be unstable. Install on your own risk.')});
},
complete: function() {
$('#apps-list').removeClass('icon-loading');
@@ -95,7 +119,7 @@ OC.Settings.Apps = OC.Settings.Apps || {
});
},
- renderApp: function(app, template, selector) {
+ renderApp: function(app, template, selector, firstExperimental) {
if (!template) {
var source = $("#app-template").html();
template = Handlebars.compile(source);
@@ -103,6 +127,7 @@ OC.Settings.Apps = OC.Settings.Apps || {
if (typeof app === 'string') {
app = OC.Settings.Apps.State.apps[app];
}
+ app.firstExperimental = firstExperimental;
var html = template(app);
if (selector) {
@@ -438,6 +463,16 @@ OC.Settings.Apps = OC.Settings.Apps || {
$select.change();
});
+ $(document).on('click', '#enable-experimental-apps', function () {
+ var state = $(this).prop('checked')
+ $.ajax(OC.generateUrl('settings/apps/experimental'), {
+ data: {state: state},
+ type: 'POST',
+ success:function () {
+ location.reload();
+ }
+ });
+ });
}
};
diff --git a/settings/routes.php b/settings/routes.php
index af9ac1d8eea..1bb14812145 100644
--- a/settings/routes.php
+++ b/settings/routes.php
@@ -33,25 +33,27 @@
namespace OC\Settings;
$application = new Application();
-$application->registerRoutes($this, array(
- 'resources' => array(
- 'groups' => array('url' => '/settings/users/groups'),
- 'users' => array('url' => '/settings/users/users')
- ),
- 'routes' => array(
- array('name' => 'MailSettings#setMailSettings', 'url' => '/settings/admin/mailsettings', 'verb' => 'POST'),
- array('name' => 'MailSettings#storeCredentials', 'url' => '/settings/admin/mailsettings/credentials', 'verb' => 'POST'),
- array('name' => 'MailSettings#sendTestMail', 'url' => '/settings/admin/mailtest', 'verb' => 'POST'),
- array('name' => 'AppSettings#listCategories', 'url' => '/settings/apps/categories', 'verb' => 'GET'),
- array('name' => 'AppSettings#listApps', 'url' => '/settings/apps/list', 'verb' => 'GET'),
- array('name' => 'SecuritySettings#trustedDomains', 'url' => '/settings/admin/security/trustedDomains', 'verb' => 'POST'),
- array('name' => 'Users#setMailAddress', 'url' => '/settings/users/{id}/mailAddress', 'verb' => 'PUT'),
- array('name' => 'LogSettings#setLogLevel', 'url' => '/settings/admin/log/level', 'verb' => 'POST'),
- array('name' => 'LogSettings#getEntries', 'url' => '/settings/admin/log/entries', 'verb' => 'GET'),
- array('name' => 'LogSettings#download', 'url' => '/settings/admin/log/download', 'verb' => 'GET'),
+$application->registerRoutes($this, [
+ 'resources' => [
+ 'groups' => ['url' => '/settings/users/groups'],
+ 'users' => ['url' => '/settings/users/users']
+ ],
+ 'routes' => [
+ ['name' => 'MailSettings#setMailSettings', 'url' => '/settings/admin/mailsettings', 'verb' => 'POST'],
+ ['name' => 'MailSettings#storeCredentials', 'url' => '/settings/admin/mailsettings/credentials', 'verb' => 'POST'],
+ ['name' => 'MailSettings#sendTestMail', 'url' => '/settings/admin/mailtest', 'verb' => 'POST'],
+ ['name' => 'AppSettings#listCategories', 'url' => '/settings/apps/categories', 'verb' => 'GET'],
+ ['name' => 'AppSettings#viewApps', 'url' => '/settings/apps', 'verb' => 'GET'],
+ ['name' => 'AppSettings#listApps', 'url' => '/settings/apps/list', 'verb' => 'GET'],
+ ['name' => 'AppSettings#changeExperimentalConfigState', 'url' => '/settings/apps/experimental', 'verb' => 'POST'],
+ ['name' => 'SecuritySettings#trustedDomains', 'url' => '/settings/admin/security/trustedDomains', 'verb' => 'POST'],
+ ['name' => 'Users#setMailAddress', 'url' => '/settings/users/{id}/mailAddress', 'verb' => 'PUT'],
+ ['name' => 'LogSettings#setLogLevel', 'url' => '/settings/admin/log/level', 'verb' => 'POST'],
+ ['name' => 'LogSettings#getEntries', 'url' => '/settings/admin/log/entries', 'verb' => 'GET'],
+ ['name' => 'LogSettings#download', 'url' => '/settings/admin/log/download', 'verb' => 'GET'],
['name' => 'CheckSetup#check', 'url' => '/settings/ajax/checksetup', 'verb' => 'GET'],
- )
-));
+ ]
+]);
/** @var $this \OCP\Route\IRouter */
@@ -62,8 +64,6 @@ $this->create('settings_personal', '/settings/personal')
->actionInclude('settings/personal.php');
$this->create('settings_users', '/settings/users')
->actionInclude('settings/users.php');
-$this->create('settings_apps', '/settings/apps')
- ->actionInclude('settings/apps.php');
$this->create('settings_admin', '/settings/admin')
->actionInclude('settings/admin.php');
// Settings ajax actions
diff --git a/settings/templates/admin.php b/settings/templates/admin.php
index 1b8ab0e3819..4bc497df764 100644
--- a/settings/templates/admin.php
+++ b/settings/templates/admin.php
@@ -269,6 +269,10 @@ if ($_['cronErrors']) {
endif; ?>
</p>
<?php endif; ?>
+ <a target="_blank" class="icon-info svg"
+ title="<?php p($l->t('Open documentation'));?>"
+ href="<?php p(link_to_docs('admin-background-jobs')); ?>"></a>
+
<p>
<input type="radio" name="mode" value="ajax"
id="backgroundjobs_ajax" <?php if ($_['backgroundjobs_mode'] === "ajax") {
diff --git a/settings/templates/apps.php b/settings/templates/apps.php
index a2fe5d9b63a..31de7fa2318 100644
--- a/settings/templates/apps.php
+++ b/settings/templates/apps.php
@@ -1,3 +1,27 @@
+<?php
+style('settings', 'settings');
+vendor_style(
+ 'core',
+ [
+ 'select2/select2',
+ ]
+);
+vendor_script(
+ 'core',
+ [
+ 'handlebars/handlebars',
+ 'select2/select2'
+ ]
+);
+script(
+ 'settings',
+ [
+ 'settings',
+ 'apps',
+ ]
+);
+/** @var array $_ */
+?>
<script id="categories-template" type="text/x-handlebars-template">
{{#each this}}
<li id="app-category-{{id}}" data-category-id="{{id}}" tabindex="0">
@@ -16,6 +40,18 @@
</script>
<script id="app-template" type="text/x-handlebars">
+ {{#if firstExperimental}}
+ <div class="section apps-experimental">
+ <h2><?php p($l->t('Experimental applications ahead')) ?></h2>
+ <p>
+ <?php p($l->t('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.')) ?>
+ </p>
+ </div>
+ {{/if}}
+
<div class="section" id="app-{{id}}">
{{#if preview}}
<div class="app-image{{#if previewAsIcon}} app-image-icon{{/if}} hidden">
@@ -23,17 +59,19 @@
{{/if}}
<h2 class="app-name"><a href="{{detailpage}}" target="_blank">{{name}}</a></h2>
<div class="app-version"> {{version}}</div>
+ {{#if profilepage}}<a href="{{profilepage}}" target="_blank" rel="noreferrer">{{/if}}
<div class="app-author"><?php p($l->t('by')); ?> {{author}}
{{#if licence}}
({{licence}}-<?php p($l->t('licensed')); ?>)
{{/if}}
</div>
+ {{#if profilepage}}</a>{{/if}}
+ <div class="app-level">
+ {{{level}}}
+ </div>
{{#if score}}
<div class="app-score">{{{score}}}</div>
{{/if}}
- {{#if internalclass}}
- <div class="{{internalclass}} icon-checkmark">{{internallabel}}</div>
- {{/if}}
<div class="app-detailpage"></div>
<div class="app-description-container hidden">
@@ -95,6 +133,24 @@
<ul id="apps-categories">
</ul>
+ <div id="app-settings">
+ <div id="app-settings-header">
+ <button class="settings-button" data-apps-slide-toggle="#app-settings-content"></button>
+ </div>
+
+ <div id="app-settings-content" class="apps-experimental">
+ <input type="checkbox" id="enable-experimental-apps" <?php if($_['experimentalEnabled']) { print_unescaped('checked="checked"'); }?>>
+ <label for="enable-experimental-apps"><?php p($l->t('Enable experimental apps')) ?></label>
+ <p>
+ <small>
+ <?php p($l->t('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.')) ?>
+ </small>
+ </p>
+ </div>
+ </div>
</div>
<div id="app-content">
<div id="apps-list" class="icon-loading"></div>
diff --git a/settings/templates/help.php b/settings/templates/help.php
index f559329c6bb..79584aba84d 100644
--- a/settings/templates/help.php
+++ b/settings/templates/help.php
@@ -4,25 +4,25 @@
<li>
<a class="<?php p($_['style1']); ?>"
href="<?php print_unescaped($_['url1']); ?>">
- <?php p($l->t( 'User Documentation' )); ?>
+ <?php p($l->t('User documentation')); ?>
</a>
</li>
<li>
<a class="<?php p($_['style2']); ?>"
href="<?php print_unescaped($_['url2']); ?>">
- <?php p($l->t( 'Administrator Documentation' )); ?>
+ <?php p($l->t('Administrator documentation')); ?>
</a>
</li>
<?php } ?>
<li>
<a href="https://owncloud.org/support" target="_blank" rel="noreferrer">
- <?php p($l->t( 'Online Documentation' )); ?> ↗
+ <?php p($l->t('Online documentation')); ?> ↗
</a>
</li>
<li>
<a href="https://forum.owncloud.org" target="_blank" rel="noreferrer">
- <?php p($l->t( 'Forum' )); ?> ↗
+ <?php p($l->t('Forum')); ?> ↗
</a>
</li>
@@ -30,14 +30,14 @@
<li>
<a href="https://github.com/owncloud/core/blob/master/CONTRIBUTING.md"
target="_blank" rel="noreferrer">
- <?php p($l->t( 'Bugtracker' )); ?> ↗
+ <?php p($l->t('Issue tracker')); ?> ↗
</a>
</li>
<?php } ?>
<li>
- <a href="https://owncloud.com" target="_blank" rel="noreferrer">
- <?php p($l->t( 'Commercial Support' )); ?> ↗
+ <a href="https://owncloud.com/subscriptions/" target="_blank" rel="noreferrer">
+ <?php p($l->t('Commercial support')); ?> ↗
</a>
</li>
</div>
diff --git a/settings/tests/js/appsSpec.js b/settings/tests/js/appsSpec.js
index 39329c19246..311fade2c66 100644
--- a/settings/tests/js/appsSpec.js
+++ b/settings/tests/js/appsSpec.js
@@ -98,4 +98,58 @@ describe('OC.Settings.Apps tests', function() {
expect(results[0]).toEqual('somestuff');
});
});
+
+ describe('loading categories', function() {
+ var suite = this;
+
+ beforeEach( function(){
+ suite.server = sinon.fakeServer.create();
+ });
+
+ afterEach( function(){
+ suite.server.restore();
+ });
+
+ function getResultsFromDom() {
+ var results = [];
+ $('#apps-list .section:not(.hidden)').each(function() {
+ results.push($(this).attr('data-id'));
+ });
+ return results;
+ }
+
+ it('sorts all applications using the level', function() {
+ Apps.loadCategory('TestId');
+
+ suite.server.requests[0].respond(
+ 200,
+ {
+ 'Content-Type': 'application/json'
+ },
+ JSON.stringify({
+ apps: [
+ {
+ id: 'foo',
+ level: 0
+ },
+ {
+ id: 'alpha',
+ level: 300
+ },
+ {
+ id: 'delta',
+ level: 200
+ }
+ ]
+ })
+ );
+
+ var results = getResultsFromDom();
+ expect(results.length).toEqual(3);
+ expect(results[0]).toEqual('alpha');
+ expect(results[1]).toEqual('delta');
+ expect(results[2]).toEqual('foo');
+ });
+ });
+
});
diff --git a/status.php b/status.php
index 1628e824e00..6e7bcea5266 100644
--- a/status.php
+++ b/status.php
@@ -41,6 +41,7 @@ try {
if (OC::$CLI) {
print_r($values);
} else {
+ header('Content-Type: application/json');
echo json_encode($values);
}
diff --git a/tests/lib/OCSClientTest.php b/tests/lib/OCSClientTest.php
new file mode 100644
index 00000000000..fa3f1fe7848
--- /dev/null
+++ b/tests/lib/OCSClientTest.php
@@ -0,0 +1,934 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+use OC\OCSClient;
+use OCP\Http\Client\IClientService;
+use OCP\IConfig;
+use OCP\ILogger;
+
+/**
+ * Class OCSClientTest
+ */
+class OCSClientTest extends \Test\TestCase {
+ /** @var OCSClient */
+ private $ocsClient;
+ /** @var IConfig */
+ private $config;
+ /** @var IClientService */
+ private $clientService;
+ /** @var ILogger */
+ private $logger;
+
+ public function setUp() {
+ parent::setUp();
+
+ $this->config = $this->getMockBuilder('\OCP\IConfig')
+ ->disableOriginalConstructor()->getMock();
+ $this->clientService = $this->getMock('\OCP\Http\Client\IClientService');
+ $this->logger = $this->getMock('\OCP\ILogger');
+
+ $this->ocsClient = new OCSClient(
+ $this->clientService,
+ $this->config,
+ $this->logger
+ );
+ }
+
+ public function testIsAppStoreEnabledSuccess() {
+ $this->config
+ ->expects($this->once())
+ ->method('getSystemValue')
+ ->with('appstoreenabled', true)
+ ->will($this->returnValue(true));
+ $this->assertTrue($this->ocsClient->isAppStoreEnabled());
+ }
+
+ public function testIsAppStoreEnabledFail() {
+ $this->config
+ ->expects($this->once())
+ ->method('getSystemValue')
+ ->with('appstoreenabled', true)
+ ->will($this->returnValue(false));
+ $this->assertFalse($this->ocsClient->isAppStoreEnabled());
+ }
+
+ public function testGetAppStoreUrl() {
+ $this->config
+ ->expects($this->once())
+ ->method('getSystemValue')
+ ->with('appstoreurl', 'https://api.owncloud.com/v1')
+ ->will($this->returnValue('https://api.owncloud.com/v1'));
+ $this->assertSame('https://api.owncloud.com/v1', Test_Helper::invokePrivate($this->ocsClient, 'getAppStoreUrl'));
+ }
+
+ public function testGetCategoriesDisabledAppStore() {
+ $this->config
+ ->expects($this->once())
+ ->method('getSystemValue')
+ ->with('appstoreenabled', true)
+ ->will($this->returnValue(false));
+ $this->assertNull($this->ocsClient->getCategories());
+ }
+
+ public function testGetCategoriesExceptionClient() {
+ $this->config
+ ->expects($this->at(0))
+ ->method('getSystemValue')
+ ->with('appstoreenabled', true)
+ ->will($this->returnValue(true));
+ $this->config
+ ->expects($this->at(1))
+ ->method('getSystemValue')
+ ->with('appstoreurl', 'https://api.owncloud.com/v1')
+ ->will($this->returnValue('https://api.owncloud.com/v1'));
+
+ $client = $this->getMock('\OCP\Http\Client\IClient');
+ $client
+ ->expects($this->once())
+ ->method('get')
+ ->with(
+ 'https://api.owncloud.com/v1/content/categories',
+ [
+ 'timeout' => 5,
+ ]
+ )
+ ->will($this->throwException(new \Exception('TheErrorMessage')));
+
+ $this->clientService
+ ->expects($this->once())
+ ->method('newClient')
+ ->will($this->returnValue($client));
+
+ $this->logger
+ ->expects($this->once())
+ ->method('error')
+ ->with(
+ 'Could not get categories: TheErrorMessage',
+ [
+ 'app' => 'core',
+ ]
+ );
+
+ $this->assertNull($this->ocsClient->getCategories());
+ }
+
+ public function testGetCategoriesParseError() {
+ $this->config
+ ->expects($this->at(0))
+ ->method('getSystemValue')
+ ->with('appstoreenabled', true)
+ ->will($this->returnValue(true));
+ $this->config
+ ->expects($this->at(1))
+ ->method('getSystemValue')
+ ->with('appstoreurl', 'https://api.owncloud.com/v1')
+ ->will($this->returnValue('https://api.owncloud.com/v1'));
+
+ $response = $this->getMock('\OCP\Http\Client\IResponse');
+ $response
+ ->expects($this->once())
+ ->method('getBody')
+ ->will($this->returnValue('MyInvalidXml'));
+
+ $client = $this->getMock('\OCP\Http\Client\IClient');
+ $client
+ ->expects($this->once())
+ ->method('get')
+ ->with(
+ 'https://api.owncloud.com/v1/content/categories',
+ [
+ 'timeout' => 5,
+ ]
+ )
+ ->will($this->returnValue($response));
+
+ $this->clientService
+ ->expects($this->once())
+ ->method('newClient')
+ ->will($this->returnValue($client));
+
+ $this->logger
+ ->expects($this->once())
+ ->method('error')
+ ->with(
+ 'Could not get categories, content was no valid XML',
+ [
+ 'app' => 'core',
+ ]
+ );
+
+ $this->assertNull($this->ocsClient->getCategories());
+ }
+
+ public function testGetCategoriesSuccessful() {
+ $this->config
+ ->expects($this->at(0))
+ ->method('getSystemValue')
+ ->with('appstoreenabled', true)
+ ->will($this->returnValue(true));
+ $this->config
+ ->expects($this->at(1))
+ ->method('getSystemValue')
+ ->with('appstoreurl', 'https://api.owncloud.com/v1')
+ ->will($this->returnValue('https://api.owncloud.com/v1'));
+
+ $response = $this->getMock('\OCP\Http\Client\IResponse');
+ $response
+ ->expects($this->once())
+ ->method('getBody')
+ ->will($this->returnValue('<?xml version="1.0"?>
+ <ocs>
+ <meta>
+ <status>ok</status>
+ <statuscode>100</statuscode>
+ <message></message>
+ <totalitems>6</totalitems>
+ </meta>
+ <data>
+ <category>
+ <id>920</id>
+ <name>ownCloud Multimedia</name>
+ </category>
+ <category>
+ <id>921</id>
+ <name>ownCloud PIM</name>
+ </category>
+ <category>
+ <id>922</id>
+ <name>ownCloud Productivity</name>
+ </category>
+ <category>
+ <id>923</id>
+ <name>ownCloud Game</name>
+ </category>
+ <category>
+ <id>924</id>
+ <name>ownCloud Tool</name>
+ </category>
+ <category>
+ <id>925</id>
+ <name>ownCloud other</name>
+ </category>
+ </data>
+ </ocs>
+ '));
+
+ $client = $this->getMock('\OCP\Http\Client\IClient');
+ $client
+ ->expects($this->once())
+ ->method('get')
+ ->with(
+ 'https://api.owncloud.com/v1/content/categories',
+ [
+ 'timeout' => 5,
+ ]
+ )
+ ->will($this->returnValue($response));
+
+ $this->clientService
+ ->expects($this->once())
+ ->method('newClient')
+ ->will($this->returnValue($client));
+
+ $expected = [
+ 920 => 'ownCloud Multimedia',
+ 921 => 'ownCloud PIM',
+ 922 => 'ownCloud Productivity',
+ 923 => 'ownCloud Game',
+ 924 => 'ownCloud Tool',
+ 925 => 'ownCloud other',
+ ];
+ $this->assertSame($expected, $this->ocsClient->getCategories());
+ }
+
+ public function testGetApplicationsDisabledAppStore() {
+ $this->config
+ ->expects($this->once())
+ ->method('getSystemValue')
+ ->with('appstoreenabled', true)
+ ->will($this->returnValue(false));
+ $this->assertSame([], $this->ocsClient->getApplications([], 1, 'approved'));
+ }
+
+ public function testGetApplicationsExceptionClient() {
+ $this->config
+ ->expects($this->at(0))
+ ->method('getSystemValue')
+ ->with('appstoreenabled', true)
+ ->will($this->returnValue(true));
+ $this->config
+ ->expects($this->at(1))
+ ->method('getSystemValue')
+ ->with('appstoreurl', 'https://api.owncloud.com/v1')
+ ->will($this->returnValue('https://api.owncloud.com/v1'));
+
+ $client = $this->getMock('\OCP\Http\Client\IClient');
+ $client
+ ->expects($this->once())
+ ->method('get')
+ ->with(
+ 'https://api.owncloud.com/v1/content/data',
+ [
+ 'timeout' => 5,
+ 'query' => [
+ 'version' => implode('x', \OC_Util::getVersion()),
+ 'filter' => 'approved',
+ 'categories' => '815x1337',
+ 'sortmode' => 'new',
+ 'page' => 1,
+ 'pagesize' => 100,
+ 'approved' => 'approved',
+ ],
+ ]
+ )
+ ->will($this->throwException(new \Exception('TheErrorMessage')));
+
+ $this->clientService
+ ->expects($this->once())
+ ->method('newClient')
+ ->will($this->returnValue($client));
+
+ $this->logger
+ ->expects($this->once())
+ ->method('error')
+ ->with(
+ 'Could not get applications: TheErrorMessage',
+ [
+ 'app' => 'core',
+ ]
+ );
+
+ $this->assertSame([], $this->ocsClient->getApplications([815, 1337], 1, 'approved'));
+ }
+
+ public function testGetApplicationsParseError() {
+ $this->config
+ ->expects($this->at(0))
+ ->method('getSystemValue')
+ ->with('appstoreenabled', true)
+ ->will($this->returnValue(true));
+ $this->config
+ ->expects($this->at(1))
+ ->method('getSystemValue')
+ ->with('appstoreurl', 'https://api.owncloud.com/v1')
+ ->will($this->returnValue('https://api.owncloud.com/v1'));
+
+ $response = $this->getMock('\OCP\Http\Client\IResponse');
+ $response
+ ->expects($this->once())
+ ->method('getBody')
+ ->will($this->returnValue('MyInvalidXml'));
+
+ $client = $this->getMock('\OCP\Http\Client\IClient');
+ $client
+ ->expects($this->once())
+ ->method('get')
+ ->with(
+ 'https://api.owncloud.com/v1/content/data',
+ [
+ 'timeout' => 5,
+ 'query' => [
+ 'version' => implode('x', \OC_Util::getVersion()),
+ 'filter' => 'approved',
+ 'categories' => '815x1337',
+ 'sortmode' => 'new',
+ 'page' => 1,
+ 'pagesize' => 100,
+ 'approved' => 'approved',
+ ],
+ ]
+ )
+ ->will($this->returnValue($response));
+
+ $this->clientService
+ ->expects($this->once())
+ ->method('newClient')
+ ->will($this->returnValue($client));
+
+ $this->logger
+ ->expects($this->once())
+ ->method('error')
+ ->with(
+ 'Could not get applications, content was no valid XML',
+ [
+ 'app' => 'core',
+ ]
+ );
+
+ $this->assertSame([], $this->ocsClient->getApplications([815, 1337], 1, 'approved'));
+ }
+
+ public function testGetApplicationsSuccessful() {
+ $this->config
+ ->expects($this->at(0))
+ ->method('getSystemValue')
+ ->with('appstoreenabled', true)
+ ->will($this->returnValue(true));
+ $this->config
+ ->expects($this->at(1))
+ ->method('getSystemValue')
+ ->with('appstoreurl', 'https://api.owncloud.com/v1')
+ ->will($this->returnValue('https://api.owncloud.com/v1'));
+
+ $response = $this->getMock('\OCP\Http\Client\IResponse');
+ $response
+ ->expects($this->once())
+ ->method('getBody')
+ ->will($this->returnValue('<?xml version="1.0"?>
+ <ocs>
+ <meta>
+ <status>ok</status>
+ <statuscode>100</statuscode>
+ <message></message>
+ <totalitems>2</totalitems>
+ <itemsperpage>100</itemsperpage>
+ </meta>
+ <data>
+ <content details="summary">
+ <id>168707</id>
+ <name>Calendar 8.0</name>
+ <version>0.6.4</version>
+ <label>recommended</label>
+ <changed>2015-02-09T15:23:56+01:00</changed>
+ <created>2015-01-26T04:35:19+01:00</created>
+ <typeid>921</typeid>
+ <typename>ownCloud PIM</typename>
+ <language></language>
+ <personid>owncloud</personid>
+ <profilepage>http://opendesktop.org/usermanager/search.php?username=owncloud</profilepage>
+ <downloads>5393</downloads>
+ <score>60</score>
+ <description>Calendar App for ownCloud</description>
+ <comments>7</comments>
+ <fans>10</fans>
+ <licensetype>16</licensetype>
+ <approved>0</approved>
+ <category>1</category>
+ <license>AGPL</license>
+ <preview1></preview1>
+ <detailpage>https://apps.owncloud.com/content/show.php?content=168707</detailpage>
+ <downloadtype1></downloadtype1>
+ <downloadway1>0</downloadway1>
+ <downloadprice1>0</downloadprice1>
+ <downloadlink1>http://apps.owncloud.com/content/download.php?content=168707&amp;id=1</downloadlink1>
+ <downloadgpgsignature1></downloadgpgsignature1>
+ <downloadgpgfingerprint1></downloadgpgfingerprint1>
+ <downloadpackagename1></downloadpackagename1>
+ <downloadrepository1></downloadrepository1>
+ <downloadname1></downloadname1>
+ <downloadsize1>885</downloadsize1>
+ </content>
+ <content details="summary">
+ <id>168708</id>
+ <name>Contacts 8.0</name>
+ <version>0.3.0.18</version>
+ <label>recommended</label>
+ <changed>2015-02-09T15:18:58+01:00</changed>
+ <created>2015-01-26T04:45:17+01:00</created>
+ <typeid>921</typeid>
+ <typename>ownCloud PIM</typename>
+ <language></language>
+ <personid>owncloud</personid>
+ <profilepage>http://opendesktop.org/usermanager/search.php?username=owncloud</profilepage>
+ <downloads>4237</downloads>
+ <score>58</score>
+ <description></description>
+ <comments>3</comments>
+ <fans>6</fans>
+ <licensetype>16</licensetype>
+ <approved>200</approved>
+ <category>1</category>
+ <license>AGPL</license>
+ <preview1></preview1>
+ <detailpage>https://apps.owncloud.com/content/show.php?content=168708</detailpage>
+ <downloadtype1></downloadtype1>
+ <downloadway1>0</downloadway1>
+ <downloadprice1>0</downloadprice1>
+ <downloadlink1>http://apps.owncloud.com/content/download.php?content=168708&amp;id=1</downloadlink1>
+ <downloadgpgsignature1></downloadgpgsignature1>
+ <downloadgpgfingerprint1></downloadgpgfingerprint1>
+ <downloadpackagename1></downloadpackagename1>
+ <downloadrepository1></downloadrepository1>
+ <downloadname1></downloadname1>
+ <downloadsize1>1409</downloadsize1>
+ </content>
+ </data>
+ </ocs> '));
+
+ $client = $this->getMock('\OCP\Http\Client\IClient');
+ $client
+ ->expects($this->once())
+ ->method('get')
+ ->with(
+ 'https://api.owncloud.com/v1/content/data',
+ [
+ 'timeout' => 5,
+ 'query' => [
+ 'version' => implode('x', \OC_Util::getVersion()),
+ 'filter' => 'approved',
+ 'categories' => '815x1337',
+ 'sortmode' => 'new',
+ 'page' => 1,
+ 'pagesize' => 100,
+ 'approved' => 'approved',
+ ],
+ ]
+ )
+ ->will($this->returnValue($response));
+
+ $this->clientService
+ ->expects($this->once())
+ ->method('newClient')
+ ->will($this->returnValue($client));
+
+ $expected = [
+ [
+ 'id' => '168707',
+ 'name' => 'Calendar 8.0',
+ 'label' => 'recommended',
+ 'version' => '0.6.4',
+ 'type' => '921',
+ 'typename' => 'ownCloud PIM',
+ 'personid' => 'owncloud',
+ 'license' => 'AGPL',
+ 'detailpage' => 'https://apps.owncloud.com/content/show.php?content=168707',
+ 'preview' => '',
+ 'preview-full' => '',
+ 'changed' => 1423491836,
+ 'description' => 'Calendar App for ownCloud',
+ 'score' => '60',
+ 'downloads' => 5393,
+ 'level' => 0,
+ 'profilepage' => 'http://opendesktop.org/usermanager/search.php?username=owncloud',
+ ],
+ [
+ 'id' => '168708',
+ 'name' => 'Contacts 8.0',
+ 'label' => 'recommended',
+ 'version' => '0.3.0.18',
+ 'type' => '921',
+ 'typename' => 'ownCloud PIM',
+ 'personid' => 'owncloud',
+ 'license' => 'AGPL',
+ 'detailpage' => 'https://apps.owncloud.com/content/show.php?content=168708',
+ 'preview' => '',
+ 'preview-full' => '',
+ 'changed' => 1423491538,
+ 'description' => '',
+ 'score' => '58',
+ 'downloads' => 4237,
+ 'level' => 200,
+ 'profilepage' => 'http://opendesktop.org/usermanager/search.php?username=owncloud',
+ ],
+ ];
+ $this->assertEquals($expected, $this->ocsClient->getApplications([815, 1337], 1, 'approved'));
+ }
+
+ public function tesGetApplicationDisabledAppStore() {
+ $this->config
+ ->expects($this->once())
+ ->method('getSystemValue')
+ ->with('appstoreenabled', true)
+ ->will($this->returnValue(false));
+ $this->assertNull($this->ocsClient->getApplication('MyId'));
+ }
+
+ public function testGetApplicationExceptionClient() {
+ $this->config
+ ->expects($this->at(0))
+ ->method('getSystemValue')
+ ->with('appstoreenabled', true)
+ ->will($this->returnValue(true));
+ $this->config
+ ->expects($this->at(1))
+ ->method('getSystemValue')
+ ->with('appstoreurl', 'https://api.owncloud.com/v1')
+ ->will($this->returnValue('https://api.owncloud.com/v1'));
+
+ $client = $this->getMock('\OCP\Http\Client\IClient');
+ $client
+ ->expects($this->once())
+ ->method('get')
+ ->with(
+ 'https://api.owncloud.com/v1/content/data/MyId',
+ [
+ 'timeout' => 5,
+ ]
+ )
+ ->will($this->throwException(new \Exception('TheErrorMessage')));
+
+ $this->clientService
+ ->expects($this->once())
+ ->method('newClient')
+ ->will($this->returnValue($client));
+
+ $this->logger
+ ->expects($this->once())
+ ->method('error')
+ ->with(
+ 'Could not get application: TheErrorMessage',
+ [
+ 'app' => 'core',
+ ]
+ );
+
+ $this->assertNull($this->ocsClient->getApplication('MyId'));
+ }
+
+ public function testGetApplicationParseError() {
+ $this->config
+ ->expects($this->at(0))
+ ->method('getSystemValue')
+ ->with('appstoreenabled', true)
+ ->will($this->returnValue(true));
+ $this->config
+ ->expects($this->at(1))
+ ->method('getSystemValue')
+ ->with('appstoreurl', 'https://api.owncloud.com/v1')
+ ->will($this->returnValue('https://api.owncloud.com/v1'));
+
+ $response = $this->getMock('\OCP\Http\Client\IResponse');
+ $response
+ ->expects($this->once())
+ ->method('getBody')
+ ->will($this->returnValue('MyInvalidXml'));
+
+ $client = $this->getMock('\OCP\Http\Client\IClient');
+ $client
+ ->expects($this->once())
+ ->method('get')
+ ->with(
+ 'https://api.owncloud.com/v1/content/data/MyId',
+ [
+ 'timeout' => 5,
+ ]
+ )
+ ->will($this->returnValue($response));
+
+ $this->clientService
+ ->expects($this->once())
+ ->method('newClient')
+ ->will($this->returnValue($client));
+
+ $this->logger
+ ->expects($this->once())
+ ->method('error')
+ ->with(
+ 'Could not get application, content was no valid XML',
+ [
+ 'app' => 'core',
+ ]
+ );
+
+ $this->assertNull($this->ocsClient->getApplication('MyId'));
+ }
+
+ public function testGetApplicationSuccessful() {
+ $this->config
+ ->expects($this->at(0))
+ ->method('getSystemValue')
+ ->with('appstoreenabled', true)
+ ->will($this->returnValue(true));
+ $this->config
+ ->expects($this->at(1))
+ ->method('getSystemValue')
+ ->with('appstoreurl', 'https://api.owncloud.com/v1')
+ ->will($this->returnValue('https://api.owncloud.com/v1'));
+
+ $response = $this->getMock('\OCP\Http\Client\IResponse');
+ $response
+ ->expects($this->once())
+ ->method('getBody')
+ ->will($this->returnValue('<?xml version="1.0"?>
+ <ocs>
+ <meta>
+ <status>ok</status>
+ <statuscode>100</statuscode>
+ <message></message>
+ </meta>
+ <data>
+ <content details="full">
+ <id>166053</id>
+ <name>Versioning</name>
+ <version>0.0.1</version>
+ <label>recommended</label>
+ <typeid>925</typeid>
+ <typename>ownCloud other</typename>
+ <language></language>
+ <personid>owncloud</personid>
+ <profilepage>http://opendesktop.org/usermanager/search.php?username=owncloud</profilepage>
+ <created>2014-07-07T16:34:40+02:00</created>
+ <changed>2014-07-07T16:34:40+02:00</changed>
+ <downloads>140</downloads>
+ <score>50</score>
+ <description>Placeholder for future updates</description>
+ <summary></summary>
+ <feedbackurl></feedbackurl>
+ <changelog></changelog>
+ <homepage></homepage>
+ <homepagetype></homepagetype>
+ <homepage2></homepage2>
+ <homepagetype2></homepagetype2>
+ <homepage3></homepage3>
+ <homepagetype3></homepagetype3>
+ <homepage4></homepage4>
+ <homepagetype4></homepagetype4>
+ <homepage5></homepage5>
+ <homepagetype5></homepagetype5>
+ <homepage6></homepage6>
+ <homepagetype6></homepagetype6>
+ <homepage7></homepage7>
+ <homepagetype7></homepagetype7>
+ <homepage8></homepage8>
+ <homepagetype8></homepagetype8>
+ <homepage9></homepage9>
+ <homepagetype9></homepagetype9>
+ <homepage10></homepage10>
+ <homepagetype10></homepagetype10>
+ <licensetype>16</licensetype>
+ <license>AGPL</license>
+ <donationpage></donationpage>
+ <comments>0</comments>
+ <commentspage>http://apps.owncloud.com/content/show.php?content=166053</commentspage>
+ <fans>0</fans>
+ <fanspage>http://apps.owncloud.com/content/show.php?action=fan&amp;content=166053</fanspage>
+ <knowledgebaseentries>0</knowledgebaseentries>
+ <knowledgebasepage>http://apps.owncloud.com/content/show.php?action=knowledgebase&amp;content=166053</knowledgebasepage>
+ <depend>ownCloud 7</depend>
+ <preview1></preview1>
+ <preview2></preview2>
+ <preview3></preview3>
+ <previewpic1></previewpic1>
+ <previewpic2></previewpic2>
+ <previewpic3></previewpic3>
+ <picsmall1></picsmall1>
+ <picsmall2></picsmall2>
+ <picsmall3></picsmall3>
+ <detailpage>https://apps.owncloud.com/content/show.php?content=166053</detailpage>
+ <downloadtype1></downloadtype1>
+ <downloadprice1>0</downloadprice1>
+ <downloadlink1>http://apps.owncloud.com/content/download.php?content=166053&amp;id=1</downloadlink1>
+ <downloadname1></downloadname1>
+ <downloadgpgfingerprint1></downloadgpgfingerprint1>
+ <downloadgpgsignature1></downloadgpgsignature1>
+ <downloadpackagename1></downloadpackagename1>
+ <downloadrepository1></downloadrepository1>
+ <downloadsize1>1</downloadsize1>
+ </content>
+ </data>
+ </ocs>
+ '));
+
+ $client = $this->getMock('\OCP\Http\Client\IClient');
+ $client
+ ->expects($this->once())
+ ->method('get')
+ ->with(
+ 'https://api.owncloud.com/v1/content/data/MyId',
+ [
+ 'timeout' => 5,
+ ]
+ )
+ ->will($this->returnValue($response));
+
+ $this->clientService
+ ->expects($this->once())
+ ->method('newClient')
+ ->will($this->returnValue($client));
+
+ $expected = [
+ 'id' => 166053,
+ 'name' => 'Versioning',
+ 'version' => '0.0.1',
+ 'type' => '925',
+ 'label' => 'recommended',
+ 'typename' => 'ownCloud other',
+ 'personid' => 'owncloud',
+ 'profilepage' => 'http://opendesktop.org/usermanager/search.php?username=owncloud',
+ 'detailpage' => 'https://apps.owncloud.com/content/show.php?content=166053',
+ 'preview1' => '',
+ 'preview2' => '',
+ 'preview3' => '',
+ 'changed' => 1404743680,
+ 'description' => 'Placeholder for future updates',
+ 'score' => 50,
+ ];
+ $this->assertSame($expected, $this->ocsClient->getApplication('MyId'));
+ }
+
+ public function testGetApplicationDownloadDisabledAppStore() {
+ $this->config
+ ->expects($this->once())
+ ->method('getSystemValue')
+ ->with('appstoreenabled', true)
+ ->will($this->returnValue(false));
+ $this->assertNull($this->ocsClient->getApplicationDownload('MyId'));
+ }
+
+ public function testGetApplicationDownloadExceptionClient() {
+ $this->config
+ ->expects($this->at(0))
+ ->method('getSystemValue')
+ ->with('appstoreenabled', true)
+ ->will($this->returnValue(true));
+ $this->config
+ ->expects($this->at(1))
+ ->method('getSystemValue')
+ ->with('appstoreurl', 'https://api.owncloud.com/v1')
+ ->will($this->returnValue('https://api.owncloud.com/v1'));
+
+ $client = $this->getMock('\OCP\Http\Client\IClient');
+ $client
+ ->expects($this->once())
+ ->method('get')
+ ->with(
+ 'https://api.owncloud.com/v1/content/download/MyId/1',
+ [
+ 'timeout' => 5,
+ ]
+ )
+ ->will($this->throwException(new \Exception('TheErrorMessage')));
+
+ $this->clientService
+ ->expects($this->once())
+ ->method('newClient')
+ ->will($this->returnValue($client));
+
+ $this->logger
+ ->expects($this->once())
+ ->method('error')
+ ->with(
+ 'Could not get application download URL: TheErrorMessage',
+ [
+ 'app' => 'core',
+ ]
+ );
+
+ $this->assertNull($this->ocsClient->getApplicationDownload('MyId'));
+ }
+
+ public function testGetApplicationDownloadParseError() {
+ $this->config
+ ->expects($this->at(0))
+ ->method('getSystemValue')
+ ->with('appstoreenabled', true)
+ ->will($this->returnValue(true));
+ $this->config
+ ->expects($this->at(1))
+ ->method('getSystemValue')
+ ->with('appstoreurl', 'https://api.owncloud.com/v1')
+ ->will($this->returnValue('https://api.owncloud.com/v1'));
+
+ $response = $this->getMock('\OCP\Http\Client\IResponse');
+ $response
+ ->expects($this->once())
+ ->method('getBody')
+ ->will($this->returnValue('MyInvalidXml'));
+
+ $client = $this->getMock('\OCP\Http\Client\IClient');
+ $client
+ ->expects($this->once())
+ ->method('get')
+ ->with(
+ 'https://api.owncloud.com/v1/content/download/MyId/1',
+ [
+ 'timeout' => 5,
+ ]
+ )
+ ->will($this->returnValue($response));
+
+ $this->clientService
+ ->expects($this->once())
+ ->method('newClient')
+ ->will($this->returnValue($client));
+
+ $this->logger
+ ->expects($this->once())
+ ->method('error')
+ ->with(
+ 'Could not get application download URL, content was no valid XML',
+ [
+ 'app' => 'core',
+ ]
+ );
+
+ $this->assertNull($this->ocsClient->getApplicationDownload('MyId'));
+ }
+
+ public function testGetApplicationDownloadUrlSuccessful() {
+ $this->config
+ ->expects($this->at(0))
+ ->method('getSystemValue')
+ ->with('appstoreenabled', true)
+ ->will($this->returnValue(true));
+ $this->config
+ ->expects($this->at(1))
+ ->method('getSystemValue')
+ ->with('appstoreurl', 'https://api.owncloud.com/v1')
+ ->will($this->returnValue('https://api.owncloud.com/v1'));
+
+ $response = $this->getMock('\OCP\Http\Client\IResponse');
+ $response
+ ->expects($this->once())
+ ->method('getBody')
+ ->will($this->returnValue('<?xml version="1.0"?>
+ <ocs>
+ <meta>
+ <status>ok</status>
+ <statuscode>100</statuscode>
+ <message></message>
+ </meta>
+ <data>
+ <content details="download">
+ <downloadlink>https://apps.owncloud.com/CONTENT/content-files/166052-files_trashbin.zip</downloadlink>
+ <mimetype>application/zip</mimetype>
+ <gpgfingerprint></gpgfingerprint>
+ <gpgsignature></gpgsignature>
+ <packagename></packagename>
+ <repository></repository>
+ </content>
+ </data>
+ </ocs>
+ '));
+
+ $client = $this->getMock('\OCP\Http\Client\IClient');
+ $client
+ ->expects($this->once())
+ ->method('get')
+ ->with(
+ 'https://api.owncloud.com/v1/content/download/MyId/1',
+ [
+ 'timeout' => 5,
+ ]
+ )
+ ->will($this->returnValue($response));
+
+ $this->clientService
+ ->expects($this->once())
+ ->method('newClient')
+ ->will($this->returnValue($client));
+
+ $expected = [
+ 'downloadlink' => 'https://apps.owncloud.com/CONTENT/content-files/166052-files_trashbin.zip',
+ ];
+ $this->assertSame($expected, $this->ocsClient->getApplicationDownload('MyId'));
+ }
+}
diff --git a/tests/lib/activitymanager.php b/tests/lib/activitymanager.php
index d227c05d827..d3263fa2ede 100644
--- a/tests/lib/activitymanager.php
+++ b/tests/lib/activitymanager.php
@@ -13,10 +13,34 @@ class Test_ActivityManager extends \Test\TestCase {
/** @var \OC\ActivityManager */
private $activityManager;
+ /** @var \PHPUnit_Framework_MockObject_MockObject */
+ protected $request;
+
+ /** @var \PHPUnit_Framework_MockObject_MockObject */
+ protected $session;
+
+ /** @var \PHPUnit_Framework_MockObject_MockObject */
+ protected $config;
+
protected function setUp() {
parent::setUp();
- $this->activityManager = new \OC\ActivityManager();
+ $this->request = $this->getMockBuilder('OCP\IRequest')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->session = $this->getMockBuilder('OCP\IUserSession')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->config = $this->getMockBuilder('OCP\IConfig')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $this->activityManager = new \OC\ActivityManager(
+ $this->request,
+ $this->session,
+ $this->config
+ );
+
$this->activityManager->registerExtension(function() {
return new NoOpExtension();
});
@@ -111,6 +135,83 @@ class Test_ActivityManager extends \Test\TestCase {
$result = $this->activityManager->getQueryForFilter('InvalidFilter');
$this->assertEquals(array(null, null), $result);
}
+
+ public function getUserFromTokenThrowInvalidTokenData() {
+ return [
+ [null, []],
+ ['', []],
+ ['12345678901234567890123456789', []],
+ ['1234567890123456789012345678901', []],
+ ['123456789012345678901234567890', []],
+ ['123456789012345678901234567890', ['user1', 'user2']],
+ ];
+ }
+
+ /**
+ * @expectedException \UnexpectedValueException
+ * @dataProvider getUserFromTokenThrowInvalidTokenData
+ *
+ * @param string $token
+ * @param array $users
+ */
+ public function testGetUserFromTokenThrowInvalidToken($token, $users) {
+ $this->mockRSSToken($token, $token, $users);
+ \Test_Helper::invokePrivate($this->activityManager, 'getUserFromToken');
+ }
+
+ public function getUserFromTokenData() {
+ return [
+ [null, '123456789012345678901234567890', 'user1'],
+ ['user2', null, 'user2'],
+ ['user2', '123456789012345678901234567890', 'user2'],
+ ];
+ }
+
+ /**
+ * @dataProvider getUserFromTokenData
+ *
+ * @param string $userLoggedIn
+ * @param string $token
+ * @param string $expected
+ */
+ public function testGetUserFromToken($userLoggedIn, $token, $expected) {
+ if ($userLoggedIn !== null) {
+ $this->mockUserSession($userLoggedIn);
+ }
+ $this->mockRSSToken($token, '123456789012345678901234567890', ['user1']);
+
+ $this->assertEquals($expected, $this->activityManager->getCurrentUserId());
+ }
+
+ protected function mockRSSToken($requestToken, $userToken, $users) {
+ if ($requestToken !== null) {
+ $this->request->expects($this->any())
+ ->method('getParam')
+ ->with('token', '')
+ ->willReturn($requestToken);
+ }
+
+ $this->config->expects($this->any())
+ ->method('getUsersForUserValue')
+ ->with('activity', 'rsstoken', $userToken)
+ ->willReturn($users);
+ }
+
+ protected function mockUserSession($user) {
+ $mockUser = $this->getMockBuilder('\OCP\IUser')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $mockUser->expects($this->any())
+ ->method('getUID')
+ ->willReturn($user);
+
+ $this->session->expects($this->any())
+ ->method('isLoggedIn')
+ ->willReturn(true);
+ $this->session->expects($this->any())
+ ->method('getUser')
+ ->willReturn($mockUser);
+ }
}
class SimpleExtension implements \OCP\Activity\IExtension {
diff --git a/tests/lib/encryption/keys/storage.php b/tests/lib/encryption/keys/storage.php
index 8ab46987f8c..bcf1c0f7624 100644
--- a/tests/lib/encryption/keys/storage.php
+++ b/tests/lib/encryption/keys/storage.php
@@ -198,6 +198,10 @@ class StorageTest extends TestCase {
public function testDeleteUserKey() {
$this->view->expects($this->once())
+ ->method('file_exists')
+ ->with($this->equalTo('/user1/files_encryption/encModule/user1.publicKey'))
+ ->willReturn(true);
+ $this->view->expects($this->once())
->method('unlink')
->with($this->equalTo('/user1/files_encryption/encModule/user1.publicKey'))
->willReturn(true);
@@ -209,6 +213,10 @@ class StorageTest extends TestCase {
public function testDeleteSystemUserKey() {
$this->view->expects($this->once())
+ ->method('file_exists')
+ ->with($this->equalTo('/files_encryption/encModule/shareKey_56884'))
+ ->willReturn(true);
+ $this->view->expects($this->once())
->method('unlink')
->with($this->equalTo('/files_encryption/encModule/shareKey_56884'))
->willReturn(true);
@@ -229,6 +237,10 @@ class StorageTest extends TestCase {
->method('isSystemWideMountPoint')
->willReturn(true);
$this->view->expects($this->once())
+ ->method('file_exists')
+ ->with($this->equalTo('/files_encryption/keys/files/foo.txt/encModule/fileKey'))
+ ->willReturn(true);
+ $this->view->expects($this->once())
->method('unlink')
->with($this->equalTo('/files_encryption/keys/files/foo.txt/encModule/fileKey'))
->willReturn(true);
@@ -249,6 +261,10 @@ class StorageTest extends TestCase {
->method('isSystemWideMountPoint')
->willReturn(false);
$this->view->expects($this->once())
+ ->method('file_exists')
+ ->with($this->equalTo('/user1/files_encryption/keys/files/foo.txt/encModule/fileKey'))
+ ->willReturn(true);
+ $this->view->expects($this->once())
->method('unlink')
->with($this->equalTo('/user1/files_encryption/keys/files/foo.txt/encModule/fileKey'))
->willReturn(true);
diff --git a/tests/lib/files/cache/updater.php b/tests/lib/files/cache/updater.php
index 970af2e68df..7c3ebd5a6f9 100644
--- a/tests/lib/files/cache/updater.php
+++ b/tests/lib/files/cache/updater.php
@@ -33,16 +33,12 @@ class Updater extends \Test\TestCase {
*/
protected $updater;
- /** @var \OC\Files\Storage\Storage */
- private $originalStorage;
-
protected function setUp() {
parent::setUp();
- $this->originalStorage = Filesystem::getStorage('/');
+ $this->loginAsUser();
$this->storage = new Temporary(array());
- Filesystem::clearMounts();
Filesystem::mount($this->storage, array(), '/');
$this->view = new View('');
$this->updater = new \OC\Files\Cache\Updater($this->view);
@@ -51,8 +47,8 @@ class Updater extends \Test\TestCase {
protected function tearDown() {
Filesystem::clearMounts();
- Filesystem::mount($this->originalStorage, array(), '/');
+ $this->logout();
parent::tearDown();
}
diff --git a/tests/lib/files/cache/updaterlegacy.php b/tests/lib/files/cache/updaterlegacy.php
index 6bdacbe34fe..f4d52e9a390 100644
--- a/tests/lib/files/cache/updaterlegacy.php
+++ b/tests/lib/files/cache/updaterlegacy.php
@@ -27,9 +27,6 @@ class UpdaterLegacy extends \Test\TestCase {
*/
private $cache;
- /** @var \OC\Files\Storage\Storage */
- private $originalStorage;
-
private static $user;
protected function setUp() {
@@ -48,14 +45,12 @@ class UpdaterLegacy extends \Test\TestCase {
$this->scanner->scan('');
$this->cache = $this->storage->getCache();
- $this->originalStorage = Filesystem::getStorage('/');
- Filesystem::tearDown();
if (!self::$user) {
self::$user = $this->getUniqueID();
}
\OC_User::createUser(self::$user, 'password');
- \OC_User::setUserId(self::$user);
+ $this->loginAsUser(self::$user);
Filesystem::init(self::$user, '/' . self::$user . '/files');
@@ -71,9 +66,8 @@ class UpdaterLegacy extends \Test\TestCase {
}
$result = \OC_User::deleteUser(self::$user);
$this->assertTrue($result);
- Filesystem::tearDown();
- Filesystem::mount($this->originalStorage, array(), '/');
+ $this->logout();
parent::tearDown();
}
diff --git a/tests/lib/files/cache/watcher.php b/tests/lib/files/cache/watcher.php
index ee605c64e01..e6947e36a17 100644
--- a/tests/lib/files/cache/watcher.php
+++ b/tests/lib/files/cache/watcher.php
@@ -15,14 +15,10 @@ class Watcher extends \Test\TestCase {
*/
private $storages = array();
- /** @var \OC\Files\Storage\Storage */
- private $originalStorage;
-
protected function setUp() {
parent::setUp();
- $this->originalStorage = \OC\Files\Filesystem::getStorage('/');
- \OC\Files\Filesystem::clearMounts();
+ $this->loginAsUser();
}
protected function tearDown() {
@@ -32,9 +28,7 @@ class Watcher extends \Test\TestCase {
$cache->clear();
}
- \OC\Files\Filesystem::clearMounts();
- \OC\Files\Filesystem::mount($this->originalStorage, array(), '/');
-
+ $this->logout();
parent::tearDown();
}
diff --git a/tests/lib/files/etagtest.php b/tests/lib/files/etagtest.php
index eec24d9f4c6..055927652bc 100644
--- a/tests/lib/files/etagtest.php
+++ b/tests/lib/files/etagtest.php
@@ -16,16 +16,11 @@ class EtagTest extends \Test\TestCase {
private $tmpDir;
- private $uid;
-
/**
* @var \OC_User_Dummy $userBackend
*/
private $userBackend;
- /** @var \OC\Files\Storage\Storage */
- private $originalStorage;
-
protected function setUp() {
parent::setUp();
@@ -37,21 +32,15 @@ class EtagTest extends \Test\TestCase {
$this->datadir = \OC_Config::getValue('datadirectory');
$this->tmpDir = \OC_Helper::tmpFolder();
\OC_Config::setValue('datadirectory', $this->tmpDir);
- $this->uid = \OC_User::getUser();
- \OC_User::setUserId(null);
$this->userBackend = new \OC_User_Dummy();
\OC_User::useBackend($this->userBackend);
- $this->originalStorage = \OC\Files\Filesystem::getStorage('/');
- \OC_Util::tearDownFS();
}
protected function tearDown() {
\OC_Config::setValue('datadirectory', $this->datadir);
- \OC_User::setUserId($this->uid);
- \OC_Util::setupFS($this->uid);
- \OC\Files\Filesystem::mount($this->originalStorage, array(), '/');
+ $this->logout();
parent::tearDown();
}
@@ -59,9 +48,7 @@ class EtagTest extends \Test\TestCase {
$user1 = $this->getUniqueID('user_');
$this->userBackend->createUser($user1, '');
- \OC_Util::tearDownFS();
- \OC_User::setUserId($user1);
- \OC_Util::setupFS($user1);
+ $this->loginAsUser($user1);
Filesystem::mkdir('/folder');
Filesystem::mkdir('/folder/subfolder');
Filesystem::file_put_contents('/foo.txt', 'asd');
diff --git a/tests/lib/files/filesystem.php b/tests/lib/files/filesystem.php
index 7bf59315d77..98e96e0cc78 100644
--- a/tests/lib/files/filesystem.php
+++ b/tests/lib/files/filesystem.php
@@ -28,9 +28,6 @@ class Filesystem extends \Test\TestCase {
*/
private $tmpDirs = array();
- /** @var \OC\Files\Storage\Storage */
- private $originalStorage;
-
/**
* @return array
*/
@@ -42,20 +39,15 @@ class Filesystem extends \Test\TestCase {
protected function setUp() {
parent::setUp();
-
- $this->originalStorage = \OC\Files\Filesystem::getStorage('/');
- \OC_User::setUserId('');
- \OC\Files\Filesystem::clearMounts();
+ $this->loginAsUser();
}
protected function tearDown() {
foreach ($this->tmpDirs as $dir) {
\OC_Helper::rmdirr($dir);
}
- \OC\Files\Filesystem::clearMounts();
- \OC\Files\Filesystem::mount($this->originalStorage, array(), '/');
- \OC_User::setUserId('');
+ $this->logout();
parent::tearDown();
}
diff --git a/tests/lib/files/node/integration.php b/tests/lib/files/node/integration.php
index 456a4a0e287..4e362607240 100644
--- a/tests/lib/files/node/integration.php
+++ b/tests/lib/files/node/integration.php
@@ -20,9 +20,6 @@ class IntegrationTests extends \Test\TestCase {
*/
private $root;
- /** @var \OC\Files\Storage\Storage */
- private $originalStorage;
-
/**
* @var \OC\Files\Storage\Storage[]
*/
@@ -36,9 +33,6 @@ class IntegrationTests extends \Test\TestCase {
protected function setUp() {
parent::setUp();
- $this->originalStorage = \OC\Files\Filesystem::getStorage('/');
- \OC\Files\Filesystem::init('', '');
- \OC\Files\Filesystem::clearMounts();
$manager = \OC\Files\Filesystem::getMountManager();
\OC_Hook::clear('OC_Filesystem');
@@ -49,7 +43,8 @@ class IntegrationTests extends \Test\TestCase {
\OC_Hook::connect('OC_Filesystem', 'post_touch', '\OC\Files\Cache\Updater', 'touchHook');
$user = new User($this->getUniqueID('user'), new \OC_User_Dummy);
- \OC_User::setUserId($user->getUID());
+ $this->loginAsUser($user->getUID());
+
$this->view = new View();
$this->root = new Root($manager, $this->view, $user);
$storage = new Temporary(array());
@@ -64,9 +59,8 @@ class IntegrationTests extends \Test\TestCase {
foreach ($this->storages as $storage) {
$storage->getCache()->clear();
}
- \OC\Files\Filesystem::clearMounts();
- \OC\Files\Filesystem::mount($this->originalStorage, array(), '/');
+ $this->logout();
parent::tearDown();
}
diff --git a/tests/lib/files/storage/wrapper/encryption.php b/tests/lib/files/storage/wrapper/encryption.php
index bf4464f0eb9..4f7a9e851c1 100644
--- a/tests/lib/files/storage/wrapper/encryption.php
+++ b/tests/lib/files/storage/wrapper/encryption.php
@@ -44,7 +44,9 @@ class Encryption extends \Test\Files\Storage\Storage {
$file = $this->getMockBuilder('\OC\Encryption\File')
->disableOriginalConstructor()
+ ->setMethods(['getAccessList'])
->getMock();
+ $file->expects($this->any())->method('getAccessList')->willReturn([]);
$logger = $this->getMock('\OC\Log');
diff --git a/tests/lib/files/stream/encryption.php b/tests/lib/files/stream/encryption.php
index 84156337ad7..53727a2213d 100644
--- a/tests/lib/files/stream/encryption.php
+++ b/tests/lib/files/stream/encryption.php
@@ -29,7 +29,9 @@ class Encryption extends \Test\TestCase {
->getMock();
$file = $this->getMockBuilder('\OC\Encryption\File')
->disableOriginalConstructor()
+ ->setMethods(['getAccessList'])
->getMock();
+ $file->expects($this->any())->method('getAccessList')->willReturn([]);
$util = $this->getMock('\OC\Encryption\Util', ['getUidAndFilename'], [new View(), new \OC\User\Manager(), $config]);
$util->expects($this->any())
->method('getUidAndFilename')
diff --git a/tests/lib/files/utils/scanner.php b/tests/lib/files/utils/scanner.php
index 65ddfe47514..dfc683c1bcf 100644
--- a/tests/lib/files/utils/scanner.php
+++ b/tests/lib/files/utils/scanner.php
@@ -39,18 +39,14 @@ class TestScanner extends \OC\Files\Utils\Scanner {
}
class Scanner extends \Test\TestCase {
- /** @var \OC\Files\Storage\Storage */
- private $originalStorage;
-
protected function setUp() {
parent::setUp();
- $this->originalStorage = \OC\Files\Filesystem::getStorage('/');
+ $this->loginAsUser();
}
protected function tearDown() {
- \OC\Files\Filesystem::mount($this->originalStorage, array(), '/');
-
+ $this->logout();
parent::tearDown();
}
diff --git a/tests/lib/files/view.php b/tests/lib/files/view.php
index cd9f2d4afd1..2ea9e8de78f 100644
--- a/tests/lib/files/view.php
+++ b/tests/lib/files/view.php
@@ -27,9 +27,6 @@ class View extends \Test\TestCase {
/** @var \OC\Files\Storage\Storage */
private $tempStorage;
- /** @var \OC\Files\Storage\Storage */
- private $originalStorage;
-
protected function setUp() {
parent::setUp();
@@ -39,9 +36,10 @@ class View extends \Test\TestCase {
//login
\OC_User::createUser('test', 'test');
$this->user = \OC_User::getUser();
- \OC_User::setUserId('test');
- $this->originalStorage = \OC\Files\Filesystem::getStorage('/');
+ $this->loginAsUser('test');
+ // clear mounts but somehow keep the root storage
+ // that was initialized above...
\OC\Files\Filesystem::clearMounts();
$this->tempStorage = null;
@@ -59,9 +57,7 @@ class View extends \Test\TestCase {
system('rm -rf ' . escapeshellarg($this->tempStorage->getDataDir()));
}
- \OC\Files\Filesystem::clearMounts();
- \OC\Files\Filesystem::mount($this->originalStorage, array(), '/');
-
+ $this->logout();
parent::tearDown();
}
diff --git a/tests/lib/preview.php b/tests/lib/preview.php
index 2a6761403f4..ea9de9b777e 100644
--- a/tests/lib/preview.php
+++ b/tests/lib/preview.php
@@ -26,6 +26,8 @@ class Preview extends TestCase {
protected function setUp() {
parent::setUp();
+ // FIXME: use proper tearDown with $this->loginAsUser() and $this->logout()
+ // (would currently break the tests for some reason)
$this->originalStorage = \OC\Files\Filesystem::getStorage('/');
// create a new user with his own filesystem view
@@ -48,6 +50,77 @@ class Preview extends TestCase {
parent::tearDown();
}
+ public function testIsMaxSizeWorking() {
+ // Max size from config
+ $maxX = 1024;
+ $maxY = 1024;
+
+ \OC::$server->getConfig()->setSystemValue('preview_max_x', $maxX);
+ \OC::$server->getConfig()->setSystemValue('preview_max_y', $maxY);
+
+ // Sample is 1680x1050 JPEG
+ $sampleFile = '/' . $this->user . '/files/testimage.jpg';
+ $this->rootView->file_put_contents($sampleFile, file_get_contents(\OC::$SERVERROOT.'/tests/data/testimage.jpg'));
+ $fileInfo = $this->rootView->getFileInfo($sampleFile);
+ $fileId = $fileInfo['fileid'];
+
+ $largeX = 1920;
+ $largeY = 1080;
+ $preview = new \OC\Preview($this->user, 'files/', 'testimage.jpg', $largeX, $largeY);
+
+ $this->assertEquals($preview->isFileValid(), true);
+
+ // There should be no cached copy
+ $isCached = $preview->isCached($fileId);
+
+ $this->assertNotEquals(\OC\Preview::THUMBNAILS_FOLDER . '/' . $fileId . '/' . $maxX . '-' . $maxY . '-max.png', $isCached);
+ $this->assertNotEquals(\OC\Preview::THUMBNAILS_FOLDER . '/' . $fileId . '/' . $maxX . '-' . $maxY . '.png', $isCached);
+ $this->assertNotEquals(\OC\Preview::THUMBNAILS_FOLDER . '/' . $fileId . '/' . $largeX . '-' . $largeY . '.png', $isCached);
+
+ // The returned preview should be of max size
+ $image = $preview->getPreview();
+
+ $this->assertEquals($image->width(), $maxX);
+ $this->assertEquals($image->height(), $maxY);
+
+ // The max thumbnail should be created
+ $maxThumbCacheFile = '/' . $this->user . '/' . \OC\Preview::THUMBNAILS_FOLDER . '/' . $fileId . '/' . $maxX . '-' . $maxY . '-max.png';
+
+ $this->assertEquals($this->rootView->file_exists($maxThumbCacheFile), true);
+
+ // A preview of the asked size should not have been created
+ $thumbCacheFile = \OC\Preview::THUMBNAILS_FOLDER . '/' . $fileId . '/' . $largeX . '-' . $largeY . '.png';
+
+ $this->assertEquals($this->rootView->file_exists($thumbCacheFile), false);
+
+ // 2nd request should indicate that we have a cached copy of max dimension
+ $isCached = $preview->isCached($fileId);
+ $this->assertEquals(\OC\Preview::THUMBNAILS_FOLDER . '/' . $fileId . '/' . $maxX . '-' . $maxY . '.png', $isCached);
+
+ // Smaller previews should be based on the cached max preview
+ $smallX = 50;
+ $smallY = 50;
+ $preview = new \OC\Preview($this->user, 'files/', 'testimage.jpg', $smallX, $smallY);
+ $isCached = $preview->isCached($fileId);
+
+ $this->assertEquals(\OC\Preview::THUMBNAILS_FOLDER . '/' . $fileId . '/' . $maxX . '-' . $maxY . '.png', $isCached);
+
+ // A small preview should be created
+ $image = $preview->getPreview();
+ $this->assertEquals($image->width(), $smallX);
+ $this->assertEquals($image->height(), $smallY);
+
+ // The cache should contain the small preview
+ $thumbCacheFile = '/' . $this->user . '/' . \OC\Preview::THUMBNAILS_FOLDER . '/' . $fileId . '/' . $smallX . '-' . $smallY . '.png';
+
+ $this->assertEquals($this->rootView->file_exists($thumbCacheFile), true);
+
+ // 2nd request should indicate that we have a cached copy of the exact dimension
+ $isCached = $preview->isCached($fileId);
+
+ $this->assertEquals(\OC\Preview::THUMBNAILS_FOLDER . '/' . $fileId . '/' . $smallX . '-' . $smallY . '.png', $isCached);
+ }
+
public function testIsPreviewDeleted() {
$sampleFile = '/'.$this->user.'/files/test.txt';
@@ -96,25 +169,6 @@ class Preview extends TestCase {
$this->assertEquals($this->rootView->is_dir($thumbCacheFolder), false);
}
- public function testIsMaxSizeWorking() {
-
- $maxX = 250;
- $maxY = 250;
-
- \OC_Config::setValue('preview_max_x', $maxX);
- \OC_Config::setValue('preview_max_y', $maxY);
-
- $sampleFile = '/'.$this->user.'/files/test.txt';
-
- $this->rootView->file_put_contents($sampleFile, 'dummy file data');
-
- $preview = new \OC\Preview($this->user, 'files/', 'test.txt', 1000, 1000);
- $image = $preview->getPreview();
-
- $this->assertEquals($image->width(), $maxX);
- $this->assertEquals($image->height(), $maxY);
- }
-
public function txtBlacklist() {
$txt = 'random text file';
diff --git a/tests/lib/share/share.php b/tests/lib/share/share.php
index f35a0fa8e43..124ad450e2e 100644
--- a/tests/lib/share/share.php
+++ b/tests/lib/share/share.php
@@ -545,6 +545,13 @@ class Test_Share extends \Test\TestCase {
// Valid share
$this->shareUserOneTestFileWithGroupOne();
+ // check if only the group share was created and not a single db-entry for each user
+ $statement = \OCP\DB::prepare('select `id` from `*PREFIX*share`');
+ $query = $statement->execute();
+ $result = $query->fetchAll();
+ $this->assertSame(1, count($result));
+
+
// Attempt to share again
OC_User::setUserId($this->user1);
$message = 'Sharing test.txt failed, because this item is already shared with '.$this->group1;
@@ -1128,6 +1135,240 @@ class Test_Share extends \Test\TestCase {
\OC_Appconfig::deleteKey('core', 'shareapi_expire_after_n_days');
\OC_Appconfig::deleteKey('core', 'shareapi_enforce_expire_date');
}
+
+ /**
+ * Cannot set password is there is no user
+ *
+ * @expectedException Exception
+ * @expectedExceptionMessage User not logged in
+ */
+ public function testSetPasswordNoUser() {
+ $userSession = $this->getMockBuilder('\OCP\IUserSession')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $connection = $this->getMockBuilder('\OC\DB\Connection')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ $config = $this->getMockBuilder('\OCP\IConfig')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+ \OC\Share\Share::setPassword($userSession, $connection, $config, 1, 'pass');
+ }
+
+ /**
+ * Test setting a password when everything is fine
+ */
+ public function testSetPassword() {
+ $user = $this->getMockBuilder('\OCP\IUser')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $user->method('getUID')->willReturn('user');
+
+ $userSession = $this->getMockBuilder('\OCP\IUserSession')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $userSession->method('getUser')->willReturn($user);
+
+
+ $ex = $this->getMockBuilder('\Doctrine\DBAL\Query\Expression\ExpressionBuilder')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $qb = $this->getMockBuilder('\Doctrine\DBAL\Query\QueryBuilder')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $qb->method('update')->will($this->returnSelf());
+ $qb->method('set')->will($this->returnSelf());
+ $qb->method('where')->will($this->returnSelf());
+ $qb->method('andWhere')->will($this->returnSelf());
+ $qb->method('select')->will($this->returnSelf());
+ $qb->method('from')->will($this->returnSelf());
+ $qb->method('setParameter')->will($this->returnSelf());
+ $qb->method('expr')->willReturn($ex);
+
+ $ret = $this->getMockBuilder('\Doctrine\DBAL\Driver\ResultStatement')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $ret->method('fetch')->willReturn(['uid_owner' => 'user']);
+ $qb->method('execute')->willReturn($ret);
+
+
+ $connection = $this->getMockBuilder('\OC\DB\Connection')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $connection->method('createQueryBuilder')->willReturn($qb);
+
+ $config = $this->getMockBuilder('\OCP\IConfig')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+
+ $res = \OC\Share\Share::setPassword($userSession, $connection, $config, 1, 'pass');
+
+ $this->assertTrue($res);
+ }
+
+ /**
+ * @expectedException Exception
+ * @expectedExceptionMessage Cannot remove password
+ *
+ * Test removing a password when password is enforced
+ */
+ public function testSetPasswordRemove() {
+ $user = $this->getMockBuilder('\OCP\IUser')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $user->method('getUID')->willReturn('user');
+
+ $userSession = $this->getMockBuilder('\OCP\IUserSession')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $userSession->method('getUser')->willReturn($user);
+
+
+ $ex = $this->getMockBuilder('\Doctrine\DBAL\Query\Expression\ExpressionBuilder')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $qb = $this->getMockBuilder('\Doctrine\DBAL\Query\QueryBuilder')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $qb->method('update')->will($this->returnSelf());
+ $qb->method('select')->will($this->returnSelf());
+ $qb->method('from')->will($this->returnSelf());
+ $qb->method('set')->will($this->returnSelf());
+ $qb->method('where')->will($this->returnSelf());
+ $qb->method('andWhere')->will($this->returnSelf());
+ $qb->method('setParameter')->will($this->returnSelf());
+ $qb->method('expr')->willReturn($ex);
+
+ $ret = $this->getMockBuilder('\Doctrine\DBAL\Driver\ResultStatement')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $ret->method('fetch')->willReturn(['uid_owner' => 'user']);
+ $qb->method('execute')->willReturn($ret);
+
+
+ $connection = $this->getMockBuilder('\OC\DB\Connection')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $connection->method('createQueryBuilder')->willReturn($qb);
+
+ $config = $this->getMockBuilder('\OCP\IConfig')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $config->method('getAppValue')->willReturn('yes');
+
+ \OC\Share\Share::setPassword($userSession, $connection, $config, 1, '');
+ }
+
+ /**
+ * @expectedException Exception
+ * @expectedExceptionMessage Share not found
+ *
+ * Test modification of invaid share
+ */
+ public function testSetPasswordInvalidShare() {
+ $user = $this->getMockBuilder('\OCP\IUser')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $user->method('getUID')->willReturn('user');
+
+ $userSession = $this->getMockBuilder('\OCP\IUserSession')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $userSession->method('getUser')->willReturn($user);
+
+
+ $ex = $this->getMockBuilder('\Doctrine\DBAL\Query\Expression\ExpressionBuilder')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $qb = $this->getMockBuilder('\Doctrine\DBAL\Query\QueryBuilder')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $qb->method('update')->will($this->returnSelf());
+ $qb->method('set')->will($this->returnSelf());
+ $qb->method('where')->will($this->returnSelf());
+ $qb->method('andWhere')->will($this->returnSelf());
+ $qb->method('select')->will($this->returnSelf());
+ $qb->method('from')->will($this->returnSelf());
+ $qb->method('setParameter')->will($this->returnSelf());
+ $qb->method('expr')->willReturn($ex);
+
+ $ret = $this->getMockBuilder('\Doctrine\DBAL\Driver\ResultStatement')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $ret->method('fetch')->willReturn([]);
+ $qb->method('execute')->willReturn($ret);
+
+
+ $connection = $this->getMockBuilder('\OC\DB\Connection')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $connection->method('createQueryBuilder')->willReturn($qb);
+
+ $config = $this->getMockBuilder('\OCP\IConfig')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+
+ \OC\Share\Share::setPassword($userSession, $connection, $config, 1, 'pass');
+ }
+
+ /**
+ * @expectedException Exception
+ * @expectedExceptionMessage Cannot update share of a different user
+ *
+ * Test modification of share of another user
+ */
+ public function testSetPasswordShareOtherUser() {
+ $user = $this->getMockBuilder('\OCP\IUser')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $user->method('getUID')->willReturn('user');
+
+ $userSession = $this->getMockBuilder('\OCP\IUserSession')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $userSession->method('getUser')->willReturn($user);
+
+
+ $ex = $this->getMockBuilder('\Doctrine\DBAL\Query\Expression\ExpressionBuilder')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $qb = $this->getMockBuilder('\Doctrine\DBAL\Query\QueryBuilder')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $qb->method('update')->will($this->returnSelf());
+ $qb->method('set')->will($this->returnSelf());
+ $qb->method('where')->will($this->returnSelf());
+ $qb->method('andWhere')->will($this->returnSelf());
+ $qb->method('select')->will($this->returnSelf());
+ $qb->method('from')->will($this->returnSelf());
+ $qb->method('setParameter')->will($this->returnSelf());
+ $qb->method('expr')->willReturn($ex);
+
+ $ret = $this->getMockBuilder('\Doctrine\DBAL\Driver\ResultStatement')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $ret->method('fetch')->willReturn(['uid_owner' => 'user2']);
+ $qb->method('execute')->willReturn($ret);
+
+
+ $connection = $this->getMockBuilder('\OC\DB\Connection')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $connection->method('createQueryBuilder')->willReturn($qb);
+
+ $config = $this->getMockBuilder('\OCP\IConfig')
+ ->disableOriginalConstructor()
+ ->getMock();
+
+
+ \OC\Share\Share::setPassword($userSession, $connection, $config, 1, 'pass');
+ }
+
}
class DummyShareClass extends \OC\Share\Share {
diff --git a/tests/lib/streamwrappers.php b/tests/lib/streamwrappers.php
index fc3d02acae7..6216c5a4be8 100644
--- a/tests/lib/streamwrappers.php
+++ b/tests/lib/streamwrappers.php
@@ -72,6 +72,8 @@ class Test_StreamWrappers extends \Test\TestCase {
}
public function testOC() {
+ // FIXME: use proper tearDown with $this->loginAsUser() and $this->logout()
+ // (would currently break the tests for some reason)
$originalStorage = \OC\Files\Filesystem::getStorage('/');
\OC\Files\Filesystem::clearMounts();
diff --git a/tests/lib/testcase.php b/tests/lib/testcase.php
index a83be713194..e66dfb13353 100644
--- a/tests/lib/testcase.php
+++ b/tests/lib/testcase.php
@@ -167,9 +167,9 @@ abstract class TestCase extends \PHPUnit_Framework_TestCase {
* Login and setup FS as a given user,
* sets the given user as the current user.
*
- * @param string $user user id
+ * @param string $user user id or empty for a generic FS
*/
- static protected function loginAsUser($user) {
+ static protected function loginAsUser($user = '') {
self::logout();
\OC\Files\Filesystem::tearDown();
\OC_User::setUserId($user);
diff --git a/tests/settings/controller/AppSettingsControllerTest.php b/tests/settings/controller/AppSettingsControllerTest.php
new file mode 100644
index 00000000000..d6379deb9c8
--- /dev/null
+++ b/tests/settings/controller/AppSettingsControllerTest.php
@@ -0,0 +1,231 @@
+<?php
+/**
+ * @author Lukas Reschke <lukas@owncloud.com>
+ *
+ * @copyright Copyright (c) 2015, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OC\Settings\Controller;
+
+use OCP\AppFramework\Http\ContentSecurityPolicy;
+use OCP\AppFramework\Http\DataResponse;
+use OCP\AppFramework\Http\TemplateResponse;
+use Test\TestCase;
+use OCP\IRequest;
+use OCP\IL10N;
+use OCP\IConfig;
+use OCP\ICache;
+use OCP\INavigationManager;
+use OCP\App\IAppManager;
+use OC\OCSClient;
+
+/**
+ * Class AppSettingsControllerTest
+ *
+ * @package OC\Settings\Controller
+ */
+class AppSettingsControllerTest extends TestCase {
+ /** @var AppSettingsController */
+ private $appSettingsController;
+ /** @var IRequest */
+ private $request;
+ /** @var IL10N */
+ private $l10n;
+ /** @var IConfig */
+ private $config;
+ /** @var ICache */
+ private $cache;
+ /** @var INavigationManager */
+ private $navigationManager;
+ /** @var IAppManager */
+ private $appManager;
+ /** @var OCSClient */
+ private $ocsClient;
+
+ public function setUp() {
+ parent::setUp();
+
+ $this->request = $this->getMockBuilder('\OCP\IRequest')
+ ->disableOriginalConstructor()->getMock();
+ $this->l10n = $this->getMockBuilder('\OCP\IL10N')
+ ->disableOriginalConstructor()->getMock();
+ $this->l10n->expects($this->any())
+ ->method('t')
+ ->will($this->returnArgument(0));
+ $this->config = $this->getMockBuilder('\OCP\IConfig')
+ ->disableOriginalConstructor()->getMock();
+ $cacheFactory = $this->getMockBuilder('\OCP\ICacheFactory')
+ ->disableOriginalConstructor()->getMock();
+ $this->cache = $this->getMockBuilder('\OCP\ICache')
+ ->disableOriginalConstructor()->getMock();
+ $cacheFactory
+ ->expects($this->once())
+ ->method('create')
+ ->with('settings')
+ ->will($this->returnValue($this->cache));
+
+ $this->navigationManager = $this->getMockBuilder('\OCP\INavigationManager')
+ ->disableOriginalConstructor()->getMock();
+ $this->appManager = $this->getMockBuilder('\OCP\App\IAppManager')
+ ->disableOriginalConstructor()->getMock();
+ $this->ocsClient = $this->getMockBuilder('\OC\OCSClient')
+ ->disableOriginalConstructor()->getMock();
+
+ $this->appSettingsController = new AppSettingsController(
+ 'settings',
+ $this->request,
+ $this->l10n,
+ $this->config,
+ $cacheFactory,
+ $this->navigationManager,
+ $this->appManager,
+ $this->ocsClient
+ );
+ }
+
+ public function testChangeExperimentalConfigStateTrue() {
+ $this->config
+ ->expects($this->once())
+ ->method('setSystemValue')
+ ->with('appstore.experimental.enabled', true);
+ $this->appManager
+ ->expects($this->once())
+ ->method('clearAppsCache');
+ $this->assertEquals(new DataResponse(), $this->appSettingsController->changeExperimentalConfigState(true));
+ }
+
+ public function testChangeExperimentalConfigStateFalse() {
+ $this->config
+ ->expects($this->once())
+ ->method('setSystemValue')
+ ->with('appstore.experimental.enabled', false);
+ $this->appManager
+ ->expects($this->once())
+ ->method('clearAppsCache');
+ $this->assertEquals(new DataResponse(), $this->appSettingsController->changeExperimentalConfigState(false));
+ }
+
+ public function testListCategoriesCached() {
+ $this->cache
+ ->expects($this->exactly(2))
+ ->method('get')
+ ->with('listCategories')
+ ->will($this->returnValue(['CachedArray']));
+ $this->assertSame(['CachedArray'], $this->appSettingsController->listCategories());
+ }
+
+ public function testListCategoriesNotCachedWithoutAppStore() {
+ $expected = [
+ [
+ 'id' => 0,
+ 'displayName' => 'Enabled',
+ ],
+ [
+ 'id' => 1,
+ 'displayName' => 'Not enabled',
+ ],
+ ];
+ $this->cache
+ ->expects($this->once())
+ ->method('get')
+ ->with('listCategories')
+ ->will($this->returnValue(null));
+ $this->cache
+ ->expects($this->once())
+ ->method('set')
+ ->with('listCategories', $expected, 3600);
+
+
+ $this->assertSame($expected, $this->appSettingsController->listCategories());
+ }
+
+ public function testListCategoriesNotCachedWithAppStore() {
+ $expected = [
+ [
+ 'id' => 0,
+ 'displayName' => 'Enabled',
+ ],
+ [
+ 'id' => 1,
+ 'displayName' => 'Not enabled',
+ ],
+ [
+ 'id' => 0,
+ 'displayName' => 'Tools',
+ ],
+ [
+ 'id' => 1,
+ 'displayName' => 'Awesome Games',
+ ],
+ [
+ 'id' => 2,
+ 'displayName' => 'PIM',
+ ],
+ [
+ 'id' => 3,
+ 'displayName' => 'Papershop',
+ ],
+ ];
+
+ $this->cache
+ ->expects($this->once())
+ ->method('get')
+ ->with('listCategories')
+ ->will($this->returnValue(null));
+ $this->cache
+ ->expects($this->once())
+ ->method('set')
+ ->with('listCategories', $expected, 3600);
+
+ $this->ocsClient
+ ->expects($this->once())
+ ->method('isAppStoreEnabled')
+ ->will($this->returnValue(true));
+ $this->ocsClient
+ ->expects($this->once())
+ ->method('getCategories')
+ ->will($this->returnValue(
+ [
+ 'ownCloud Tools',
+ 'Awesome Games',
+ 'ownCloud PIM',
+ 'Papershop',
+ ]
+ ));
+
+ $this->assertSame($expected, $this->appSettingsController->listCategories());
+ }
+
+ public function testViewApps() {
+ $this->config
+ ->expects($this->once())
+ ->method('getSystemValue')
+ ->with('appstore.experimental.enabled', false);
+ $this->navigationManager
+ ->expects($this->once())
+ ->method('setActiveEntry')
+ ->with('core_apps');
+
+ $policy = new ContentSecurityPolicy();
+ $policy->addAllowedImageDomain('https://apps.owncloud.com');
+
+ $expected = new TemplateResponse('settings', 'apps', ['experimentalEnabled' => false], 'user');
+ $expected->setContentSecurityPolicy($policy);
+
+ $this->assertEquals($expected, $this->appSettingsController->viewApps());
+ }
+}