/*
* Copyright 2015 Anyware Services
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* This is a helper to select one or more users from the user manager. See {@link #act} method
*
* Ametys.helper.SelectUser.act({
* callback: Ext.bind(function (users) { console.info(users[0].login + ', ' + users[0].populationId); }, this),
* allowMultiselection: true,
* });
*/
Ext.define('Ametys.helper.SelectUser', {
singleton: true,
/**
* @property {Number} RESULT_LIMIT
* @readonly
* The maximum number of records to search for
*/
RESULT_LIMIT: 100,
/**
* @private
* @property {Boolean} _initialized Determines if the dialog box have been already initialized or not
*/
_initialized: false,
/**
* @property {Boolean} allowMultiselection Property to enable or disable multiselection
*/
/**
* @property {String} [pluginName="core"] The name of the currently selected plugin to use for requests. Selected by the {@link #act} call.
*/
/**
* @property {String} [url="users/search.json"] The url of the currently selected plugin to use for requests. Selected by the {@link #act} call.
*/
/**
* @property {String[]} [contexts] The contexts for the populations to display in the combobox
*/
/**
* @property {Boolean} _enableAllPopulationsOption True to add an option in the populations combobx for searching over all the populations.
* @private
*/
/**
* @property {String} _noPopulationMessage The message to display when there is no user population available for the context.
* @private
*/
/**
* @property {String} _allPopulationsOptionId The id of the 'all populations' options.
* @private
* @readonly
*/
_allPopulationsOptionId: '#all',
/**
* @property {String} _anyDirectoryOptionId The id of the '-' options.
* @private
* @readonly
*/
_anyDirectoryOptionId: '-',
/**
* @private
* @property {Function} callBack The current callback function registered by the {@link #act} call
*/
/**
* @private
* @property {Function} cancelCallback The current cancel callback function registered by the {@link #act} call
*/
/**
* @private
* @property {Ext.form.field.ComboBox} _userPopulationsField The combobox of the dialog box that displays the user populations where to search
*/
/**
* @private
* @property {Ext.form.field.ComboBox} _userDirectoriesField The combobox of the dialog box that displays the user directories where to search
*/
/**
* @private
* @property {Ext.form.field.Text} _searchField The field of the dialog box that displays the filter field
*/
/**
* @private
* @property {Ametys.window.DialogBox} _box The re-usable dialog box
*/
/**
* @private
* @property {Ext.grid.Panel} _userList The grid of result users
*/
/**
* Open the dialog box to select a user
* @param {Object} config The configuration options:
* @param {Function} config.callback The callback function to call when user(s) has(have) been selected
* @param {Object[]} config.callback.users An array of users.
* @param {String} config.callback.users.login The user's login
* @param {String} config.callback.users.populationId The user's population id
* @param {String} config.callback.users.populationName The user's population name
* @param {String} config.callback.users.fullName The user's fullname
* @param {Function} config.cancelCallback The callback function if the user cancel the dialog box. Can be null.
* @param {Boolean} [config.allowMultiselection=true] Set to false to disable multiple selection of users.
* @param {String} [config.plugin=core] The plugin to use for search request.
* @param {String} [config.url=users/search.json] The url to use for search request.
* @param {String/String[]} [config.contexts] The contexts for the populations to display in the combobox. Default to the current contexts.
* @param {Boolean} [config.enableAllPopulationsOption=true] True to add an option in the populations combobx for searching over all the populations.
* @param {String} [config.noPopulationMessage] The message to display when there is no user population available for the contexts. There is a default message if not provided.
* @param {Boolean} [config.showDirectoryCombobox] True to show the user directory combobox field (then it is possible to filter with user directories).
*/
act: function (config)
{
if (config.url)
{
throw new Error("The config parameter 'url' is not supported anymore");
}
config = config || {};
this.callback = config.callback || function () {};
this.cancelCallback = config.cancelCallback || function () {};
this.allowMultiselection = config.allowMultiselection || true;
this.pluginName = config.plugin || 'core';
this.contexts = Ext.Array.from(config.contexts || Ametys.getAppParameter('populationContexts'));
this._enableAllPopulationsOption = config.enableAllPopulationsOption !== false;
this._noPopulationMessage = config.noPopulationMessage || "{{i18n PLUGINS_CORE_UI_USERS_SELECTUSER_DIALOG_NO_POPULATION_DESCRIPTION}}";
this._delayedInitialize();
this._userPopulationsField.clearValue();
this._userDirectoriesField.setVisible(config.showDirectoryCombobox == "true");
this._userDirectoriesField.getStore().loadData([], false);
this._searchField.setValue("");
this._userList.getStore().loadData([], false);
this._userList.getSelectionModel().setSelectionMode(this.allowMultiselection ? 'SIMPLE' : 'SINGLE');
this._userList.getSelectionModel().deselectAll();
this._userList.getStore().setProxy ({
type: 'ametys',
role: "org.ametys.plugins.core.user.UserDAO",
// No method name because we don't always call the same one
cancelOutdatedRequest: true,
reader: {
type: 'json',
rootProperty: 'users'
},
extraParams: {
limit: this.RESULT_LIMIT
}
});
this._box.show();
this.loadPopulations();
},
/**
* @private
* This method is called to initialize the dialog box. Only the first call will be taken in account.
*/
_delayedInitialize: function ()
{
if (this._initialized)
{
return true;
}
this._initialized = true;
this._userPopulationsField = Ext.create('Ext.form.field.ComboBox', {
fieldLabel: "{{i18n PLUGINS_CORE_UI_USERS_SELECTUSER_DIALOG_POPULATION}}",
name: "userPopulations",
cls: 'ametys',
labelWidth: 150,
store: {
fields: ['id', {name: 'label', type: 'string'}],
proxy: {
type: 'ametys',
plugin: 'core-ui',
url: 'populations.json',
reader: {
type: 'json',
rootProperty: 'userPopulations'
}
},
sorters: [{property: 'label', direction: 'ASC'}],
listeners: {
'beforeload': {fn: this._onBeforeLoadPopulations, scope: this},
'load': {fn: this._onLoadPopulations, scope: this}
}
},
valueField: 'id',
displayField: 'label',
queryMode: 'local',
forceSelection: true,
triggerAction: 'all',
listeners: {
'change': {fn: this._onChangePopulation, scope: this}
}
});
this._userDirectoriesField = Ext.create('Ext.form.field.ComboBox', {
fieldLabel: "{{i18n PLUGINS_CORE_UI_USERS_SELECTUSER_DIALOG_USER_DIRECTORY}}",
name: "userDirectories",
cls: 'ametys',
labelWidth: 150,
hidden: true, // hidden by default, will be shown if told
store: {
fields: ['id', {name: 'label', type: 'string'}],
data: [],
sorters: [{property: 'label', direction: 'ASC'}],
listeners: {
'datachanged': Ext.bind(function(store) {
this._userDirectoriesField.clearValue();
this._userDirectoriesField.setValue(this._anyDirectoryOptionId);
}, this)
}
},
valueField: 'id',
displayField: 'label',
queryMode: 'local',
forceSelection: true,
triggerAction: 'all',
listeners: {change: Ext.Function.createBuffered(this.loadUsers, 500, this)}
});
this._searchField = Ext.create('Ext.form.TextField', {
fieldLabel: "{{i18n PLUGINS_CORE_UI_USERS_SELECTUSER_DIALOG_FIND}}",
name: "criteria",
cls: 'ametys',
labelWidth: 150,
value: "",
listeners: {change: Ext.Function.createBuffered(this.loadUsers, 500, this)}
});
var model = Ext.define('Ametys.helper.SelectUser.Users', {
extend: 'Ext.data.Model',
fields: [
{
name: 'id',
calculate: function (data)
{
return data.login + '#' + data.populationId;
}
},
{name: 'login'},
{name: 'populationId'},
{name: 'populationLabel'},
{name: 'lastname', type: 'string'},
{name: 'firstname', type: 'string'},
{name: 'email'},
{name: 'fullname', type: 'string'},
{name: 'sortablename', type: 'string'},
{
name: 'displayName',
type: 'string',
calculate: function (data)
{
return Ametys.helper.Users.renderUser(data.login, data.populationLabel, data.sortablename);
}
}
]
});
var store = Ext.create('Ext.data.Store', {
model: 'Ametys.helper.SelectUser.Users',
data: { users: []},
listeners: {
'beforeload': Ext.bind(this._onBeforeLoad, this),
'load': Ext.bind(this._onLoad, this)
},
remoteSort: false,
sortOnLoad: true,
sorters: [{property: 'displayName', direction:'ASC'}]
});
this._userList = Ext.create('Ext.grid.Panel', {
flex: 1,
store : store,
hideHeaders : true,
columns: [{header: "Label", flex: 1, menuDisabled : true, sortable: true, dataIndex: 'displayName', renderer: Ametys.grid.GridColumnHelper.renderUser}]
});
this._box = Ext.create('Ametys.window.DialogBox', {
title : this.allowMultiselection ? "{{i18n PLUGINS_CORE_UI_USERS_SELECTUSERS_DIALOG_CAPTION}}" : "{{i18n PLUGINS_CORE_UI_USERS_SELECTUSER_DIALOG_CAPTION}}",
//icon: Ametys.getPluginResourcesPrefix('core-ui') + '/img/users/user_16.png',
iconCls: 'ametysicon-black302',
layout: {
type: 'vbox',
align : 'stretch',
pack : 'start'
},
width: 450,
height: 600,
items : [
this._userPopulationsField,
this._userDirectoriesField,
this._searchField,
this._userList,
{
xtype: 'container',
style: {
textAlign: 'center'
},
cls: 'a-text-warning',
html: "{{i18n PLUGINS_CORE_UI_USERS_SELECTUSER_DIALOG_WARN100}}"
}
],
defaultFocus: this._searchField,
closeAction: 'hide',
defaultButton: 'validate',
referenceHolder: true,
buttons : [{
reference: 'validate',
text: "{{i18n PLUGINS_CORE_UI_USERS_SELECTUSER_DIALOG_OK}}",
handler: Ext.bind(this.ok, this)
}, {
text: "{{i18n PLUGINS_CORE_UI_USERS_SELECTUSER_DIALOG_CANCEL}}",
handler: Ext.bind(this.cancel, this)
} ]
});
},
/**
* Function called before loading the population store
* @param {Ext.data.Store} store The store
* @param {Ext.data.operation.Operation} operation The object that will be passed to the Proxy to load the store
* @private
*/
_onBeforeLoadPopulations: function(store, operation)
{
operation.setParams( Ext.apply(operation.getParams() || {}, {
contexts: this.contexts
}));
},
/**
* @private
* Listener invoked after loading populations
* @param {Ext.data.Store} store The store
* @param {Ext.data.Model[]} records The records of the store
*/
_onLoadPopulations: function(store, records)
{
if (records.length == 0)
{
Ametys.Msg.show({
title: "{{i18n PLUGINS_CORE_UI_USERS_SELECTUSER_DIALOG_NO_POPULATION_TITLE}}",
msg: this._noPopulationMessage,
buttons: Ext.Msg.OK,
icon: Ext.MessageBox.WARNING,
fn: Ext.bind(function() {
this._box.close();
}, this)
});
return;
}
if (this._enableAllPopulationsOption)
{
// Add an option in the populations combobox for searching over all the populations
store.add({
id: this._allPopulationsOptionId,
label: "{{i18n PLUGINS_CORE_UI_USERS_SELECTUSER_DIALOG_POPULATION_OPTION_ALL}}"
});
}
},
/**
* @private
* Function called when the value of the population combobox field changed.
* @param {Ext.form.field.ComboBox} combo The combobox
* @param {String} newValue The new value
* @param {String} oldValue The original value
*/
_onChangePopulation: function(combo, newValue, oldValue)
{
if (newValue == this._allPopulationsOptionId)
{
// Search over all the populations
this._userDirectoriesField.setDisabled(true);
Ext.defer(this.loadUsers, 500, this);
return;
}
else
{
this._userDirectoriesField.setDisabled(false);
}
// Populate the user directories combobox store
var data = [{
id: this._anyDirectoryOptionId,
label: "{{i18n PLUGINS_CORE_UI_USERS_SELECTUSER_DIALOG_USER_DIRECTORY_OPTION_ALL}}"
}];
var record = combo.getStore().getById(newValue);
if (record)
{
Ext.Array.forEach(record.get('userDirectories'), function(item, index) {
data.push({
id: item.id,
label: item.label
});
}, this);
this._userDirectoriesField.getStore().loadData(data, false);
}
},
/**
* Load the store of the populations combobox.
*/
loadPopulations: function()
{
this._userPopulationsField.getStore().load({
scope: this,
callback: function(records) {
// When store loaded, select the 'all' option if available
if (this._enableAllPopulationsOption)
{
this._userPopulationsField.select(this._allPopulationsOptionId);
}
// Otherwise select the fist data
else if (records.length > 0)
{
this._userPopulationsField.select(records[0].get('id'));
}
// If there is one and only one population, hide the combobox
this._userPopulationsField.setHidden(records.length == 1);
}
});
},
/**
* This method is called to apply the current filter immediately
* @private
*/
loadUsers: function ()
{
this._userList.getStore().load();
},
/**
* Function called before loading the store
* @param {Ext.data.Store} store The store
* @param {Ext.data.operation.Operation} operation The object that will be passed to the Proxy to load the store
* @param {Object} eOpts Event options
* @private
*/
_onBeforeLoad: function(store, operation, eOpts)
{
// If one of the two comboboxes is invalid, cancel the loading
if (this._userPopulationsField.getValue() == null
|| this._userPopulationsField.getValue() != this._allPopulationsOptionId && this._userDirectoriesField.getValue() == null)
{
return false;
}
var proxy = operation.getProxy();
// 'all' option is selected
if (this._userPopulationsField.getValue() == this._allPopulationsOptionId)
{
proxy.methodName = "searchUsersByContexts";
proxy.methodArguments = ['contexts', 'limit', 'offset', 'searchCriteria', 'limitedToStoredUserData'];
proxy.setExtraParam('contexts', this.contexts);
proxy.setExtraParam('searchCriteria', this._searchField.getValue());
return true;
}
// If no user directory is requested ('-'), search by population
else if (this._userDirectoriesField.getValue() == this._anyDirectoryOptionId)
{
proxy.methodName = "searchUsersByPopulation";
proxy.methodArguments = ['userPopulationId', 'limit', 'offset', 'searchCriteria', 'limitedToStoredUserData'];
proxy.setExtraParam('userPopulationId', this._userPopulationsField.getValue());
proxy.setExtraParam('searchCriteria', this._searchField.getValue());
}
else // If a user directory is requested, search in this directory
{
proxy.methodName = "searchUsersByDirectory";
proxy.methodArguments = ['userDirectoryId', 'userPopulationId', 'limit', 'offset', 'searchCriteria', 'limitedToStoredUserData'];
proxy.setExtraParam('userDirectoryId', this._userDirectoriesField.getValue());
proxy.setExtraParam('userPopulationId', this._userPopulationsField.getValue());
proxy.setExtraParam('searchCriteria', this._searchField.getValue());
}
},
/**
* Function called after loading the store
* @param {Ext.data.Store} store The store
* @param {Ext.data.Model[]} records The loaded records
*/
_onLoad: function (store, records)
{
if (records != null && records.length == 0)
{
Ametys.Msg.show({
title: "{{i18n PLUGINS_CORE_UI_USERS_SELECTUSER_DIALOG_CAPTION}}",
msg: "{{i18n PLUGINS_CORE_UI_USERS_SELECTUSER_DIALOG_NORESULT}}",
buttons: Ext.Msg.OK,
icon: Ext.MessageBox.INFO
});
}
},
/**
* @private
* The method called when the user push the ok button of the dialog box
*/
ok: function ()
{
var addedusers = [];
var selection = this._userList.getSelectionModel().getSelection();
if (selection.length == 0)
{
Ametys.Msg.show({
title: this.allowMultiselection ? "{{i18n PLUGINS_CORE_UI_USERS_SELECTUSERS_DIALOG_CAPTION}}" : "{{i18n PLUGINS_CORE_UI_USERS_SELECTUSER_DIALOG_CAPTION}}",
msg: "{{i18n PLUGINS_CORE_UI_USERS_SELECTUSER_DIALOG_ERROR_EMPTY}}",
buttons: Ext.Msg.OK,
icon: Ext.MessageBox.INFO
});
return;
}
this._box.hide();
for (var i=0; i < selection.length; i++)
{
var opt = selection[i];
addedusers.push({
login: opt.get('login'),
populationId: opt.get('populationId'),
fullName: opt.get('sortablename'),
populationName: opt.get('populationLabel')
});
}
this.callback(addedusers);
},
/**
* @private
* The method called when the user cancel the dialog box
*/
cancel: function ()
{
this._box.hide();
this.cancelCallback();
},
});