diff --git a/apps/user_ldap/ajax/getNewServerConfigPrefix.php b/apps/user_ldap/ajax/getNewServerConfigPrefix.php index 7d1434a8fb..d013591788 100644 --- a/apps/user_ldap/ajax/getNewServerConfigPrefix.php +++ b/apps/user_ldap/ajax/getNewServerConfigPrefix.php @@ -32,4 +32,17 @@ sort($serverConnections); $lk = array_pop($serverConnections); $ln = intval(str_replace('s', '', $lk)); $nk = 's'.str_pad($ln+1, 2, '0', STR_PAD_LEFT); -OCP\JSON::success(array('configPrefix' => $nk)); + +$resultData = array('configPrefix' => $nk); + +if(isset($_POST['copyConfig'])) { + $originalConfig = new \OCA\user_ldap\lib\Configuration($_POST['copyConfig']); + $newConfig = new \OCA\user_ldap\lib\Configuration($nk, false); + $newConfig->setConfiguration($originalConfig->getConfiguration()); + $newConfig->saveConfiguration(); +} else { + $configuration = new \OCA\user_ldap\lib\Configuration($nk, false); + $resultData['defaults'] = $configuration->getDefaults(); +} + +OCP\JSON::success($resultData); diff --git a/apps/user_ldap/ajax/wizard.php b/apps/user_ldap/ajax/wizard.php index ab521b5bf0..267b9568a2 100644 --- a/apps/user_ldap/ajax/wizard.php +++ b/apps/user_ldap/ajax/wizard.php @@ -72,13 +72,11 @@ switch($action) { case 'determineGroupsForGroups': case 'determineAttributes': case 'getUserListFilter': - case 'getLoginFilterMode': case 'getUserLoginFilter': - case 'getUserFilterMode': case 'getGroupFilter': - case 'getGroupFilterMode': case 'countUsers': case 'countGroups': + case 'countInBaseDN': try { $result = $wizard->$action(); if($result !== false) { @@ -93,6 +91,23 @@ switch($action) { exit; break; + case 'testLoginName': { + try { + $loginName = $_POST['ldap_test_loginname']; + $result = $wizard->$action($loginName); + if($result !== false) { + OCP\JSON::success($result->getResultArray()); + exit; + } + } catch (\Exception $e) { + \OCP\JSON::error(array('message' => $e->getMessage())); + exit; + } + \OCP\JSON::error(); + exit; + break; + } + case 'save': $key = isset($_POST['cfgkey']) ? $_POST['cfgkey'] : false; $val = isset($_POST['cfgval']) ? $_POST['cfgval'] : null; @@ -115,6 +130,6 @@ switch($action) { OCP\JSON::success(); break; default: - //TODO: return 4xx error + \OCP\JSON::error(array('message' => $l->t('Action does not exist'))); break; } diff --git a/apps/user_ldap/appinfo/version b/apps/user_ldap/appinfo/version index 8f0916f768..a918a2aa18 100644 --- a/apps/user_ldap/appinfo/version +++ b/apps/user_ldap/appinfo/version @@ -1 +1 @@ -0.5.0 +0.6.0 diff --git a/apps/user_ldap/css/settings.css b/apps/user_ldap/css/settings.css index 8f339451c6..b351f9ae2a 100644 --- a/apps/user_ldap/css/settings.css +++ b/apps/user_ldap/css/settings.css @@ -1,6 +1,6 @@ .table { display: table; - width: 60%; + width: 85%; } .tablerow { @@ -21,10 +21,18 @@ margin-left: 3px; } +.ldapIconCopy { + background-image: url('../img/copy.svg'); +} + .invisible { visibility: hidden; } +.forceHidden { + display: none !important; +} + .ldapSettingsTabs { float: right !important; } @@ -49,13 +57,16 @@ } #ldapWizard1 .hostPortCombinator div span { - width: 7%; - display: table-cell; + width: 14.5%; + display: inline-block; text-align: right; } #ldapWizard1 .host { - width: 96.5% !important; + width: 100%; + margin-left: 0; + margin-right: 0; + border: 0; } .tableCellInput { @@ -77,7 +88,7 @@ color: #FF3B3B; } -.wizSpinner { +.ldapSpinner { height: 15px; margin: 5px; } @@ -104,10 +115,51 @@ width: auto; } +.ldapManyGroupsSupport span { + display: inline-block; + vertical-align: top; + height: 150px; +} + +.ldapManyGroupsSupport span button { + margin-top: 35px; +} + +.ldapManyGroupsSearch { + width: 425px !important; +} + +.ldapGroupList { + height: 150px; + width: 200px; +} + #ldap fieldset input, #ldap fieldset textarea { width: 60%; } +#ldap fieldset textarea ~ button { + vertical-align: text-bottom; +} + +input.ldapVerifyInput { + width: 150px !important; +} + +.ldapInputColElement { + width: 35%; + display: inline-block; + padding-left: 10px; +} + +.ldapToggle { + text-decoration: underline; +} + +span.ldapInputColElement { + margin-top: 9px; +} + #ldap fieldset p input[type=checkbox] { vertical-align: bottom; } diff --git a/apps/user_ldap/img/copy.png b/apps/user_ldap/img/copy.png new file mode 100644 index 0000000000..283d627a5a Binary files /dev/null and b/apps/user_ldap/img/copy.png differ diff --git a/apps/user_ldap/img/copy.svg b/apps/user_ldap/img/copy.svg new file mode 100644 index 0000000000..2e19d8066e --- /dev/null +++ b/apps/user_ldap/img/copy.svg @@ -0,0 +1,120 @@ + + + + + + + + + + + + + image/svg+xml + + + + + Openclipart + + + + + + + + + + + diff --git a/apps/user_ldap/js/experiencedAdmin.js b/apps/user_ldap/js/experiencedAdmin.js deleted file mode 100644 index 7dc5a4e503..0000000000 --- a/apps/user_ldap/js/experiencedAdmin.js +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Copyright (c) 2014, Arthur Schiwon - * This file is licensed under the Affero General Public License version 3 or later. - * See the COPYING-README file. - */ - -/* global LdapWizard */ - -/** - * controls behaviour depend on whether the admin is experienced in LDAP or not. - * - * @class - * @param {object} wizard the LDAP Wizard object - * @param {boolean} initialState whether the admin is experienced or not - */ -function ExperiencedAdmin(wizard, initialState) { - this.wizard = wizard; - this._isExperienced = initialState; - if(this._isExperienced) { - this.hideEntryCounters(); - } -} - - -/** - * toggles whether the admin is an experienced one or not - * - * @param {boolean} isExperienced whether the admin is experienced or not - */ -ExperiencedAdmin.prototype.setExperienced = function(isExperienced) { - this._isExperienced = isExperienced; - if(this._isExperienced) { - this.enableRawMode(); - this.hideEntryCounters(); - } else { - this.showEntryCounters(); - } -}; - -/** -* answers whether the admin is an experienced one or not -* -* @return {boolean} whether the admin is experienced or not -*/ -ExperiencedAdmin.prototype.isExperienced = function() { - return this._isExperienced; -}; - -/** - * switches all LDAP filters from Assisted to Raw mode. - */ -ExperiencedAdmin.prototype.enableRawMode = function() { - LdapWizard._save({id: 'ldapGroupFilterMode'}, LdapWizard.filterModeRaw); - LdapWizard._save({id: 'ldapUserFilterMode' }, LdapWizard.filterModeRaw); - LdapWizard._save({id: 'ldapLoginFilterMode'}, LdapWizard.filterModeRaw); -}; - -ExperiencedAdmin.prototype.updateUserTab = function(mode) { - this._updateTab(mode, $('#ldap_user_count')); -}; - -ExperiencedAdmin.prototype.updateGroupTab = function(mode) { - this._updateTab(mode, $('#ldap_group_count')); -}; - -ExperiencedAdmin.prototype._updateTab = function(mode, $countEl) { - if(mode === LdapWizard.filterModeAssisted) { - $countEl.removeClass('hidden'); - } else if(!this._isExperienced) { - $countEl.removeClass('hidden'); - } else { - $countEl.addClass('hidden'); - } -}; - -/** - * hide user and group counters, they will be displayed on demand only - */ -ExperiencedAdmin.prototype.hideEntryCounters = function() { - $('#ldap_user_count').addClass('hidden'); - $('#ldap_group_count').addClass('hidden'); - $('.ldapGetEntryCount').removeClass('hidden'); -}; - -/** -* shows user and group counters, they will be displayed on demand only -*/ -ExperiencedAdmin.prototype.showEntryCounters = function() { - $('#ldap_user_count').removeClass('hidden'); - $('#ldap_group_count').removeClass('hidden'); - $('.ldapGetEntryCount').addClass('hidden'); -}; diff --git a/apps/user_ldap/js/ldapFilter.js b/apps/user_ldap/js/ldapFilter.js deleted file mode 100644 index dc65858217..0000000000 --- a/apps/user_ldap/js/ldapFilter.js +++ /dev/null @@ -1,193 +0,0 @@ -/* global LdapWizard */ - -function LdapFilter(target, determineModeCallback) { - this.locked = true; - this.target = false; - this.mode = LdapWizard.filterModeAssisted; - this.lazyRunCompose = false; - this.determineModeCallback = determineModeCallback; - this.foundFeatures = false; - this.activated = false; - this.countPending = false; - - if( target === 'User' || - target === 'Login' || - target === 'Group') { - this.target = target; - } -} - -LdapFilter.prototype.activate = function() { - if(this.activated) { - // might be necessary, if configuration changes happened. - this.findFeatures(); - return; - } - this.activated = true; - - this.determineMode(); -}; - -LdapFilter.prototype.compose = function(updateCount) { - var action; - - if(updateCount === true) { - this.countPending = updateCount; - } - - if(this.locked) { - this.lazyRunCompose = true; - return false; - } - - if(this.mode === LdapWizard.filterModeRaw) { - //Raw filter editing, i.e. user defined filter, don't compose - return; - } - - if(this.target === 'User') { - action = 'getUserListFilter'; - } else if(this.target === 'Login') { - action = 'getUserLoginFilter'; - } else if(this.target === 'Group') { - action = 'getGroupFilter'; - } - - var param = 'action='+action+ - '&ldap_serverconfig_chooser='+ - encodeURIComponent($('#ldap_serverconfig_chooser').val()); - - var filter = this; - - LdapWizard.ajax(param, - function(result) { - filter.afterComposeSuccess(result); - }, - function () { - filter.countPending = false; - console.log('LDAP Wizard: could not compose filter. '+ - 'Please check owncloud.log'); - } - ); -}; - -/** - * this function is triggered after LDAP filters have been composed successfully - * @param {object} result returned by the ajax call - */ -LdapFilter.prototype.afterComposeSuccess = function(result) { - LdapWizard.applyChanges(result); - if(this.countPending) { - this.countPending = false; - this.updateCount(); - } -}; - -LdapFilter.prototype.determineMode = function() { - var param = 'action=get'+encodeURIComponent(this.target)+'FilterMode'+ - '&ldap_serverconfig_chooser='+ - encodeURIComponent($('#ldap_serverconfig_chooser').val()); - - var filter = this; - LdapWizard.ajax(param, - function(result) { - var property = 'ldap' + filter.target + 'FilterMode'; - filter.mode = parseInt(result.changes[property], 10); - var rawContainerIsInvisible = - $('#raw'+filter.target+'FilterContainer').hasClass('invisible'); - if ( filter.mode === LdapWizard.filterModeRaw - && rawContainerIsInvisible - ) { - LdapWizard['toggleRaw'+filter.target+'Filter'](); - } else if ( filter.mode === LdapWizard.filterModeAssisted - && !rawContainerIsInvisible - ) { - LdapWizard['toggleRaw'+filter.target+'Filter'](); - } else { - console.log('LDAP Wizard determineMode: returned mode was »' + - filter.mode + '« of type ' + typeof filter.mode); - } - filter.unlock(); - filter.determineModeCallback(filter.mode); - }, - function () { - //on error case get back to default i.e. Assisted - if(!$('#raw'+filter.target+'FilterContainer').hasClass('invisible')) { - LdapWizard['toggleRaw'+filter.target+'Filter'](); - filter.mode = LdapWizard.filterModeAssisted; - } - filter.unlock(); - filter.determineModeCallback(filter.mode); - } - ); -}; - -LdapFilter.prototype.setMode = function(mode) { - if(mode === LdapWizard.filterModeAssisted || mode === LdapWizard.filterModeRaw) { - this.mode = mode; - } -}; - -LdapFilter.prototype.getMode = function() { - return this.mode; -}; - -LdapFilter.prototype.unlock = function() { - this.locked = false; - if(this.lazyRunCompose) { - this.lazyRunCompose = false; - this.compose(); - } -}; - -/** - * resets this.foundFeatures so that LDAP queries can be fired again to retrieve - * objectClasses, groups, etc. - */ -LdapFilter.prototype.reAllowFeatureLookup = function () { - this.foundFeatures = false; -}; - -LdapFilter.prototype.findFeatures = function() { - if(!this.foundFeatures && !this.locked && this.mode === LdapWizard.filterModeAssisted) { - this.foundFeatures = true; - var objcEl, avgrEl; - if(this.target === 'User') { - objcEl = 'ldap_userfilter_objectclass'; - avgrEl = 'ldap_userfilter_groups'; - } else if (this.target === 'Group') { - objcEl = 'ldap_groupfilter_objectclass'; - avgrEl = 'ldap_groupfilter_groups'; - } else if (this.target === 'Login') { - LdapWizard.findAttributes(); - return; - } else { - return false; - } - LdapWizard.findObjectClasses(objcEl, this.target); - LdapWizard.findAvailableGroups(avgrEl, this.target + "s"); - } -}; - -/** - * this function is triggered before user and group counts are executed - * resolving the passed status variable will fire up counting - */ -LdapFilter.prototype.beforeUpdateCount = function() { - var status = $.Deferred(); - LdapWizard.runDetectors(this.target, function() { - status.resolve(); - }); - return status; -}; - -LdapFilter.prototype.updateCount = function(doneCallback) { - var filter = this; - $.when(this.beforeUpdateCount()).done(function() { - if(filter.target === 'User') { - LdapWizard.countUsers(doneCallback); - } else if (filter.target === 'Group') { - LdapWizard.countGroups(doneCallback); - } - }); -}; diff --git a/apps/user_ldap/js/settings.js b/apps/user_ldap/js/settings.js deleted file mode 100644 index 768d62a18d..0000000000 --- a/apps/user_ldap/js/settings.js +++ /dev/null @@ -1,1205 +0,0 @@ -var LdapConfiguration = { - refreshConfig: function() { - if($('#ldap_serverconfig_chooser option').length < 2) { - LdapConfiguration.addConfiguration(true); - return; - } - $.post( - OC.filePath('user_ldap','ajax','getConfiguration.php'), - $('#ldap_serverconfig_chooser').serialize(), - function (result) { - if(result.status === 'success') { - $.each(result.configuration, function(configkey, configvalue) { - elementID = '#'+configkey; - - //deal with Checkboxes - if($(elementID).is('input[type=checkbox]')) { - if(parseInt(configvalue, 10) === 1) { - $(elementID).attr('checked', 'checked'); - } else { - $(elementID).removeAttr('checked'); - } - return; - } - - //On Textareas, Multi-Line Settings come as array - if($(elementID).is('textarea') && $.isArray(configvalue)) { - configvalue = configvalue.join("\n"); - } - - // assign the value - $('#'+configkey).val(configvalue); - }); - LdapWizard.init(); - } - } - ); - }, - - resetDefaults: function() { - $('#ldap').find('input[type=text], input[type=number], input[type=password], textarea, select').each(function() { - if($(this).attr('id') === 'ldap_serverconfig_chooser') { - return; - } - $(this).val($(this).attr('data-default')); - }); - $('#ldap').find('input[type=checkbox]').each(function() { - if($(this).attr('data-default') === 1) { - $(this).attr('checked', 'checked'); - } else { - $(this).removeAttr('checked'); - } - }); - }, - - deleteConfiguration: function() { - $.post( - OC.filePath('user_ldap','ajax','deleteConfiguration.php'), - $('#ldap_serverconfig_chooser').serialize(), - function (result) { - if(result.status === 'success') { - $('#ldap_serverconfig_chooser option:selected').remove(); - $('#ldap_serverconfig_chooser option:first').select(); - LdapConfiguration.refreshConfig(); - } else { - OC.dialogs.alert( - result.message, - t('user_ldap', 'Deletion failed') - ); - } - } - ); - }, - - addConfiguration: function(doNotAsk) { - $.post( - OC.filePath('user_ldap','ajax','getNewServerConfigPrefix.php'), - function (result) { - if(result.status === 'success') { - if(doNotAsk) { - LdapConfiguration.resetDefaults(); - } else { - OC.dialogs.confirm( - t('user_ldap', 'Take over settings from recent server configuration?'), - t('user_ldap', 'Keep settings?'), - function(keep) { - if(!keep) { - LdapConfiguration.resetDefaults(); - } - } - ); - } - $('#ldap_serverconfig_chooser option:selected').removeAttr('selected'); - var html = ''; - $('#ldap_serverconfig_chooser option:last').before(html); - LdapWizard.init(); - } else { - OC.dialogs.alert( - result.message, - t('user_ldap', 'Cannot add server configuration') - ); - } - } - ); - }, - - testConfiguration: function(onSuccess, onError) { - $.post( - OC.filePath('user_ldap','ajax','testConfiguration.php'), - $('#ldap').serialize(), - function (result) { - if (result.status === 'success') { - onSuccess(result); - } else { - onError(result); - } - } - ); - }, - - clearMappings: function(mappingSubject) { - $.post( - OC.filePath('user_ldap','ajax','clearMappings.php'), - 'ldap_clear_mapping='+encodeURIComponent(mappingSubject), - function(result) { - if(result.status === 'success') { - OC.dialogs.info( - t('user_ldap', 'mappings cleared'), - t('user_ldap', 'Success') - ); - } else { - OC.dialogs.alert( - result.message, - t('user_ldap', 'Error') - ); - } - } - ); - } -}; - -var LdapWizard = { - checkPortInfoShown: false, - saveBlacklist: {}, - userFilterGroupSelectState: 'enable', - spinner: '', - filterModeAssisted: 0, - filterModeRaw: 1, - userFilter: false, - loginFilter: false, - groupFilter: false, - ajaxRequests: {}, - lastTestSuccessful: true, - - ajax: function(param, fnOnSuccess, fnOnError, reqID) { - if(!_.isUndefined(reqID)) { - if(LdapWizard.ajaxRequests.hasOwnProperty(reqID)) { - console.log('aborting ' + reqID); - console.log(param); - LdapWizard.ajaxRequests[reqID].abort(); - } - } - var request = $.post( - OC.filePath('user_ldap','ajax','wizard.php'), - param, - function(result) { - if(result.status === 'success') { - fnOnSuccess(result); - } else { - fnOnError(result); - } - } - ); - if(!_.isUndefined(reqID)) { - LdapWizard.ajaxRequests[reqID] = request; - } - return request; - }, - - applyChanges: function (result) { - for (var id in result.changes) { - LdapWizard.blacklistAdd(id); - if(id.indexOf('count') > 0) { - $('#'+id).text(result.changes[id]); - } else { - $('#'+id).val(result.changes[id]); - } - } - LdapWizard.functionalityCheck(); - - if($('#ldapSettings').tabs('option', 'active') == 0) { - LdapWizard.basicStatusCheck(); - } - }, - - enableTabs: function() { - //do not use this function directly, use basicStatusCheck instead. - if(LdapWizard.saveProcesses === 0) { - $('.ldap_action_continue').removeAttr('disabled'); - $('.ldap_action_back').removeAttr('disabled'); - $('#ldapSettings').tabs('option', 'disabled', []); - } - }, - - disableTabs: function() { - $('.ldap_action_continue').attr('disabled', 'disabled'); - $('.ldap_action_back').attr('disabled', 'disabled'); - $('#ldapSettings').tabs('option', 'disabled', [1, 2, 3, 4, 5]); - }, - - basicStatusCheck: function() { - //criteria to continue from the first tab - // - host, port, user filter, agent dn, password, base dn - var host = $('#ldap_host').val(); - var port = $('#ldap_port').val(); - var agent = $('#ldap_dn').val(); - var pwd = $('#ldap_agent_password').val(); - var base = $('#ldap_base').val(); - - if((host && port && base) && ((!agent && !pwd) || (agent && pwd))) { - LdapWizard.enableTabs(); - } else { - LdapWizard.disableTabs(); - } - }, - - - blacklistAdd: function(id) { - var obj = $('#' + id); - if(!(obj[0].hasOwnProperty('multiple') && obj[0]['multiple'] === true)) { - //no need to blacklist multiselect - LdapWizard.saveBlacklist[id] = true; - return true; - } - return false; - }, - - blacklistRemove: function(id) { - if(LdapWizard.saveBlacklist.hasOwnProperty(id)) { - delete LdapWizard.saveBlacklist[id]; - return true; - } - return false; - }, - - checkBaseDN: function() { - var host = $('#ldap_host').val(); - var port = $('#ldap_port').val(); - var user = $('#ldap_dn').val(); - var pass = $('#ldap_agent_password').val(); - - //FIXME: determine base dn with anonymous access - if(host && port && user && pass) { - var param = 'action=guessBaseDN'+ - '&ldap_serverconfig_chooser='+ - encodeURIComponent($('#ldap_serverconfig_chooser').val()); - - LdapWizard.showSpinner('#ldap_base'); - $('#ldap_base').prop('disabled', 'disabled'); - LdapWizard.ajax(param, - function(result) { - LdapWizard.applyChanges(result); - LdapWizard.hideSpinner('#ldap_base'); - if($('#ldap_base').val()) { - LdapWizard.hideInfoBox(); - } - $('#ldap_base').prop('disabled', false); - }, - function (result) { - LdapWizard.hideSpinner('#ldap_base'); - LdapWizard.showInfoBox(t('user_ldap', 'Please specify a Base DN')); - LdapWizard.showInfoBox(t('user_ldap', 'Could not determine Base DN')); - $('#ldap_base').prop('disabled', false); - }, - 'guessBaseDN' - ); - } - }, - - checkPort: function() { - var host = $('#ldap_host').val(); - var port = $('#ldap_port').val(); - - if(host && !port) { - var param = 'action=guessPortAndTLS'+ - '&ldap_serverconfig_chooser='+ - encodeURIComponent($('#ldap_serverconfig_chooser').val()); - - LdapWizard.showSpinner('#ldap_port'); - $('#ldap_port').prop('disabled', 'disabled'); - LdapWizard.ajax(param, - function(result) { - LdapWizard.applyChanges(result); - LdapWizard.hideSpinner('#ldap_port'); - if($('#ldap_port').val()) { - LdapWizard.checkBaseDN(); - $('#ldap_port').prop('disabled', false); - LdapWizard.hideInfoBox(); - } - }, - function (result) { - LdapWizard.hideSpinner('#ldap_port'); - $('#ldap_port').prop('disabled', false); - LdapWizard.showInfoBox(t('user_ldap', 'Please specify the port')); - }, - 'guessPortAndTLS' - ); - } - }, - - controlBack: function() { - var curTabIndex = $('#ldapSettings').tabs('option', 'active'); - if(curTabIndex == 0) { - return; - } - $('#ldapSettings').tabs('option', 'active', curTabIndex - 1); - LdapWizard.controlUpdate(curTabIndex - 1); - }, - - controlContinue: function() { - var curTabIndex = $('#ldapSettings').tabs('option', 'active'); - if(curTabIndex == 3) { - return; - } - $('#ldapSettings').tabs('option', 'active', 1 + curTabIndex); - LdapWizard.controlUpdate(curTabIndex + 1); - }, - - controlUpdate: function(nextTabIndex) { - if(nextTabIndex == 0) { - $('.ldap_action_back').addClass('invisible'); - $('.ldap_action_continue').removeClass('invisible'); - } else - if(nextTabIndex == 1) { - $('.ldap_action_back').removeClass('invisible'); - $('.ldap_action_continue').removeClass('invisible'); - } else - if(nextTabIndex == 2) { - $('.ldap_action_continue').removeClass('invisible'); - $('.ldap_action_back').removeClass('invisible'); - } else - if(nextTabIndex == 3) { - //now last tab - $('.ldap_action_back').removeClass('invisible'); - $('.ldap_action_continue').addClass('invisible'); - } - }, - - _countThings: function(method, spinnerID, doneCallback) { - var param = 'action='+method+ - '&ldap_serverconfig_chooser='+ - encodeURIComponent($('#ldap_serverconfig_chooser').val()); - - LdapWizard.showSpinner(spinnerID); - LdapWizard.ajax(param, - function(result) { - LdapWizard.applyChanges(result); - LdapWizard.hideSpinner(spinnerID); - if(!_.isUndefined(doneCallback)) { - doneCallback(method); - } - }, - function (result) { - OC.Notification.showTemporary('Counting the entries failed with: ' + result.message); - LdapWizard.hideSpinner(spinnerID); - if(!_.isUndefined(doneCallback)) { - doneCallback(method); - } - }, - method - ); - }, - - countGroups: function(doneCallback) { - var groupFilter = $('#ldap_group_filter').val(); - if(!_.isEmpty(groupFilter)) { - LdapWizard._countThings('countGroups', '#ldap_group_count', doneCallback); - } - }, - - countUsers: function(doneCallback) { - var userFilter = $('#ldap_userlist_filter').val(); - if(!_.isEmpty(userFilter)) { - LdapWizard._countThings('countUsers', '#ldap_user_count', doneCallback); - } - }, - - /** - * called after detectors have run - * @callback runDetectorsCallback - */ - - /** - * runs detectors to determine appropriate attributes, e.g. displayName - * @param {string} type either "User" or "Group" - * @param {runDetectorsCallback} triggered after all detectors have completed - */ - runDetectors: function(type, callback) { - if(type === 'Group') { - $.when(LdapWizard.detectGroupMemberAssoc()) - .then(callback, callback); - if( LdapWizard.admin.isExperienced - && !(LdapWizard.detectorsRunInXPMode & LdapWizard.groupDetectors)) { - LdapWizard.detectorsRunInXPMode += LdapWizard.groupDetectors; - } - } else if(type === 'User') { - var req1 = LdapWizard.detectUserDisplayNameAttribute(); - var req2 = LdapWizard.detectEmailAttribute(); - $.when(req1, req2) - .then(callback, callback); - if( LdapWizard.admin.isExperienced - && !(LdapWizard.detectorsRunInXPMode & LdapWizard.userDetectors)) { - LdapWizard.detectorsRunInXPMode += LdapWizard.userDetectors; - } - } - }, - - /** - * runs detector to find out a fitting user display name attribute - */ - detectUserDisplayNameAttribute: function() { - var param = 'action=detectUserDisplayNameAttribute' + - '&ldap_serverconfig_chooser='+ - encodeURIComponent($('#ldap_serverconfig_chooser').val()); - - //runs in the background, no callbacks necessary - return LdapWizard.ajax(param, LdapWizard.applyChanges, function(){}, 'detectUserDisplayNameAttribute'); - }, - - detectEmailAttribute: function() { - var param = 'action=detectEmailAttribute'+ - '&ldap_serverconfig_chooser='+ - encodeURIComponent($('#ldap_serverconfig_chooser').val()); - //runs in the background, no callbacks necessary - return LdapWizard.ajax(param, LdapWizard.applyChanges, function(){}, 'detectEmailAttribute'); - }, - - detectGroupMemberAssoc: function() { - param = 'action=determineGroupMemberAssoc'+ - '&ldap_serverconfig_chooser='+ - encodeURIComponent($('#ldap_serverconfig_chooser').val()); - - return LdapWizard.ajax(param, - function(result) { - //pure background story - }, - function (result) { - // error handling - }, - 'determineGroupMemberAssoc' - ); - }, - - findAttributes: function() { - param = 'action=determineAttributes'+ - '&ldap_serverconfig_chooser='+ - encodeURIComponent($('#ldap_serverconfig_chooser').val()); - - LdapWizard.showSpinner('#ldap_loginfilter_attributes'); - LdapWizard.ajax(param, - function(result) { - $('#ldap_loginfilter_attributes').find('option').remove(); - for (var i in result.options['ldap_loginfilter_attributes']) { - //FIXME: move HTML into template - var attr = result.options['ldap_loginfilter_attributes'][i]; - $('#ldap_loginfilter_attributes').append( - ""); - } - LdapWizard.hideSpinner('#ldap_loginfilter_attributes'); - LdapWizard.applyChanges(result); - $('#ldap_loginfilter_attributes').multiselect('refresh'); - if($('#rawLoginFilterContainer').hasClass('invisible')) { - $('#ldap_loginfilter_attributes').multiselect('enable'); - } - LdapWizard.postInitLoginFilter(); - }, - function (result) { - //deactivate if no attributes found - $('#ldap_loginfilter_attributes').multiselect( - {noneSelectedText : 'No attributes found'}); - $('#ldap_loginfilter_attributes').multiselect('disable'); - LdapWizard.hideSpinner('#ldap_loginfilter_attributes'); - }, - 'determineAttributes' - ); - }, - - findAvailableGroups: function(multisel, type) { - if(type !== 'Users' && type !== 'Groups') { - return false; - } - param = 'action=determineGroupsFor'+encodeURIComponent(type)+ - '&ldap_serverconfig_chooser='+ - encodeURIComponent($('#ldap_serverconfig_chooser').val()); - - LdapWizard.showSpinner('#'+multisel); - LdapWizard.ajax(param, - function(result) { - $('#'+multisel).find('option').remove(); - for (var i in result.options[multisel]) { - //FIXME: move HTML into template - objc = result.options[multisel][i]; - $('#'+multisel).append(""); - } - LdapWizard.hideSpinner('#'+multisel); - LdapWizard.applyChanges(result); - $('#'+multisel).multiselect('refresh'); - part = type.slice(0, -1); - if($('#raw' + part + 'FilterContainer').hasClass('invisible')) { - //enable only when raw filter editing is not turned on - $('#'+multisel).multiselect('enable'); - } - if(type === 'Users') { - //required for initial save - filter = $('#ldap_userlist_filter').val(); - if(!filter) { - LdapWizard.saveMultiSelect(multisel, - $('#'+multisel).multiselect("getChecked")); - } - LdapWizard.userFilterAvailableGroupsHasRun = true; - LdapWizard.postInitUserFilter(); - } - }, - function (result) { - LdapWizard.hideSpinner('#'+multisel); - $('#'+multisel).multiselect('disable'); - if(type === 'Users') { - LdapWizard.userFilterAvailableGroupsHasRun = true; - LdapWizard.postInitUserFilter(); - } - }, - 'findAvailableGroupsFor' + type - ); - }, - - findObjectClasses: function(multisel, type) { - if(type !== 'User' && type !== 'Group') { - return false; - } - var param = 'action=determine'+encodeURIComponent(type)+'ObjectClasses'+ - '&ldap_serverconfig_chooser='+ - encodeURIComponent($('#ldap_serverconfig_chooser').val()); - - LdapWizard.showSpinner('#'+multisel); - LdapWizard.ajax(param, - function(result) { - $('#'+multisel).find('option').remove(); - for (var i in result.options[multisel]) { - //FIXME: move HTML into template - objc = result.options[multisel][i]; - $('#'+multisel).append(""); - } - LdapWizard.hideSpinner('#'+multisel); - LdapWizard.applyChanges(result); - $('#'+multisel).multiselect('refresh'); - if(type === 'User') { - //required for initial save - filter = $('#ldap_userlist_filter').val(); - if(!filter) { - LdapWizard.saveMultiSelect(multisel, - $('#'+multisel).multiselect("getChecked")); - } - LdapWizard.userFilterObjectClassesHasRun = true; - LdapWizard.postInitUserFilter(); - } - }, - function (result) { - LdapWizard.hideSpinner('#'+multisel); - if(type === 'User') { - LdapWizard.userFilterObjectClassesHasRun = true; - LdapWizard.postInitUserFilter(); - } - //TODO: error handling - }, - 'determine' + type + 'ObjectClasses' - ); - }, - - functionalityCheck: function() { - //criteria to enable the connection: - // - host, port, basedn, user filter, login filter - var host = $('#ldap_host').val(); - var port = $('#ldap_port').val(); - var base = $('#ldap_base').val(); - var userfilter = $('#ldap_userlist_filter').val(); - var loginfilter = $('#ldap_login_filter').val(); - - //FIXME: activates a manually deactivated configuration. - if(host && port && base && userfilter && loginfilter) { - LdapWizard.updateStatusIndicator(true); - if($('#ldap_configuration_active').is(':checked')) { - return; - } - if(!LdapWizard.isConfigurationActiveControlLocked) { - //avoids a manually deactivated connection will be activated - //upon opening the admin page - $('#ldap_configuration_active').prop('checked', true); - LdapWizard.save($('#ldap_configuration_active')[0]); - } - } else { - if($('#ldap_configuration_active').is(':checked')) { - $('#ldap_configuration_active').prop('checked', false); - LdapWizard.save($('#ldap_configuration_active')[0]); - } - LdapWizard.updateStatusIndicator(false); - } - }, - - hideInfoBox: function() { - if(LdapWizard.checkInfoShown) { - $('#ldapWizard1 .ldapWizardInfo').addClass('invisible'); - LdapWizard.checkInfoShown = false; - } - }, - - hideSpinner: function(id) { - $(id+' + .wizSpinner').remove(); - $(id + " + button").css('display', 'inline'); - }, - - isConfigurationActiveControlLocked: true, - detectorsRunInXPMode: 0, - userDetectors: 1, - groupDetectors: 2, - - init: function() { - LdapWizard.detectorsRunInXPMode = 0; - LdapWizard.instantiateFilters(); - LdapWizard.admin.setExperienced($('#ldap_experienced_admin').is(':checked')); - LdapWizard.lastTestSuccessful = true; - LdapWizard.basicStatusCheck(); - LdapWizard.functionalityCheck(); - LdapWizard.isConfigurationActiveControlLocked = false; - }, - - initGroupFilter: function() { - LdapWizard.groupFilter.activate(); - }, - - /** init login filter tab section **/ - - initLoginFilter: function() { - LdapWizard.loginFilter.activate(); - }, - - postInitLoginFilter: function() { - if($('#rawLoginFilterContainer').hasClass('invisible')) { - LdapWizard.loginFilter.compose(); - } - }, - - /** end of init user filter tab section **/ - - initMultiSelect: function(object, id, caption) { - object.multiselect({ - header: false, - selectedList: 9, - noneSelectedText: caption, - click: function(event, ui) { - LdapWizard.saveMultiSelect(id, - $('#'+id).multiselect("getChecked")); - } - }); - }, - - hideTestSpinner:function (countMethod) { - var selector; - if(countMethod === 'countUsers') { - selector = '#rawUserFilterContainer .ldapGetEntryCount'; - } else { - selector = '#rawGroupFilterContainer .ldapGetEntryCount'; - } - LdapWizard.hideSpinner(selector); - }, - - /** init user filter tab section **/ - - instantiateFilters: function() { - delete LdapWizard.userFilter; - LdapWizard.userFilter = new LdapFilter('User', function(mode) { - if( !LdapWizard.admin.isExperienced() - || mode === LdapWizard.filterModeAssisted) { - LdapWizard.userFilter.updateCount(); - } - LdapWizard.userFilter.findFeatures(); - }); - $('#rawUserFilterContainer .ldapGetEntryCount').click(function(event) { - event.preventDefault(); - $('#ldap_user_count').text(''); - LdapWizard.showSpinner('#rawUserFilterContainer .ldapGetEntryCount'); - LdapWizard.userFilter.updateCount(LdapWizard.hideTestSpinner); - $('#ldap_user_count').removeClass('hidden'); - }); - - delete LdapWizard.loginFilter; - LdapWizard.loginFilter = new LdapFilter('Login', function(mode) { - LdapWizard.loginFilter.findFeatures(); - }); - - delete LdapWizard.groupFilter; - LdapWizard.groupFilter = new LdapFilter('Group', function(mode) { - if( !LdapWizard.admin.isExperienced() - || mode === LdapWizard.filterModeAssisted) { - LdapWizard.groupFilter.updateCount(); - } - LdapWizard.groupFilter.findFeatures(); - }); - $('#rawGroupFilterContainer .ldapGetEntryCount').click(function(event) { - event.preventDefault(); - $('#ldap_group_count').text(''); - LdapWizard.showSpinner('#rawGroupFilterContainer .ldapGetEntryCount'); - LdapWizard.groupFilter.updateCount(LdapWizard.hideTestSpinner); - $('#ldap_group_count').removeClass('hidden'); - }); - }, - - userFilterObjectClassesHasRun: false, - userFilterAvailableGroupsHasRun: false, - - initUserFilter: function() { - LdapWizard.userFilterObjectClassesHasRun = false; - LdapWizard.userFilterAvailableGroupsHasRun = false; - LdapWizard.userFilter.activate(); - }, - - postInitUserFilter: function() { - if(LdapWizard.userFilterObjectClassesHasRun && - LdapWizard.userFilterAvailableGroupsHasRun) { - LdapWizard.userFilter.compose(); - } - }, - - /** end of init user filter tab section **/ - - onTabChange: function(event, ui) { - if(LdapWizard.saveProcesses > 0) { - //do not allow to switch tabs as long as a save process is active - return false; - } - var newTabIndex = 0; - if(ui.newTab[0].id === '#ldapWizard2') { - LdapWizard.initUserFilter(); - newTabIndex = 1; - } else if(ui.newTab[0].id === '#ldapWizard3') { - LdapWizard.initLoginFilter(); - newTabIndex = 2; - } else if(ui.newTab[0].id === '#ldapWizard4') { - LdapWizard.initGroupFilter(); - newTabIndex = 3; - } - - var curTabIndex = $('#ldapSettings').tabs('option', 'active'); - if(curTabIndex >= 0 && curTabIndex <= 3) { - LdapWizard.controlUpdate(newTabIndex); - //run detectors in XP mode, when "Test Filter" button has not been - //clicked in order to make sure that email, displayname, member- - //group association attributes are properly set. - if( curTabIndex === 1 - && LdapWizard.admin.isExperienced - && !(LdapWizard.detecorsRunInXPMode & LdapWizard.userDetectors) - ) { - LdapWizard.runDetectors('User', function(){}); - } else if( curTabIndex === 3 - && LdapWizard.admin.isExperienced - && !(LdapWizard.detecorsRunInXPMode & LdapWizard.groupDetectors) - ) { - LdapWizard.runDetectors('Group', function(){}); - } - } - }, - - /** - * allows UserFilter, LoginFilter and GroupFilter to lookup objectClasses - * and similar again. This should be called after essential changes, e.g. - * Host or BaseDN changes, or positive functionality check - * - */ - allowFilterFeatureSearch: function () { - LdapWizard.userFilter.reAllowFeatureLookup(); - LdapWizard.loginFilter.reAllowFeatureLookup(); - LdapWizard.groupFilter.reAllowFeatureLookup(); - }, - - processChanges: function (triggerObj) { - LdapWizard.hideInfoBox(); - - if(triggerObj.id === 'ldap_host' - || triggerObj.id === 'ldap_port' - || triggerObj.id === 'ldap_dn' - || triggerObj.id === 'ldap_agent_password') { - LdapWizard.checkPort(); - if($('#ldap_port').val()) { - //if Port is already set, check BaseDN - LdapWizard.checkBaseDN(); - LdapWizard.allowFilterFeatureSearch(); - } - } - - if(triggerObj.id === 'ldap_loginfilter_username' - || triggerObj.id === 'ldap_loginfilter_email') { - LdapWizard.loginFilter.compose(); - } else if (!LdapWizard.admin.isExperienced()) { - if(triggerObj.id === 'ldap_userlist_filter') { - LdapWizard.userFilter.updateCount(); - } else if (triggerObj.id === 'ldap_group_filter') { - LdapWizard.groupFilter.updateCount(); - } - } - - if($('#ldapSettings').tabs('option', 'active') == 0) { - LdapWizard.basicStatusCheck(); - LdapWizard.functionalityCheck(); - } - }, - - save: function(inputObj) { - if(LdapWizard.blacklistRemove(inputObj.id)) { - return; - } - if($(inputObj).is('input[type=checkbox]') - && !$(inputObj).is(':checked')) { - val = 0; - } else { - val = $(inputObj).val(); - } - LdapWizard._save(inputObj, val); - }, - - /** - * updates user or group count on multiSelect close. Resets the event - * function subsequently. - * - * @param {LdapFilter} filter - * @param {Object} $multiSelectObj - */ - onMultiSelectClose: function(filter, $multiSelectObj) { - filter.updateCount(); - $multiSelectObj.multiselect({close: function(){}}); - }, - - saveMultiSelect: function(originalObj, resultObj) { - var values = ''; - for(var i = 0; i < resultObj.length; i++) { - values = values + "\n" + resultObj[i].value; - } - LdapWizard._save($('#'+originalObj)[0], $.trim(values)); - var $multiSelectObj = $('#'+originalObj); - var updateCount = !$multiSelectObj.multiselect("isOpen"); - var applyUpdateOnCloseToFilter; - if(originalObj === 'ldap_userfilter_objectclass' - || originalObj === 'ldap_userfilter_groups') { - LdapWizard.userFilter.compose(updateCount); - if(!updateCount) { - applyUpdateOnCloseToFilter = LdapWizard.userFilter; - } - //when user filter is changed afterwards, login filter needs to - //be adjusted, too - if(!LdapWizard.loginFilter) { - LdapWizard.initLoginFilter(); - } - LdapWizard.loginFilter.compose(); - } else if(originalObj === 'ldap_loginfilter_attributes') { - LdapWizard.loginFilter.compose(); - } else if(originalObj === 'ldap_groupfilter_objectclass' - || originalObj === 'ldap_groupfilter_groups') { - LdapWizard.groupFilter.compose(updateCount); - if(!updateCount) { - applyUpdateOnCloseToFilter = LdapWizard.groupFilter; - } - } - - if(applyUpdateOnCloseToFilter instanceof LdapFilter) { - $multiSelectObj.multiselect({ - close: function () { - LdapWizard.onMultiSelectClose( - applyUpdateOnCloseToFilter, $multiSelectObj); - } - }); - } - }, - - saveProcesses: 0, - _save: function(object, value) { - $('#ldap .ldap_saving').removeClass('hidden'); - LdapWizard.saveProcesses += 1; - $('#ldap *').addClass('save-cursor'); - param = 'cfgkey='+encodeURIComponent(object.id)+ - '&cfgval='+encodeURIComponent(value)+ - '&action=save'+ - '&ldap_serverconfig_chooser='+$('#ldap_serverconfig_chooser').val(); - - $.post( - OC.filePath('user_ldap','ajax','wizard.php'), - param, - function(result) { - LdapWizard.saveProcesses -= 1; - if(LdapWizard.saveProcesses === 0) { - $('#ldap .ldap_saving').addClass('hidden'); - $('#ldap *').removeClass('save-cursor'); - } - if(result.status === 'success') { - LdapWizard.processChanges(object); - } else { - console.log('Could not save value for ' + object.id); - } - } - ); - }, - - showInfoBox: function(text) { - $('#ldapWizard1 .ldapWizardInfo').text(text); - $('#ldapWizard1 .ldapWizardInfo').removeClass('invisible'); - LdapWizard.checkInfoShown = true; - }, - - showSpinner: function(id) { - if($(id + ' + .wizSpinner').length == 0) { - $(LdapWizard.spinner).insertAfter($(id)); - $(id + " + img + button").css('display', 'none'); - } - }, - - toggleRawFilter: function(container, moc, mg, stateVar, modeKey) { - var isUser = moc.indexOf('user') >= 0; - var filter = isUser ? LdapWizard.userFilter : LdapWizard.groupFilter; - //moc = multiselect objectclass - //mg = mutliselect groups - if($(container).hasClass('invisible')) { - filter.setMode(LdapWizard.filterModeRaw); - $(container).removeClass('invisible'); - $(moc).multiselect('disable'); - if($(mg).multiselect().attr('disabled') === 'disabled') { - LdapWizard[stateVar] = 'disable'; - } else { - LdapWizard[stateVar] = 'enable'; - } - $(mg).multiselect('disable'); - LdapWizard._save({ id: modeKey }, LdapWizard.filterModeRaw); - } else { - filter.setMode(LdapWizard.filterModeAssisted); - filter.findFeatures(); - $(container).addClass('invisible'); - $(mg).multiselect(LdapWizard[stateVar]); - $(moc).multiselect('enable'); - LdapWizard._save({ id: modeKey }, LdapWizard.filterModeAssisted); - if(isUser) { - LdapWizard.blacklistRemove('ldap_userlist_filter'); - LdapWizard.userFilter.compose(true); - } else { - LdapWizard.blacklistRemove('ldap_group_filter'); - LdapWizard.groupFilter.compose(true); - } - } - }, - - onToggleRawFilterConfirmation: function(currentMode, isRawVisible, callback) { - if( !LdapWizard.admin.isExperienced() - || currentMode === LdapWizard.filterModeAssisted - || (LdapWizard.admin.isExperienced() && !isRawVisible) - ) { - return callback(true); - } - - var confirmed = OCdialogs.confirm( - 'Switching the mode will enable automatic LDAP queries. Depending on your LDAP size they may take a while. Do you still want to switch the mode?', - 'Mode switch', - callback - ); - }, - - toggleRawGroupFilter: function() { - LdapWizard.onToggleRawFilterConfirmation( - LdapWizard.groupFilter.getMode(), - !$('#rawGroupFilterContainer').hasClass('invisible'), - function(confirmed) { - if(confirmed !== true) { - return; - } - - LdapWizard.blacklistRemove('ldap_group_filter'); - LdapWizard.toggleRawFilter('#rawGroupFilterContainer', - '#ldap_groupfilter_objectclass', - '#ldap_groupfilter_groups', - 'groupFilterGroupSelectState', - 'ldapGroupFilterMode' - ); - LdapWizard.admin.updateGroupTab(LdapWizard.groupFilter.getMode()); - } - ); - }, - - toggleRawLoginFilter: function() { - LdapWizard.onToggleRawFilterConfirmation( - LdapWizard.loginFilter.getMode(), - !$('#rawLoginFilterContainer').hasClass('invisible'), - function(confirmed) { - if(confirmed !== true) { - return; - } - - LdapWizard.blacklistRemove('ldap_login_filter'); - container = '#rawLoginFilterContainer'; - if($(container).hasClass('invisible')) { - $(container).removeClass('invisible'); - action = 'disable'; - property = 'disabled'; - mode = LdapWizard.filterModeRaw; - } else { - $(container).addClass('invisible'); - action = 'enable'; - property = false; - mode = LdapWizard.filterModeAssisted; - } - LdapWizard.loginFilter.setMode(mode); - LdapWizard.loginFilter.findFeatures(); - $('#ldap_loginfilter_attributes').multiselect(action); - $('#ldap_loginfilter_email').prop('disabled', property); - $('#ldap_loginfilter_username').prop('disabled', property); - LdapWizard._save({ id: 'ldapLoginFilterMode' }, mode); - if(action === 'enable') { - LdapWizard.loginFilter.compose(); - } - } - ); - }, - - toggleRawUserFilter: function() { - LdapWizard.onToggleRawFilterConfirmation( - LdapWizard.userFilter.getMode(), - !$('#rawUserFilterContainer').hasClass('invisible'), - function(confirmed) { - if(confirmed === true) { - LdapWizard.blacklistRemove('ldap_userlist_filter'); - LdapWizard.toggleRawFilter('#rawUserFilterContainer', - '#ldap_userfilter_objectclass', - '#ldap_userfilter_groups', - 'userFilterGroupSelectState', - 'ldapUserFilterMode' - ); - LdapWizard.admin.updateUserTab(LdapWizard.userFilter.getMode()); - } - } - ); - }, - - updateStatusIndicator: function(isComplete) { - if(isComplete) { - LdapConfiguration.testConfiguration( - //onSuccess - function(result) { - $('.ldap_config_state_indicator').text(t('user_ldap', - 'Configuration OK' - )); - $('.ldap_config_state_indicator').addClass('ldap_grey'); - $('.ldap_config_state_indicator_sign').removeClass('error'); - $('.ldap_config_state_indicator_sign').addClass('success'); - if(!LdapWizard.lastTestSuccessful) { - LdapWizard.lastTestSuccessful = true; - LdapWizard.allowFilterFeatureSearch(); - } - }, - //onError - function(result) { - $('.ldap_config_state_indicator').text(t('user_ldap', - 'Configuration incorrect' - )); - $('.ldap_config_state_indicator').removeClass('ldap_grey'); - $('.ldap_config_state_indicator_sign').addClass('error'); - $('.ldap_config_state_indicator_sign').removeClass('success'); - LdapWizard.lastTestSuccessful = false; - } - ); - } else { - $('.ldap_config_state_indicator').text(t('user_ldap', - 'Configuration incomplete' - )); - $('.ldap_config_state_indicator').removeClass('ldap_grey'); - $('.ldap_config_state_indicator_sign').removeClass('error'); - $('.ldap_config_state_indicator_sign').removeClass('success'); - } - } -}; - -$(document).ready(function() { - $('#ldapAdvancedAccordion').accordion({ heightStyle: 'content', animate: 'easeInOutCirc'}); - $('#ldapSettings').tabs({ beforeActivate: LdapWizard.onTabChange }); - $('.ldap_submit').button(); - $('.ldap_action_test_connection').button(); - $('#ldap_action_delete_configuration').button(); - LdapWizard.initMultiSelect($('#ldap_userfilter_groups'), - 'ldap_userfilter_groups', - t('user_ldap', 'Select groups')); - LdapWizard.initMultiSelect($('#ldap_userfilter_objectclass'), - 'ldap_userfilter_objectclass', - t('user_ldap', 'Select object classes')); - LdapWizard.initMultiSelect($('#ldap_loginfilter_attributes'), - 'ldap_loginfilter_attributes', - t('user_ldap', 'Select attributes')); - LdapWizard.initMultiSelect($('#ldap_groupfilter_groups'), - 'ldap_groupfilter_groups', - t('user_ldap', 'Select groups')); - LdapWizard.initMultiSelect($('#ldap_groupfilter_objectclass'), - 'ldap_groupfilter_objectclass', - t('user_ldap', 'Select object classes')); - - $('.lwautosave').change(function() { LdapWizard.save(this); }); - $('#toggleRawUserFilter').click(LdapWizard.toggleRawUserFilter); - $('#toggleRawGroupFilter').click(LdapWizard.toggleRawGroupFilter); - $('#toggleRawLoginFilter').click(LdapWizard.toggleRawLoginFilter); - LdapConfiguration.refreshConfig(); - $('.ldap_action_continue').click(function(event) { - event.preventDefault(); - LdapWizard.controlContinue(); - }); - $('.ldap_action_back').click(function(event) { - event.preventDefault(); - LdapWizard.controlBack(); - }); - $('.ldap_action_test_connection').click(function(event){ - event.preventDefault(); - LdapConfiguration.testConfiguration( - //onSuccess - function(result) { - OC.dialogs.alert( - result.message, - t('user_ldap', 'Connection test succeeded') - ); - }, - //onError - function(result) { - OC.dialogs.alert( - result.message, - t('user_ldap', 'Connection test failed') - ); - } - ); - }); - - $('#ldap_action_delete_configuration').click(function(event) { - event.preventDefault(); - OC.dialogs.confirm( - t('user_ldap', 'Do you really want to delete the current Server Configuration?'), - t('user_ldap', 'Confirm Deletion'), - function(deleteConfiguration) { - if(deleteConfiguration) { - LdapConfiguration.deleteConfiguration(); - } - } - ); - }); - - $('.ldap_submit').click(function(event) { - event.preventDefault(); - $.post( - OC.filePath('user_ldap','ajax','setConfiguration.php'), - $('#ldap').serialize(), - function (result) { - bgcolor = $('.ldap_submit').css('background'); - if (result.status === 'success') { - //the dealing with colors is a but ugly, but the jQuery version in use has issues with rgba colors - $('.ldap_submit').css('background', '#fff'); - $('.ldap_submit').effect('highlight', {'color':'#A8FA87'}, 5000, function() { - $('.ldap_submit').css('background', bgcolor); - }); - //update the Label in the config chooser - caption = $('#ldap_serverconfig_chooser option:selected:first').text(); - pretext = '. Server: '; - caption = caption.slice(0, caption.indexOf(pretext) + pretext.length); - caption = caption + $('#ldap_host').val(); - $('#ldap_serverconfig_chooser option:selected:first').text(caption); - - } else { - $('.ldap_submit').css('background', '#fff'); - $('.ldap_submit').effect('highlight', {'color':'#E97'}, 5000, function() { - $('.ldap_submit').css('background', bgcolor); - }); - } - } - ); - }); - - $('#ldap_action_clear_user_mappings').click(function(event) { - event.preventDefault(); - LdapConfiguration.clearMappings('user'); - }); - - $('#ldap_action_clear_group_mappings').click(function(event) { - event.preventDefault(); - LdapConfiguration.clearMappings('group'); - }); - - $('#ldap_serverconfig_chooser').change(function(event) { - value = $('#ldap_serverconfig_chooser option:selected:first').attr('value'); - if(value === 'NEW') { - LdapConfiguration.addConfiguration(false); - } else { - LdapConfiguration.refreshConfig(); - } - }); - - expAdminCB = $('#ldap_experienced_admin'); - LdapWizard.admin = new ExperiencedAdmin(LdapWizard, expAdminCB.is(':checked')); - expAdminCB.change(function() { - LdapWizard.admin.setExperienced($(this).is(':checked')); - }); -}); diff --git a/apps/user_ldap/js/wizard/configModel.js b/apps/user_ldap/js/wizard/configModel.js new file mode 100644 index 0000000000..c3f1e85b59 --- /dev/null +++ b/apps/user_ldap/js/wizard/configModel.js @@ -0,0 +1,606 @@ +/** + * Copyright (c) 2015, Arthur Schiwon + * 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 0000000000..7c1f0d5d81 --- /dev/null +++ b/apps/user_ldap/js/wizard/controller.js @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2015, Arthur Schiwon + * 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 0000000000..8eb10c5801 --- /dev/null +++ b/apps/user_ldap/js/wizard/view.js @@ -0,0 +1,433 @@ +/** + * Copyright (c) 2015, Arthur Schiwon + * 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 0000000000..faa9de918a --- /dev/null +++ b/apps/user_ldap/js/wizard/wizard.js @@ -0,0 +1,80 @@ +/** + * Copyright (c) 2015, Arthur Schiwon + * 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 0000000000..f027235174 --- /dev/null +++ b/apps/user_ldap/js/wizard/wizardDetectorAvailableAttributes.js @@ -0,0 +1,59 @@ + +/** + * Copyright (c) 2015, Arthur Schiwon + * 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 0000000000..70b9923e58 --- /dev/null +++ b/apps/user_ldap/js/wizard/wizardDetectorBaseDN.js @@ -0,0 +1,52 @@ + +/** + * Copyright (c) 2015, Arthur Schiwon + * 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 0000000000..c6ef0a9cab --- /dev/null +++ b/apps/user_ldap/js/wizard/wizardDetectorClearGroupMappings.js @@ -0,0 +1,30 @@ + +/** + * Copyright (c) 2015, Arthur Schiwon + * 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 0000000000..0e4811b39e --- /dev/null +++ b/apps/user_ldap/js/wizard/wizardDetectorClearUserMappings.js @@ -0,0 +1,30 @@ + +/** + * Copyright (c) 2015, Arthur Schiwon + * 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 0000000000..5f17773468 --- /dev/null +++ b/apps/user_ldap/js/wizard/wizardDetectorEmailAttribute.js @@ -0,0 +1,38 @@ + +/** + * Copyright (c) 2015, Arthur Schiwon + * 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 0000000000..e025d8d624 --- /dev/null +++ b/apps/user_ldap/js/wizard/wizardDetectorFeatureAbstract.js @@ -0,0 +1,52 @@ + +/** + * Copyright (c) 2015, Arthur Schiwon + * 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 0000000000..cca889839e --- /dev/null +++ b/apps/user_ldap/js/wizard/wizardDetectorFilterGroup.js @@ -0,0 +1,31 @@ + +/** + * Copyright (c) 2015, Arthur Schiwon + * 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 0000000000..310f261e05 --- /dev/null +++ b/apps/user_ldap/js/wizard/wizardDetectorFilterLogin.js @@ -0,0 +1,32 @@ + +/** + * Copyright (c) 2015, Arthur Schiwon + * 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 0000000000..63dff4e298 --- /dev/null +++ b/apps/user_ldap/js/wizard/wizardDetectorFilterUser.js @@ -0,0 +1,31 @@ + +/** + * Copyright (c) 2015, Arthur Schiwon + * 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 0000000000..fd80018943 --- /dev/null +++ b/apps/user_ldap/js/wizard/wizardDetectorGeneric.js @@ -0,0 +1,117 @@ + +/** + * Copyright (c) 2015, Arthur Schiwon + * 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 0000000000..12d7df7514 --- /dev/null +++ b/apps/user_ldap/js/wizard/wizardDetectorGroupCount.js @@ -0,0 +1,27 @@ + +/** + * Copyright (c) 2015, Arthur Schiwon + * 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 0000000000..6d6048b798 --- /dev/null +++ b/apps/user_ldap/js/wizard/wizardDetectorGroupObjectClasses.js @@ -0,0 +1,29 @@ + +/** + * Copyright (c) 2015, Arthur Schiwon + * 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 0000000000..fbb3f02e10 --- /dev/null +++ b/apps/user_ldap/js/wizard/wizardDetectorGroupsForGroups.js @@ -0,0 +1,29 @@ + +/** + * Copyright (c) 2015, Arthur Schiwon + * 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 0000000000..fe67854c79 --- /dev/null +++ b/apps/user_ldap/js/wizard/wizardDetectorGroupsForUsers.js @@ -0,0 +1,29 @@ + +/** + * Copyright (c) 2015, Arthur Schiwon + * 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 0000000000..ba07518966 --- /dev/null +++ b/apps/user_ldap/js/wizard/wizardDetectorPort.js @@ -0,0 +1,44 @@ + +/** + * Copyright (c) 2015, Arthur Schiwon + * 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 0000000000..448dfc1803 --- /dev/null +++ b/apps/user_ldap/js/wizard/wizardDetectorQueue.js @@ -0,0 +1,89 @@ + +/** + * Copyright (c) 2015, Arthur Schiwon + * 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 0000000000..37e41f42a6 --- /dev/null +++ b/apps/user_ldap/js/wizard/wizardDetectorSimpleRequestAbstract.js @@ -0,0 +1,44 @@ + +/** + * Copyright (c) 2015, Arthur Schiwon + * 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 0000000000..df0b0a2200 --- /dev/null +++ b/apps/user_ldap/js/wizard/wizardDetectorTestAbstract.js @@ -0,0 +1,63 @@ + +/** + * Copyright (c) 2015, Arthur Schiwon + * 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 0000000000..52848819bd --- /dev/null +++ b/apps/user_ldap/js/wizard/wizardDetectorTestBaseDN.js @@ -0,0 +1,29 @@ + +/** + * Copyright (c) 2015, Arthur Schiwon + * 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 0000000000..1308c18290 --- /dev/null +++ b/apps/user_ldap/js/wizard/wizardDetectorTestConfiguration.js @@ -0,0 +1,31 @@ + +/** + * Copyright (c) 2015, Arthur Schiwon + * 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 0000000000..cf992df43b --- /dev/null +++ b/apps/user_ldap/js/wizard/wizardDetectorTestLoginName.js @@ -0,0 +1,30 @@ + +/** + * Copyright (c) 2015, Arthur Schiwon + * 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 0000000000..bcff2cf3b1 --- /dev/null +++ b/apps/user_ldap/js/wizard/wizardDetectorUserCount.js @@ -0,0 +1,26 @@ + +/** + * Copyright (c) 2015, Arthur Schiwon + * 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 0000000000..ae734480c1 --- /dev/null +++ b/apps/user_ldap/js/wizard/wizardDetectorUserDisplayNameAttribute.js @@ -0,0 +1,39 @@ + +/** + * Copyright (c) 2015, Arthur Schiwon + * 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 0000000000..953a0b909a --- /dev/null +++ b/apps/user_ldap/js/wizard/wizardDetectorUserGroupAssociation.js @@ -0,0 +1,40 @@ + +/** + * Copyright (c) 2015, Arthur Schiwon + * 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 0000000000..0fa324a080 --- /dev/null +++ b/apps/user_ldap/js/wizard/wizardDetectorUserObjectClasses.js @@ -0,0 +1,29 @@ + +/** + * Copyright (c) 2015, Arthur Schiwon + * 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 0000000000..f3f1368982 --- /dev/null +++ b/apps/user_ldap/js/wizard/wizardFilterOnType.js @@ -0,0 +1,76 @@ +/** + * Copyright (c) 2015, Arthur Schiwon + * 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($('