summaryrefslogtreecommitdiffstats
path: root/apps/user_ldap/js/wizard
diff options
context:
space:
mode:
Diffstat (limited to 'apps/user_ldap/js/wizard')
-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.js433
-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.js32
-rw-r--r--apps/user_ldap/js/wizard/wizardDetectorFilterUser.js31
-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.js373
-rw-r--r--apps/user_ldap/js/wizard/wizardTabAdvanced.js330
-rw-r--r--apps/user_ldap/js/wizard/wizardTabElementary.js346
-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.js235
-rw-r--r--apps/user_ldap/js/wizard/wizardTabUserFilter.js136
40 files changed, 4609 insertions, 0 deletions
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..8eb10c58017
--- /dev/null
+++ b/apps/user_ldap/js/wizard/view.js
@@ -0,0 +1,433 @@
+/**
+ * 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)) {
+ 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);
+ }
+ },
+
+ /**
+ * 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..faa9de918a4
--- /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));
+ tabs.push(new OCA.LDAP.Wizard.WizardTabLoginFilter());
+ tabs.push(new OCA.LDAP.Wizard.WizardTabGroupFilter(filterOnTypeFactory));
+ 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..310f261e05e
--- /dev/null
+++ b/apps/user_ldap/js/wizard/wizardDetectorFilterLogin.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 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.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..63dff4e2985
--- /dev/null
+++ b/apps/user_ldap/js/wizard/wizardDetectorFilterUser.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 WizardDetectorFilterUser = OCA.LDAP.Wizard.WizardDetectorFilterSimpleRequestAbstract.subClass({
+ init: function() {
+ this.setTrigger([
+ 'ldap_userfilter_groups',
+ 'ldap_userfilter_objectclass'
+ ]);
+ this.setTargetKey('ldap_userlist_filter');
+
+ 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..448dfc18036
--- /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;
+})(); \ No newline at end of file
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..cf992df43b3
--- /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 a Port Detector. It executes the auto-detection of the port
+ * by the ownCloud server, if requirements are met.
+ *
+ * @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..f3f1368982b
--- /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;
+})(); \ No newline at end of file
diff --git a/apps/user_ldap/js/wizard/wizardFilterOnTypeFactory.js b/apps/user_ldap/js/wizard/wizardFilterOnTypeFactory.js
new file mode 100644
index 00000000000..eb529212734
--- /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;
+})(); \ No newline at end of file
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..ace8501117f
--- /dev/null
+++ b/apps/user_ldap/js/wizard/wizardTabAbstractFilter.js
@@ -0,0 +1,373 @@
+/**
+ * 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);
+ } 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;
+})(); \ No newline at end of file
diff --git a/apps/user_ldap/js/wizard/wizardTabAdvanced.js b/apps/user_ldap/js/wizard/wizardTabAdvanced.js
new file mode 100644
index 00000000000..931a547218b
--- /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;
+})(); \ No newline at end of file
diff --git a/apps/user_ldap/js/wizard/wizardTabElementary.js b/apps/user_ldap/js/wizard/wizardTabElementary.js
new file mode 100644
index 00000000000..9113ecffcf4
--- /dev/null
+++ b/apps/user_ldap/js/wizard/wizardTabElementary.js
@@ -0,0 +1,346 @@
+
+/**
+ * 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) {
+ 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})+'</option>';
+ $(view.configChooserID + ' option:last').before(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;
+})(); \ No newline at end of file
diff --git a/apps/user_ldap/js/wizard/wizardTabExpert.js b/apps/user_ldap/js/wizard/wizardTabExpert.js
new file mode 100644
index 00000000000..6ff93bfd62f
--- /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;
+})(); \ No newline at end of file
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..effb2b3debd
--- /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;
+})(); \ No newline at end of file
diff --git a/apps/user_ldap/js/wizard/wizardTabLoginFilter.js b/apps/user_ldap/js/wizard/wizardTabLoginFilter.js
new file mode 100644
index 00000000000..121b43793f2
--- /dev/null
+++ b/apps/user_ldap/js/wizard/wizardTabLoginFilter.js
@@ -0,0 +1,235 @@
+/**
+ * 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 = result.changes.ldap_test_effective_filter;
+ 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();
+ },
+
+ /**
+ * 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;
+})(); \ No newline at end of file
diff --git a/apps/user_ldap/js/wizard/wizardTabUserFilter.js b/apps/user_ldap/js/wizard/wizardTabUserFilter.js
new file mode 100644
index 00000000000..4ca1199b503
--- /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;
+})(); \ No newline at end of file