/*
* Copyright 2016 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 class provides a widget for the mapping of a collection of a synchronizable contents based on an Ametys user population.
*/
Ext.define('Ametys.plugins.user.directory.widget.UserPopulationMapping', {
extend: 'Ametys.plugins.contentio.widget.AbstractMapping',
/**
* @cfg {String} [populationField] The relative path of the population id field
*/
/**
* @private
* @property {String} _populationFieldName The property related to {@link #cfg-populationField}
*/
/**
* @cfg {String} [firstNameField] The relative path of the firstname field
*/
/**
* @private
* @property {String} _firstNameFieldName The property related to {@link #cfg-firstNameField}
*/
/**
* @cfg {String} [lastNameField] The relative path of the lastname field
*/
/**
* @private
* @property {String} _lastNameFieldName The property related to {@link #cfg-lastNameField}
*/
/**
* @cfg {String} [emailField] The relative path of the email field
*/
/**
* @private
* @property {String} _emailFieldName The property related to {@link #cfg-emailField}
*/
initComponent: function()
{
this._populationFieldName = this.populationField || null;
this._firstNameFieldName = this.firstNameField || null;
this._lastNameFieldName = this.lastNameField || null;
this._emailFieldName = this.emailField || null;
this._createModel([]);
this._createGrid();
this.items = [this._grid];
this.form.onRelativeFieldsChange([this._populationFieldName], this, this._onPopulationChanged);
this.form.onRelativeFieldsChange([this._firstNameFieldName, this._lastNameFieldName, this._emailFieldName], this, this._onMetadataFieldChanged);
this._addColumns = Ext.Function.createInterceptor(this._addColumns, this._isNotDestroyed, this);
this.callParent(arguments);
},
/**
* @private
* Listener called when the value of the population field changes
* @param {Ext.form.field.Field} field The relative field that has triggered the on change event.
* @param {Object} newValue The new value
* @param {Object} oldValue The old value
*/
_onPopulationChanged: function(field, newValue, oldValue)
{
if (this._populationId != newValue)
{
// Remove old data in the store
if (oldValue != null)
{
this._grid.getStore().getData().each(function(record) {
for (var fieldName in record.data)
{
if (/^attribute-.*/.test(fieldName))
{
delete record.data[fieldName];
}
}
});
}
this._populationId = newValue;
this._reconfigureColumns();
}
},
_isInitialized: function ()
{
return this.callParent() && this._populationId != null;
},
_initializeGrid: function (callback)
{
this.callParent([Ext.bind(this._reconfigureColumns, this, [callback])]);
},
/**
* Configure grid's columns with the selected population
* @param {Function} [callback] The callback function
*/
_reconfigureColumns: function (callback)
{
this._populationId = this._populationId == null ? this.form.getRelativeField(this._populationFieldName, this).getValue() : this._populationId;
if (this._populationId != null)
{
Ametys.data.ServerComm.callMethod({
role: "org.ametys.plugins.userdirectory.synchronize.UserPopulationSynchronizableContentsCollectionHelper",
methodName: "getSupportedUserDirectories",
parameters: [this._populationId],
callback: {
handler: this._addColumns,
arguments: {
callback: callback
},
scope: this
},
errorMessage: {
category: Ext.getClassName(this),
msg: "{{i18n PLUGINS_USER_DIRECTORY_SYNCHRONIZE_USERPOPULATION_GET_SUPPORTED_USERDIRECTORIES_ERROR}}"
}
});
}
else
{
// No population selected, reconfigure with initial columns without any user directory column
this._createModel([]);
this._grid.reconfigure(this._getColumnsCfg());
if (Ext.isFunction(callback))
{
callback();
}
}
},
/**
* @private
* It not destroyed?
* @return {Boolean} true if the user population mapping is destroyed, false otherwise
*/
_isNotDestroyed: function()
{
return !this.destroyed;
},
/**
* @private
* Reconfigure the columns (add one column by supported user directory)
* @param {Object[]} userDirectories The supported user directories
* @param {Object} [args] The additional arguments
* @param {Function} [args.callback] A function to callback
*/
_addColumns: function(userDirectories, args)
{
var additionalFields = Ext.Array.map(userDirectories, function(userDirectory) {
return {name: 'attribute-' + userDirectory.id, type: 'string'};
}, this);
var columnsCfg = this._getColumnsCfg();
var udColumns = Ext.Array.map(userDirectories, function(userDirectory) {
var dataIndex = 'attribute-' + userDirectory.id;
var type = userDirectory.type;
var text = "{{i18n plugin.contentio:PLUGINS_CONTENTIO_WIDGET_MAPPING_MAPPING}}";
var tooltip = "{{i18n plugin.contentio:PLUGINS_CONTENTIO_WIDGET_MAPPING_MAPPING_DESC}}";
if (type == "SQL")
{
text = "{{i18n plugin.contentio:PLUGINS_CONTENTIO_WIDGET_MAPPING_SQL_COLUMN}}";
tooltip = "{{i18n plugin.contentio:PLUGINS_CONTENTIO_WIDGET_MAPPING_SQL_COLUMN_DESC}}";
}
else if (type == "LDAP")
{
text = "{{i18n plugin.contentio:PLUGINS_CONTENTIO_WIDGET_MAPPING_LDAP_ATTRIBUTE}}";
tooltip = "{{i18n plugin.contentio:PLUGINS_CONTENTIO_WIDGET_MAPPING_LDAP_ATTRIBUTE_DESC}}";
}
return {
text: text + " (" + userDirectory.modelLabel + (userDirectory.label ? " \"" + userDirectory.label + "\"" : "") + ")",
tooltip: tooltip,
dataIndex: dataIndex,
flex: 1,
xtype: 'widgetcolumn',
widget: {
xtype: 'textfield',
listeners: {
'change': Ext.bind(this._onWidgetChange, this, [dataIndex], 0)
}
}
};
}, this);
Ext.Array.insert(columnsCfg, 1, udColumns);
this._createModel(additionalFields);
this._grid.reconfigure(columnsCfg);
if (Ext.isFunction(args.callback))
{
args.callback();
}
},
/**
* @private
* Listener called when the value of the login/firstname/lastname/email field changes
* @param {Ext.form.field.Field} field The relative field that has triggered the on change event.
* @param {Object} newValue The new value
* @param {Object} oldValue The old value
*/
_onMetadataFieldChanged: function(field, newValue, oldValue)
{
var gridStore = this._grid.getStore();
// Remove in the grid the new value
if (newValue != null)
{
var record = gridStore.findExact('metadata-ref', newValue);
if (record == -1)
{
// This listener may be fired too soon (i.e. store is not set with its data yet)
// if #setValue is called on login, firstName, lastName, email before called on this widget
this._excludedRecords.push(newValue);
}
else
{
gridStore.remove([record]);
}
}
// Add in the grid the old value
if (oldValue != null)
{
var fieldStore = field.combobox.getStore(),
record = fieldStore.findExact('id', oldValue);
if (record > -1)
{
gridStore.add({
"metadata-ref": oldValue
});
}
}
},
/**
* @private
* Creates the model for the store of the grid
* @param {Object[]} additionalFields The additonnal fields to define in the model
*/
_createModel: function(additionalFields)
{
var fields = [
{
name: 'metadata-ref',
type: 'string',
sortType: Ext.bind(function(metadataRef) {
var metadataLabel = this._getMetadataLabel(metadataRef);
return Ext.data.SortTypes.asNonAccentedUCString(metadataLabel);
}, this)
},
{name: 'synchro', type: 'boolean'}
].concat(additionalFields);
var modelName = this._getModel();
if (Ext.data.schema.Schema.get('default').hasEntity(modelName))
{
Ext.data.schema.Schema.get('default').getEntity(modelName).replaceFields(fields, true);
}
else
{
Ext.define(modelName, {
extend: 'Ext.data.Model',
idProperty: 'metadata-ref',
fields: fields
});
}
},
_getModel: function()
{
return 'Ametys.plugins.userdirectory.UserPopulationMapping.Mapping$' + this.getId();
},
_getColumnsCfg: function()
{
return [{
header: "{{i18n plugin.contentio:PLUGINS_CONTENTIO_WIDGET_MAPPING_FIELDS}}",
dataIndex: 'metadata-ref',
renderer: Ext.bind(this._renderMetadata, this),
flex: 1
}, {
header: "{{i18n plugin.contentio:PLUGINS_CONTENTIO_WIDGET_MAPPING_SYNCHRO}}",
dataIndex: 'synchro',
align: 'center',
width: 110,
xtype: 'widgetcolumn',
widget: {
xtype: 'checkbox',
listeners: {
'change': Ext.bind(this._onWidgetChange, this, ['synchro'], 0)
}
}
}];
},
updateRecord: function(record, value)
{
record.set('synchro', value.synchro);
Ext.Object.each (value, function (key, v) {
if (Ext.String.startsWith(key, "attribute-"))
{
record.set(key, v);
}
});
record.commit();
},
getValueFromRecord: function (record)
{
var data = record.getData(),
attrs = {};
// Find non-empty attributes
Ext.Object.each (data, function (key, v) {
if (Ext.String.startsWith(key, "attribute-") && v)
{
attrs[key] = v;
}
});
if (!Ext.Object.isEmpty(attrs))
{
return Ext.apply({
'metadata-ref': record.get('metadata-ref'),
'synchro': record.get('synchro')
}, attrs)
}
// Filter empty value
return null;
},
getErrors: function(value)
{
value = value || [];
if (Ext.isString(value))
{
value = Ext.decode(value);
}
var errors = this.callParent(arguments);
return errors;
}
});