/*
* Copyright 2013 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.
*/
/**
* Helper for editing a user
* @private
*/
Ext.define('Ametys.plugins.coreui.users.EditUserHelper', {
singleton: true,
/**
* @property {Boolean} _chooseUserDirectoryInitialized True if the dialog box for choosing user directory is initialized.
* @private
*/
/**
* @property {String} _mode The current edition mode ('new' or 'edit')
* @private
*/
/**
* @property {String} _usersMessageTargetType The type of message target for user
* @private
*/
/**
* @property {Ametys.window.DialogBox} _chooseUserDirectoryDialog The dialog box for choosing the population and the user directory before creating a user.
* @private
*/
/**
* @property {Ametys.window.DialogBox} _box The dialog box for creating/editing a user.
* @private
*/
/**
* @property {Ext.XTemplate} _importResultTpl Template for result of import
* @private
*/
_importResultTpl: Ext.create('Ext.XTemplate',
"{{i18n PLUGINS_CORE_UI_USERS_DIALOG_IMPORT_SUCCESS_MESSAGE_INTRO}}",
"<ul>",
"<li>{[this.format('{{i18n PLUGINS_CORE_UI_USERS_DIALOG_IMPORT_SUCCESS_MESSAGE_ADDED}}', '{{i18n PLUGINS_CORE_UI_USERS_DIALOG_IMPORT_SUCCESS_MESSAGE_ADDED_SINGLE}}', values.addedCount)]}</li>",
"<li>{[this.format('{{i18n PLUGINS_CORE_UI_USERS_DIALOG_IMPORT_SUCCESS_MESSAGE_UPDATED}}', '{{i18n PLUGINS_CORE_UI_USERS_DIALOG_IMPORT_SUCCESS_MESSAGE_UPDATED_SINGLE}}', values.existingCount)]}</li>",
"<li>{[this.format('{{i18n PLUGINS_CORE_UI_USERS_DIALOG_IMPORT_SUCCESS_MESSAGE_DELETED}}', '{{i18n PLUGINS_CORE_UI_USERS_DIALOG_IMPORT_SUCCESS_MESSAGE_DELETED_SINGLE}}', values.deletedCount)]}</li>",
"<tpl if='errorCount > 0'>",
"<li>{[this.format('{{i18n PLUGINS_CORE_UI_USERS_DIALOG_IMPORT_SUCCESS_MESSAGE_ERROR}}', '{{i18n PLUGINS_CORE_UI_USERS_DIALOG_IMPORT_SUCCESS_MESSAGE_ERROR_SINGLE}}', values.errorCount)]}</li>",
"</tpl>",
"</ul>",
"<tpl if='errorCount > 0'>",
"{{i18n PLUGINS_CORE_UI_USERS_DIALOG_IMPORT_SUCCESS_MESSAGE_ERROR_SEE_LOGS}}",
"</tpl>",
{
format: function(multiFormat, singleFormat, count)
{
return count > 1 ? Ext.String.format(multiFormat, count) : Ext.String.format(singleFormat, count);
}
}
),
/**
* Open dialog box to create a new user
* @param {String[]} populationContexts The contexts for the populations to display in the combobox.
* @param {String} [userMessageTargetType=user] the type of user message target
* @param {String} [userToolRole] The role of users tool
* @param {Boolean} [userPopulationOnly] true if the dialog should proposed only directories of its own population
* @param {Function} [callback] the callback function. Parameters are:
* @param {Object} callback.user The user's properties
*/
add: function (populationContexts, userMessageTargetType, userToolRole, userPopulationOnly, callback)
{
this._mode = 'new';
this._userMessageTargetType = userMessageTargetType;
this._callback = callback;
if (!this._chooseUserDirectoryInitialized)
{
var directoryField = this._createDirectoryField(userToolRole, userPopulationOnly);
this._chooseUserDirectoryDialog = Ext.create('Ametys.window.DialogBox', {
title: "{{i18n PLUGINS_CORE_UI_USERS_DIALOG_ADD_TITLE}}",
icon: Ametys.getPluginResourcesPrefix('core-ui') + '/img/users/add_16.png',
layout: {
type: 'vbox',
align : 'stretch',
pack : 'start'
},
width: 450,
defaultFocus: 'userDirectories',
items: [
{
xtype: 'component',
html: "{{i18n PLUGINS_CORE_UI_USERS_DIALOG_CHOOSE_USER_DIRECTORY_HINT}}",
height: 25
},
directoryField
],
closeAction: 'hide',
referenceHolder: true,
defaultButton: 'next',
buttons : [{
reference: 'next',
text: "{{i18n PLUGINS_CORE_UI_USERS_DIALOG_NEXT}}",
handler: Ext.bind(function() {
var userDirectoryField = this._chooseUserDirectoryDialog.items.getByKey('userDirectories');
if (!userDirectoryField.isValid())
{
return;
}
var userDirectoryValue = userDirectoryField.getValue().split('#', 2);
this._open(userDirectoryValue[0], userDirectoryValue[1], null);
this._chooseUserDirectoryDialog.close();
},this)
}, {
text: "{{i18n PLUGINS_CORE_UI_USERS_DIALOG_CANCEL}}",
handler: Ext.bind(function() {this._chooseUserDirectoryDialog.close();}, this)
}]
});
this._chooseUserDirectoryInitialized = true;
}
this._chooseUserDirectoryDialog.show();
},
/**
* Open dialog box to to edit user's information
* @param {String} login The login of user to edit
* @param {String} populationId The id of the population of the user
* @param {String} [userMessageTargetType=user] the type of user message target
* @param {Function} [callback] the callback function. Parameters are:
* @param {Object} callback.user The user's properties
*/
edit: function (login, populationId, userMessageTargetType, callback)
{
this._mode = 'edit';
this._userMessageTargetType = userMessageTargetType;
this._callback = callback;
this._open (populationId, null, login);
},
/**
* Open dialog box to bulk import users
* @param {String} [userToolRole] The role of users tool
* @param {Boolean} [userPopulationOnly] true if the dialog should proposed only directories of its own population
* @param {Function} [callback] function called with true/false if success/error
*/
importUsers: function (userToolRole, userPopulationOnly, callback)
{
this._importCallback = callback;
if (this._importDialog == null)
{
this._importDialog = this._createImportDialogBox(userToolRole, userPopulationOnly);
}
this._importDialog.show();
this._initImportForm();
},
/**
* Create a new Select User Directory field
* @private
* @param {String} [userToolRole] The role of users tool
* @param {Boolean} [userPopulationOnly] true if the dialog should proposed only directories of its own population
* @return {Ametys.form.field.SelectUserDirectory} a new Select User Directory field
*/
_createDirectoryField: function(userToolRole, userPopulationOnly)
{
var directoryField = Ext.create("Ametys.form.field.SelectUserDirectory", {
itemId: 'userDirectories',
name: 'userDirectories',
labelWidth: 150,
allowBlank: false,
onlyModifiable: true,
showLabels: true,
showDescriptions: true,
userPopulationOnly: userPopulationOnly,
layout: {
type: 'vbox',
align : 'stretch',
pack : 'start'
}
});
directoryField.getStore().on('load', function(store, records) {
if (records.length == 0)
{
if (this._chooseUserDirectoryDialog)
{
this._chooseUserDirectoryDialog.close();
}
Ametys.Msg.show({
title: "{{i18n PLUGINS_CORE_UI_USERS_DIALOG_NO_MODIFIABLE_POPULATION_WARNING_TITLE}}",
msg: "{{i18n PLUGINS_CORE_UI_USERS_DIALOG_NO_MODIFIABLE_POPULATION_WARNING_MSG}}",
buttons: Ext.Msg.OK,
icon: Ext.MessageBox.INFO
});
}
else
{
directoryField._userPopulations.select(records[0].get('id'));
// If UserTool opened, try to select the same values in the comboboxes
if (userToolRole)
{
var tool = Ametys.tool.ToolsManager.getTool(userToolRole);
if (tool != null)
{
var value = tool.getPopulationComboValue();
if (value && value != '#all')
{
directoryField.setValue(value); // First set the first combobox to update the second one
var userDirectoryId = tool.getUserDirectoryComboValue();
if (directoryField._userDirectories.getStore().findExact('index', userDirectoryId) >= 0) // test if the value selected in the tool is present in the dialog box
{
value += "#" + userDirectoryId;
directoryField.setValue(value);
}
}
}
}
}
}, this);
directoryField.getStore().load();
return directoryField;
},
/**
* @private
* Show dialog box for user edition
* @param {String} populationId The id of the population of the user. Cannot be null
* @param {String} userDirectoryId The id of the user directory in its population. Can be null in 'edit' mode
* @param {String} login The user's login. Can be null in 'new' mode
*/
_open: function (populationId, userDirectoryId, login)
{
var me = this;
function configureCallback (success)
{
if (success)
{
me._box.show();
me._initForm (login, populationId);
}
}
// Create dialog box if needed
this._createDialogBox(login, populationId, userDirectoryId, configureCallback);
},
/**
* @private
* Creates the dialog if needed.
* @param {String} login The user's login. Can be null in 'new' mode.
* @param {String} populationId The id of the population of the user. Cannot be null.
* @param {String} userDirectoryId The index of the user directory in its population. Can be null in 'edit' mode
* @param {Function} callback Function to called after drawing box.
*/
_createDialogBox: function (login, populationId, userDirectoryId, callback)
{
this._form = Ext.create('Ametys.form.ConfigurableFormPanel', {
'fieldNamePrefix': '',
defaultFieldConfig: {
labelWidth: 100
}
});
this._box = Ext.create('Ametys.window.DialogBox', {
title: this._mode == 'new' ? "{{i18n PLUGINS_CORE_UI_USERS_DIALOG_ADD_TITLE}}" : "{{i18n PLUGINS_CORE_UI_USERS_DIALOG_EDIT_TITLE}}",
iconCls: 'ametysicon-black302' + (this._mode == 'new' ? ' ametysicon-add64' : ' ametysicon-edit45'),
layout: 'fit',
width: 500,
maxHeight: 500,
items: [ this._form ],
closeAction: 'hide',
referenceHolder: true,
defaultButton: 'validate',
buttons : [{
reference: 'validate',
text: "{{i18n PLUGINS_CORE_UI_USERS_DIALOG_OK}}",
handler: Ext.bind(this._validate, this, [populationId, userDirectoryId])
}, {
text: "{{i18n PLUGINS_CORE_UI_USERS_DIALOG_CANCEL}}",
handler: Ext.bind(function() {this._box.hide();}, this)
}]
});
this._configureForm(login, populationId, userDirectoryId, callback);
},
/**
* @private
* Configures the user edition form.
* @param {String} login The user's login. Can be null in 'new' mode.
* @param {String} populationId The id of the population of the user. Cannot be null.
* @param {Number} userDirectoryId The id of the user directory in its population. Can be null in 'edit' mode
* @param {Function} callback Function to called after drawing box.
*/
_configureForm: function (login, populationId, userDirectoryId, callback)
{
Ametys.data.ServerComm.callMethod({
role: "org.ametys.plugins.core.user.UserDAO",
methodName: this._mode == "new" ? "getEditionModelForDirectory" : "getEditionModelForUSer",
parameters: this._mode == "new" ? [populationId, userDirectoryId] : [login, populationId],
callback: {
handler: this._getEditionModelCb,
scope: this,
arguments: [callback]
},
errorMessage: true
});
},
/**
* @private
* Callback function after retrieving user's edition model. Configure the form.
* @param {Object} view the user's edition view
* @param {Object[]} args the callback arguments.
*/
_getEditionModelCb: function (view, args)
{
var model = view.elements;
model['password'].widget = 'edition.changepassword';
this._form.configure(model);
args[0](true);
},
/**
* @private
* Initialize the form
* @param {String} [login] The user's login. Can not be null in edition mode.
* @param {String} [populationId] The id of the population of the user. Can not be null in edition mode.
*/
_initForm: function (login, populationId)
{
if (this._mode == 'new')
{
this._form.reset();
this._form.setValues(); // setValues must always be called for configurable form panel in order to complete its initialization
this._form.getForm().findField('login').enable();
this._form.getForm().findField('login').focus();
}
else
{
Ametys.plugins.core.users.UsersDAO.getStoredUser([login, populationId], this._getUserCb, {scope: this});
}
},
/**
* @private
* Callback function invoked after retrieving user's properties
* Initialize the form
* @param {Object} user the user's properties
*/
_getUserCb: function (user)
{
user.password = "PASSWORD" ;
this._form.setValues({values: user, comments: {}, repeaters: []});
this._form.getForm().findField('login').disable();
// focus first field != changepasswordfield
this._form.getForm().getFields().each(function(item, index, length) {
if (item.isDisabled() || item.type == 'password')
{
return true;
}
else
{
item.focus(true);
return false;
}
});
},
/**
* @private
* Validates the dialog box.
* Creates or edits user.
* @param {String} populationId The id of the population the user belongs to ('edit' mode) or where the user has to be created ('new' mode)
* @param {String} userDirectoryId The index of the user directory in its population. Can be null in 'edit' mode
*/
_validate: function(populationId, userDirectoryId)
{
if (!this._form.isValid())
{
return;
}
var values = this._form.getValues();
if (this._mode == 'new')
{
Ametys.plugins.core.users.UsersDAO.addUser([populationId, userDirectoryId, values, this._userMessageTargetType], this._editUserCb, {scope: this, waitMessage: {target: this._box}});
}
else
{
values.login = this._form.getForm().findField('login').getValue(); // disabled field
Ametys.plugins.core.users.UsersDAO.editUser([populationId, values, this._userMessageTargetType], this._editUserCb, {scope:this, waitMessage: {target: this._box}});
}
},
/**
* @private
* Callback function invoked after group creation/edition process is over.
* @param {Object} user the added/edited user or the errors
* @param {Object} args the callback arguments
*/
_editUserCb: function (user, args)
{
if (user.errors)
{
var errors = user.errors;
var me = this;
Ext.Object.each(errors, function(fieldName, fieldErrors) {
var fd = me._form.getField(fieldName);
if (fd)
{
if (fieldErrors && fieldErrors.errors)
{
var msgError = [];
Ext.Array.forEach(fieldErrors.errors, function(fieldError) {
msgError.push(fieldError);
});
fd.markInvalid(msgError.join("<br>"));
}
else
{
fd.markInvalid("{{i18n PLUGINS_CORE_UI_USERS_DIALOG_INVALID_FIELD}}");
}
}
});
return;
}
this._box.hide();
if (Ext.isFunction (this._callback))
{
this._callback (user)
}
},
/**
* Create the dialog box for importing users.
* @param {String} [userToolRole] The role of users tool
* @param {Boolean} [userPopulationOnly] true if the dialog should proposed only directories of its own population
* @return {Ametys.window.DialogBox} The importing dialog box
* @private
*/
_createImportDialogBox: function(userToolRole, userPopulationOnly)
{
var formPanel = Ext.create('Ext.form.Panel', {
border: false,
itemId: 'importForm',
scrollable: false,
defaults: {
cls: 'ametys',
labelSeparator: '',
labelAlign: 'right',
labelWidth: 150,
anchor: '100%'
},
items: [
{
xtype: 'component',
cls: 'a-text',
html: "{{i18n PLUGINS_CORE_UI_USERS_DIALOG_IMPORT_CHOOSE_USER_DIRECTORY_HINT}}"
},
this._createDirectoryField(userToolRole, userPopulationOnly),
{
xtype: 'fileuploadfield',
id: 'importFile',
name: 'importFile',
fieldLabel: "{{i18n PLUGINS_CORE_UI_USERS_DIALOG_IMPORT_DIALOG_FILE_LABEL}}",
ametysDescription: "{{i18n PLUGINS_CORE_UI_USERS_DIALOG_IMPORT_FILE_DESC}}",
allowBlank: false,
emptyText: "{{i18n PLUGINS_CORE_UI_USERS_DIALOG_IMPORT_FILE_EMPTY_TEXT}}",
buttonText: "{{i18n PLUGINS_CORE_UI_USERS_DIALOG_IMPORT_FILE_BUTTON}}"
},
{
xtype: 'checkbox',
name: 'cleanDirectory',
inputValue: 'true',
hideLabel: true,
style: {
marginLeft: '150px'
},
boxLabel: "{{i18n PLUGINS_CORE_UI_USERS_DIALOG_IMPORT_CLEAN_DIRECTORY_LABEL}}",
ametysDescription: "{{i18n PLUGINS_CORE_UI_USERS_DIALOG_IMPORT_CLEAN_DIRECTORY_DESC}}",
checked: false
}
]
});
var dialog = Ext.create('Ametys.window.DialogBox', {
layout: 'fit',
title: "{{i18n PLUGINS_CORE_UI_USERS_DIALOG_IMPORT_TITLE}}",
iconCls: 'ametysicon-black302 decorator-ametysicon-upload119',
width: 550,
scrollable: false,
items: [
formPanel
],
closeAction: 'hide',
buttons: [{
text: "{{i18n PLUGINS_CORE_UI_USERS_DIALOG_OK}}",
handler: Ext.bind(this._okImport, this)
}, {
text: "{{i18n PLUGINS_CORE_UI_USERS_DIALOG_CANCEL}}",
handler: Ext.bind(this._cancelImport, this)
}]
});
return dialog;
},
/**
* Initializes the import form.
* @private
*/
_initImportForm: function()
{
var form = this._importDialog.getComponent('importForm').getForm();
form.findField('importFile').reset();
form.findField('cleanDirectory').setValue(false);
},
/**
* The action to perform when the user clicks on the OK button from the importing dialog box.
* @param {Ext.button.Button} button The clicked button.
* @param {Ext.event.Event} event The click event.
* @private
*/
_okImport: function(button, event)
{
var form = this._importDialog.getComponent('importForm').getForm();
var clean = form.findField('cleanDirectory').checked;
if (clean)
{
Ametys.Msg.confirm("{{i18n PLUGINS_CORE_UI_USERS_DIALOG_IMPORT_DIALOG_WAIT_TITLE}}",
"{{i18n PLUGINS_CORE_UI_USERS_DIALOG_IMPORT_CLEAN_CONFIRM}}",
function(answer) {
if (answer == 'yes')
{
this._doImport();
}
},
this
);
}
else
{
this._doImport();
}
},
/**
* The action to perform when the user clicks on the cancel button from the importing dialog box.
* @param {Ext.button.Button} button The clicked button.
* @param {Ext.event.Event} event The click event.
* @private
*/
_cancelImport: function(button, event)
{
this._importDialog.hide();
},
/**
* The action to perform when the user confirmed the import.
* @private
*/
_doImport: function()
{
var form = this._importDialog.getComponent('importForm').getForm();
if (form.isValid())
{
form.submit({
url: Ametys.getPluginDirectPrefix('core-ui') + '/users/import',
params : Ametys.getAppParameters(),
waitTitle: "{{i18n PLUGINS_CORE_UI_USERS_DIALOG_IMPORT_DIALOG_WAIT_TITLE}}",
waitMsg: "{{i18n PLUGINS_CORE_UI_USERS_DIALOG_IMPORT_DIALOG_WAIT_MSG}}",
success: Ext.bind(this._successImport, this),
failure: Ext.bind(this._failureImport, this)
});
}
},
/**
* The callback that will be invoked after a successful response
* @param {Ext.form.Basic} form The form that requested the action.
* @param {Ext.form.action.Action} action The Action object which performed the operation.
* @private
*/
_successImport: function(form, action)
{
var result = Ext.applyIf(action.result, {
addedCount: 0,
existingCount: 0,
deletedCount: 0,
errorCount: 0
})
var message = this._importResultTpl.applyTemplate(result);
Ametys.Msg.show({
title: "{{i18n PLUGINS_CORE_UI_USERS_DIALOG_IMPORT_SUCCESS_TITLE}}",
msg: message,
buttons: Ext.Msg.OK,
icon: Ext.MessageBox.INFO
});
var userDirectoryValue = form.findField("userDirectories").getValue();
var userDirectoryValues = userDirectoryValue.split('#', 2);
var populationId = userDirectoryValues[0];
//fake message to refresh tool
if (result.addedCount > 0 || result.existingCount > 0 || result.deletedCount > 0)
{
Ext.create('Ametys.message.Message', {
type: Ametys.message.Message.CREATED,
targets: {
id: Ametys.message.MessageTarget.USER,
parameters: {
id: "fake-login", // bad way of doing it, but it can be dangerous to send a full list of users, there can be sooooo many
populationId: populationId
}
}
});
}
this._importDialog.hide();
if (Ext.isFunction (this._importCallback))
{
this._importCallback (true)
}
},
/**
* The callback that will be invoked after a failed transaction attempt.
* @param {Ext.form.Basic} form The form that requested the action.
* @param {Ext.form.action.Action} action The Action object which performed the operation.
* @private
*/
_failureImport: function(form, action)
{
var message = action.result.message;
Ametys.Msg.show({
title: "{{i18n PLUGINS_CORE_UI_USERS_DIALOG_IMPORT_ERROR_TITLE}}",
msg: message || "{{i18n PLUGINS_CORE_UI_USERS_DIALOG_IMPORT_ERROR_MSG}}",
buttons: Ext.Msg.OK,
icon: Ext.MessageBox.ERROR
});
if (Ext.isFunction (this._importCallback))
{
this._importCallback (false)
}
}
});