diff options
Diffstat (limited to 'apps/user_ldap/js/wizard/configModel.js')
-rw-r--r-- | apps/user_ldap/js/wizard/configModel.js | 606 |
1 files changed, 606 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; +})(); |