123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642 |
- /**
- * SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors
- * SPDX-FileCopyrightText: 2015-2016 ownCloud, Inc.
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
- OCA = OCA || {};
- (function() {
- /**
- * @classdesc An abstract tab view
- * @abstract
- */
- var WizardTabGeneric = OCA.LDAP.Wizard.WizardObject.subClass({
- isActive: false,
- /**
- * @property {string} - class that identifies a multiselect-plugin
- * control.
- */
- multiSelectPluginClass: 'multiSelectPlugin',
- /**
- * @property {string} - class that identifies a multiselect-plugin
- * control.
- */
- bjQuiButtonClass: 'ui-button',
- /**
- * @property {boolean} - indicates whether a filter mode toggle operation
- * is still in progress
- */
- isToggling: false,
- /** @inheritdoc */
- init: function(tabIndex, tabID) {
- this.tabIndex = tabIndex;
- this.tabID = tabID;
- this.spinner = $('.ldapSpinner').first().clone().removeClass('hidden');
- _.bindAll(this, '_toggleRawFilterMode', '_toggleRawFilterModeConfirmation');
- },
- /**
- * sets the configuration items that are managed by that view.
- *
- * The parameter contains key-value pairs the key being the
- * configuration keys and the value being its setter method.
- *
- * @param {object} managedItems
- */
- setManagedItems: function(managedItems) {
- this.managedItems = managedItems;
- this._enableAutoSave();
- this._enableSaveButton();
- },
- /**
- * Sets the config model. The concrete view likely wants to subscribe
- * to events as well.
- *
- * @param {OCA.LDAP.Wizard.ConfigModel} configModel
- */
- setModel: function(configModel) {
- this.configModel = configModel;
- this.parsedFilterMode = this.configModel.FILTER_MODE_ASSISTED;
- this.configModel.on('detectionStarted', this.onDetectionStarted, this);
- this.configModel.on('detectionCompleted', this.onDetectionCompleted, this);
- this.configModel.on('serverError', this.onServerError, this);
- this.configModel.on('setCompleted', this.onItemSaved, this);
- this.configModel.on('configUpdated', this.onConfigLoaded, this);
- },
- /**
- * the method can be used to display a different error/information
- * message than provided by the Nextcloud server response. The concrete
- * Tab View may optionally implement it. Returning an empty string will
- * avoid any notification.
- *
- * @param {string} message
- * @param {string} key
- * @returns {string}
- */
- overrideErrorMessage: function(message, key) {
- if(message === 'LDAP authentication method rejected'
- && !this.configModel.configuration.ldap_dn)
- {
- message = t('user_ldap', 'Anonymous bind is not allowed. Please provide a User DN and Password.');
- } else if (message === 'LDAP Operations error'
- && !this.configModel.configuration.ldap_dn
- && !this.configModel.configuration.ldap_agent_password)
- {
- message = t('user_ldap', 'LDAP Operations error. Anonymous bind might not be allowed.');
- }
- return message;
- },
- /**
- * this is called by the main view, if the tab is being switched to.
- */
- onActivate: function() {
- if(!_.isUndefined(this.filterModeKey)
- && this.configModel.configuration.ldap_experienced_admin === '1') {
- this.setFilterMode(this.configModel.FILTER_MODE_RAW);
- }
- },
- /**
- * updates the tab when the model loaded a configuration and notified
- * this view.
- *
- * @param {WizardTabGeneric} view - this instance
- * @param {Object} configuration
- */
- onConfigLoaded: function(view, configuration) {
- for(var key in view.managedItems){
- if(!_.isUndefined(configuration[key])) {
- var value = configuration[key];
- var methodName = view.managedItems[key].setMethod;
- if(!_.isUndefined(view[methodName])) {
- view[methodName](value);
- }
- }
- }
- },
- /**
- * reacts on a set action on the model and updates the tab with the
- * valid value.
- *
- * @param {WizardTabGeneric} view
- * @param {Object} result
- */
- onItemSaved: function(view, result) {
- if(!_.isUndefined(view.managedItems[result.key])) {
- var methodName = view.managedItems[result.key].setMethod;
- view[methodName](result.value);
- if(!result.isSuccess) {
- OC.Notification.showTemporary(t('user_ldap', 'Saving failed. Please make sure the database is in Operation. Reload before continuing.'));
- console.warn(result.errorMessage);
- }
- }
- },
- /**
- * displays server error messages.
- *
- * @param {any} view -
- * @param {any} payload -
- */
- onServerError: function(view, payload) {
- if ( !_.isUndefined(view.managedItems[payload.relatedKey])) {
- var message = view.overrideErrorMessage(payload.message, payload.relatedKey);
- if(message) {
- OC.Notification.showTemporary(message);
- }
- }
- },
- /**
- * disables affected, managed fields if a detector is running against them
- *
- * @param {WizardTabGeneric} view
- * @param {string} key
- */
- onDetectionStarted: function(view, key) {
- if(!_.isUndefined(view.managedItems[key])) {
- view.disableElement(view.managedItems[key].$element);
- if(!_.isUndefined(view.managedItems[key].$relatedElements)){
- view.disableElement(view.managedItems[key].$relatedElements);
- }
- view.attachSpinner(view.managedItems[key].$element.attr('id'));
- }
- },
- /**
- * enables affected, managed fields after a detector was run against them
- *
- * @param {WizardTabGeneric} view
- * @param {string} key
- */
- onDetectionCompleted: function(view, key) {
- if(!_.isUndefined(view.managedItems[key])) {
- view.enableElement(view.managedItems[key].$element);
- if(!_.isUndefined(view.managedItems[key].$relatedElements)){
- view.enableElement(view.managedItems[key].$relatedElements);
- }
- view.removeSpinner(view.managedItems[key].$element.attr('id'));
- }
- },
- /**
- * sets the value to an HTML element. Checkboxes, text areas and (text)
- * input fields are supported.
- *
- * @param {jQuery} $element - the target element
- * @param {string|number|Array} value
- */
- setElementValue: function($element, value) {
- // deal with check box
- if ($element.is('input[type=checkbox]')) {
- this._setCheckBox($element, value);
- return;
- }
- // special cases: deal with text area and multiselect
- if ($element.is('textarea') && $.isArray(value)) {
- value = value.join("\n");
- } else if($element.hasClass(this.multiSelectPluginClass)) {
- if(!_.isArray(value)) {
- value = value.split("\n");
- }
- }
- if ($element.is('span')) {
- $element.text(value);
- } else {
- $element.val(value);
- }
- },
- /**
- * replaces options on a multiselect element
- *
- * @param {jQuery} $element - the multiselect element
- * @param {Array} options
- */
- equipMultiSelect: function($element, options) {
- if($element.find('option').length === 0) {
- $element.empty();
- for (var i in options) {
- var name = options[i];
- $element.append($('<option>').val(name).text(name).attr('title', name));
- }
- }
- if(!$element.hasClass('ldapGroupList')) {
- $element.multiselect('refresh');
- this.enableElement($element);
- }
- },
- /**
- * enables the specified HTML element
- *
- * @param {jQuery} $element
- */
- enableElement: function($element) {
- var isMS = $element.is('select[multiple]');
- var hasOptions = isMS ? ($element.find('option').length > 0) : false;
- if($element.hasClass(this.multiSelectPluginClass) && hasOptions) {
- $element.multiselect("enable");
- } else if ($element.hasClass(this.bjQuiButtonClass)) {
- $element.button("enable");
- }
- else if(!isMS || (isMS && hasOptions)) {
- $element.prop('disabled', false);
- }
- },
- /**
- * disables the specified HTML element
- *
- * @param {jQuery} $element
- */
- disableElement: function($element) {
- if($element.hasClass(this.multiSelectPluginClass)) {
- $element.multiselect("disable");
- } else if ($element.hasClass(this.bjQuiButtonClass)) {
- $element.button("disable");
- } else {
- $element.prop('disabled', 'disabled');
- }
- },
- /**
- * attaches a spinner icon to the HTML element specified by ID
- *
- * @param {string} elementID
- */
- attachSpinner: function(elementID) {
- if($('#' + elementID + ' + .ldapSpinner').length == 0) {
- var spinner = this.spinner.clone();
- var $element = $('#' + elementID);
- $(spinner).insertAfter($element);
- // and special treatment for multiselects:
- if ($element.is('select[multiple]')) {
- $('#' + elementID + " + img + button").css('display', 'none');
- }
- }
- },
- /**
- * removes the spinner icon from the HTML element specified by ID
- *
- * @param {string} elementID
- */
- removeSpinner: function(elementID) {
- $('#' + elementID+' + .ldapSpinner').remove();
- // and special treatment for multiselects:
- $('#' + elementID + " + button").css('display', 'inline');
- },
- /**
- * whether the wizard works in experienced admin mode
- *
- * @returns {boolean}
- */
- isExperiencedMode: function() {
- return parseInt(this.configModel.configuration.ldap_experienced_admin, 10) === 1;
- },
- /**
- * sets up auto-save functionality to the managed items
- *
- * @private
- */
- _enableAutoSave: function() {
- var view = this;
- for(var id in this.managedItems) {
- if(_.isUndefined(this.managedItems[id].$element)
- || _.isUndefined(this.managedItems[id].setMethod)
- || (!_.isUndefined(this.managedItems[id].preventAutoSave)
- && this.managedItems[id].preventAutoSave === true)
- ) {
- continue;
- }
- var $element = this.managedItems[id].$element;
- if (!$element.is('select[multiple]')) {
- $element.change(function() {
- view._requestSave($(this));
- });
- }
- }
- },
- /**
- * set's up save-button behavior (essentially used for agent dn and pwd)
- *
- * @private
- */
- _enableSaveButton: function() {
- var view = this;
- // TODO: this is not nice, because it fires one request per change
- // in the scenario this happens twice, causes detectors to run
- // duplicated etc. To have this work properly, the wizard endpoint
- // must accept setting multiple changes. Instead of messing around
- // with old ajax/wizard.php use this opportunity and create a
- // Controller
- for(var id in this.managedItems) {
- if(_.isUndefined(this.managedItems[id].$element)
- || _.isUndefined(this.managedItems[id].$saveButton)
- ) {
- continue;
- }
- (function (item) {
- item.$saveButton.click(function(event) {
- event.preventDefault();
- view._requestSave(item.$element);
- item.$saveButton.removeClass('primary');
- });
- item.$element.change(function () {
- item.$saveButton.addClass('primary');
- });
- })(this.managedItems[id]);
- }
- },
- /**
- * initializes a multiSelect element
- *
- * @param {jQuery} $element
- * @param {string} caption
- * @private
- */
- _initMultiSelect: function($element, caption) {
- var view = this;
- $element.multiselect({
- header: false,
- selectedList: 9,
- noneSelectedText: caption,
- classes: this.multiSelectPluginClass,
- close: function() {
- view._requestSave($element);
- }
- });
- },
- /**
- * @typedef {object} viewSaveInfo
- * @property {Function} val
- * @property {Function} attr
- * @property {Function} is
- */
- /**
- * requests a save operation from the model for a given value
- * represented by a HTML element and its ID.
- *
- * @param {jQuery|viewSaveInfo} $element
- * @private
- */
- _requestSave: function($element) {
- var value = '';
- if($element.is('input[type=checkbox]')
- && !$element.is(':checked')) {
- value = 0;
- } else if ($element.is('select[multiple]')) {
- var entries = $element.multiselect("getChecked");
- for(var i = 0; i < entries.length; i++) {
- value = value + "\n" + entries[i].value;
- }
- value = $.trim(value);
- } else {
- value = $element.val();
- }
- this.configModel.set($element.attr('id'), value);
- },
- /**
- * updates a checkbox element according to the provided value
- *
- * @param {jQuery} $element
- * @param {string|number} value
- * @private
- */
- _setCheckBox: function($element, value) {
- if(parseInt(value, 10) === 1) {
- $element.prop('checked', 'checked');
- } else {
- $element.removeAttr('checked');
- }
- },
- /**
- * this is called when the filter mode is switched to assisted. The
- * concrete tab view should implement this, to load LDAP features
- * (e.g. object classes, groups, attributes…), if necessary.
- */
- considerFeatureRequests: function() {},
- /**
- * this is called when the filter mode is switched to Assisted. The
- * concrete tab view should request the compilation of the respective
- * filter.
- */
- requestCompileFilter: function() {
- this.configModel.requestWizard(this.filterName);
- },
- /**
- * sets the filter mode initially and resets the "isToggling" marker.
- * This method is called after a save operation against the mode key.
- *
- * @param {any} mode -
- */
- setFilterModeOnce: function(mode) {
- this.isToggling = false;
- if(!this.filterModeInitialized) {
- this.filterModeInitialized = true;
- this.setFilterMode(mode);
- }
- },
- /**
- * sets the filter mode according to the provided configuration value
- *
- * @param {string} mode
- */
- setFilterMode: function(mode) {
- if(parseInt(mode, 10) === this.configModel.FILTER_MODE_ASSISTED) {
- this.parsedFilterMode = this.configModel.FILTER_MODE_ASSISTED;
- this.considerFeatureRequests();
- this._setFilterModeAssisted();
- if(this.isActive) {
- // filter compilation should happen only, if the mode was
- // switched manually, but not when initiating the view
- this.requestCompileFilter();
- }
- } else {
- this._setFilterModeRaw();
- this.parsedFilterMode = this.configModel.FILTER_MODE_RAW;
- }
- },
- /**
- * updates the UI so that it represents the assisted mode setting
- *
- * @private
- */
- _setFilterModeAssisted: function() {
- var view = this;
- this.$filterModeRawContainer.addClass('invisible');
- var filter = this.$filterModeRawContainer.find('.ldapFilterInputElement').val();
- this.$filterModeRawContainer.siblings('.ldapReadOnlyFilterContainer').find('.ldapFilterReadOnlyElement').text(filter);
- this.$filterModeRawContainer.siblings('.ldapReadOnlyFilterContainer').removeClass('hidden');
- $.each(this.filterModeDisableableElements, function(i, $element) {
- view.enableElement($element);
- });
- if(!_.isUndefined(this.filterModeStateElement)) {
- if (this.filterModeStateElement.status === 'enabled') {
- this.enableElement(this.filterModeStateElement.$element);
- } else {
- this.filterModeStateElement.status = 'disabled';
- }
- }
- },
- /**
- * updates the UI so that it represents the raw mode setting
- *
- * @private
- */
- _setFilterModeRaw: function() {
- var view = this;
- this.$filterModeRawContainer.removeClass('invisible');
- this.$filterModeRawContainer.siblings('.ldapReadOnlyFilterContainer').addClass('hidden');
- $.each(this.filterModeDisableableElements, function (i, $element) {
- view.disableElement($element);
- });
- if(!_.isUndefined(this.filterModeStateElement)) {
- if(this.filterModeStateElement.$element.multiselect().attr('disabled') === 'disabled') {
- this.filterModeStateElement.status = 'disabled';
- } else {
- this.filterModeStateElement.status = 'enabled';
- }
- }
- if(!_.isUndefined(this.filterModeStateElement)) {
- this.disableElement(this.filterModeStateElement.$element);
- }
- },
- /**
- * @callback toggleConfirmCallback
- * @param {boolean} isConfirmed
- */
- /**
- * shows a confirmation dialogue before switching from raw to assisted
- * mode if experienced mode is enabled.
- *
- * @param {toggleConfirmCallback} toggleFnc
- * @private
- */
- _toggleRawFilterModeConfirmation: function(toggleFnc) {
- if( !this.isExperiencedMode()
- || this.parsedFilterMode === this.configModel.FILTER_MODE_ASSISTED
- ) {
- toggleFnc(true);
- } else {
- OC.dialogs.confirm(
- t('user_ldap', 'Switching the mode will enable automatic LDAP queries. Depending on your LDAP size they may take a while. Do you still want to switch the mode?'),
- t('user_ldap', 'Mode switch'),
- toggleFnc
- );
- }
- },
- /**
- * toggles the visibility of a raw filter container and so also the
- * state of the multi-select controls. The model is requested to save
- * the state.
- */
- _toggleRawFilterMode: function() {
- var view = this;
- this._toggleRawFilterModeConfirmation(function(isConfirmed) {
- if(!isConfirmed) {
- return;
- }
- /** var {number} */
- var mode;
- if (view.parsedFilterMode === view.configModel.FILTER_MODE_ASSISTED) {
- mode = view.configModel.FILTER_MODE_RAW;
- } else {
- mode = view.configModel.FILTER_MODE_ASSISTED;
- }
- view.setFilterMode(mode);
- /** @var {viewSaveInfo} */
- var saveInfo = {
- val: function () {
- return mode;
- },
- attr: function () {
- return view.filterModeKey;
- },
- is: function () {
- return false;
- }
- };
- view._requestSave(saveInfo);
- });
- },
- /**
- * @typedef {object} filterModeStateElementObj
- * @property {string} status - either "enabled" or "disabled"
- * @property {jQuery} $element
- */
- /**
- * initializes a raw filter mode switcher
- *
- * @param {jQuery} $switcher - the element receiving the click
- * @param {jQuery} $filterModeRawContainer - contains the raw filter
- * input elements
- * @param {jQuery[]} filterModeDisableableElements - an array of elements
- * not belonging to the raw filter part that shall be en/disabled.
- * @param {string} filterModeKey - the setting key that save the state
- * of the mode
- * @param {filterModeStateElementObj} [filterModeStateElement] - one element
- * which status (enabled or not) is tracked by a setting
- * @private
- */
- _initFilterModeSwitcher: function(
- $switcher,
- $filterModeRawContainer,
- filterModeDisableableElements,
- filterModeKey,
- filterModeStateElement
- ) {
- this.$filterModeRawContainer = $filterModeRawContainer;
- this.filterModeDisableableElements = filterModeDisableableElements;
- this.filterModeStateElement = filterModeStateElement;
- this.filterModeKey = filterModeKey;
- var view = this;
- $switcher.click(function() {
- if(view.isToggling) {
- return;
- }
- view.isToggling = true;
- view._toggleRawFilterMode();
- });
- },
- });
- OCA.LDAP.Wizard.WizardTabGeneric = WizardTabGeneric;
- })();
|