/*
* 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 tool displays the list of groups, and allow a CMS user perform several actions such as :
*
* - rename / delete a group
* - add / delete users in a group
*
* @private
*/
Ext.define('Ametys.plugins.coreui.groups.GroupsTool', {
extend: 'Ametys.tool.Tool',
/**
* @property {Ext.grid.GridPanel} _groupGrid The grid with the group records
* See {@link Ametys.plugins.coreui.groups.GroupsTool.Group}
* @private
*/
/**
* @property {Ext.form.field.Text} _groupSearchField The search field filter for groups
* @private
*/
/**
* @property {Ext.grid.GridPanel} _userGrid The grid with the user records of the current selected group
* See {@link Ametys.plugins.coreui.groups.GroupsTool.UserGroup}
* @private
*/
/**
* @property {String} [_userTargetId=user] The target for users
* @private
*/
/**
* @property {String} [_groupTargetId=group] The target for groups
* @private
*/
/**
* @property {String[]} _contexts The contexts for the group directories to display in the combobox.
* @private
*/
/**
* @property {Boolean} _enableAllDirectoriesOption True to add an option in the group directories combobox for searching over all the directories.
* @private
*/
/**
* @property {Boolean} _allDirectoriesOptionId The id of the 'all directories' options.
* @private
* @readonly
*/
_allDirectoriesOptionId: '#all',
getMBSelectionInteraction: function()
{
return Ametys.tool.Tool.MB_TYPE_ACTIVE;
},
/**
* @inheritdoc
* @param {Object} params The params to set
* @param {String} [params.contexts] The contexts for the group directories to display in the combobox. Default to the current contexts.
* @param {Boolean/String} [params.enableAllDirectoriesOption=false] True to add an option in the group directories combobox for searching over all the directories.
*/
setParams: function(params)
{
this.callParent(arguments);
this._contexts = Ext.Array.from(params.contexts || Ametys.getAppParameter('populationContexts'));
this._enableAllDirectoriesOption = Ext.isBoolean(params.enableAllDirectoriesOption) ? params.enableAllDirectoriesOption : params.enableAllDirectoriesOption == "true";
this.showOutOfDate();
},
refresh: function()
{
this.showRefreshing();
this._loadDirectories(Ext.bind(this.showRefreshed, this));
},
constructor: function(config)
{
this.callParent(arguments);
// Set the role and the message target type
this._groupTargetId = config['group-target-id'] || Ametys.message.MessageTarget.GROUP;
this._userTargetId = config['user-target-id'] || Ametys.message.MessageTarget.USER;
// Bus messages listeners
Ametys.message.MessageBus.on(Ametys.message.Message.CREATED, this._onMessageCreated, this);
Ametys.message.MessageBus.on(Ametys.message.Message.MODIFIED, this._onMessageEdited, this);
Ametys.message.MessageBus.on(Ametys.message.Message.DELETED, this._onMessageDeleted, this);
},
createPanel: function()
{
this._groupDirectoriesField = Ext.create('Ext.form.field.ComboBox', this._getGroupDirectoryComboboxCfg());
// Group search input
this._groupSearchField = Ext.create('Ext.form.TextField', {
xtype: 'textfield',
cls: 'ametys',
maxWidth: 250,
flex: 1,
emptyText: "{{i18n PLUGINS_CORE_UI_TOOL_GROUPS_SEARCH_GROUP_EMPTY_TEXT}}",
listeners: {change: Ext.Function.createBuffered(this._search, 500, this)}
});
var groupStore = this._createGroupStore();
this._groupGrid = Ext.create('Ext.grid.Panel', {
scrollable: true,
stateful: true,
stateId: this.self.getName() + "$grid",
style: {
borderRightStyle: 'solid',
borderRightWidth: '1px'
},
flex: 0.4,
store: groupStore,
columns: [
{header: "{{i18n PLUGINS_CORE_UI_TOOL_GROUPS_LABEL}}", width: 150, dataIndex: 'label', renderer: Ametys.grid.GridColumnHelper.renderTextWithIcon, hideable: false},
{header: "{{i18n PLUGINS_CORE_UI_TOOL_GROUPS_ID}}", width: 100, dataIndex: 'groupId', hideable: false},
{header: "{{i18n PLUGINS_CORE_UI_TOOL_GROUPS_DIRECTORY}}", flex: 1, dataIndex: 'groupDirectoryLabel'}
],
dockedItems: [{
xtype: 'toolbar',
dock: 'top',
layout: {
type: 'hbox',
align: 'stretch'
},
items: [
this._groupSearchField,
{
flex: 1,
xtype: 'component',
html: "<span title=\"{{i18n PLUGINS_CORE_UI_TOOL_GROUPS_SEARCH_GROUP_LIMIT_HELPTEXT}}\">{{i18n PLUGINS_CORE_UI_TOOL_GROUPS_SEARCH_GROUP_LIMIT_HELPTEXT}}</span>",
cls: 'a-toolbar-text'
}
]
}],
listeners: {
selectionchange: {fn: this._onGroupSelectionChange, scope: this}
},
viewConfig: {
plugins: {
ptype: 'ametysgridviewdragdrop',
setAmetysDragInfos: Ext.emptyFn,
setAmetysDropZoneInfos: Ext.bind(this.getGroupDropInfo, this)
}
}
});
var userStore = this._createUserStore();
this._userGrid = Ext.create('Ext.grid.Panel', {
scrollable: true,
stateful: true,
stateId: this.getId() + "$usergrid",
flex: 1,
minWidth: 350,
style: {
borderLeftStyle: 'solid',
borderLeftWidth: '1px'
},
split: true,
store: userStore,
selModel: {
mode: 'MULTI'
},
columns: [
{header: "{{i18n PLUGINS_CORE_UI_TOOL_GROUPS_USER_GRID_NAME}}", width: 250, dataIndex: 'displayName', renderer: Ext.bind(this._renderUserName, this), hideable: false, sortable: true},
{header: "{{i18n PLUGINS_CORE_UI_TOOL_GROUPS_USER_GRID_LOGIN}}", width: 200, sortable: true, dataIndex: 'login'},
{header: "{{i18n PLUGINS_CORE_UI_TOOL_GROUPS_USER_GRID_POPULATION}}", width: 200, sortable: true, dataIndex: 'populationLabel'},
{header: "{{i18n PLUGINS_CORE_UI_TOOL_GROUPS_USER_GRID_EMAIl}}", flex: 1, sortable: true, dataIndex: 'email'}
],
listeners: {
selectionchange: {fn: this._onUserSelectionChange, scope: this}
},
viewConfig: {
plugins: {
ptype: 'ametysgridviewdragdrop',
dragTextField: 'login',
setAmetysDragInfos: Ext.bind(this.getUserDragInfo, this),
setAmetysDropZoneInfos: Ext.bind(this.getUserDropInfo, this)
}
}
});
return Ext.create('Ext.panel.Panel', {
layout: {
type: 'hbox',
pack: 'start',
align: 'stretch'
},
scrollable: 'horizontal',
items: [this._groupGrid, this._userGrid],
dockedItems: [{
xtype: 'toolbar',
layout: {
type: 'hbox',
align: 'stretch'
},
dock: 'top',
items: [this._groupDirectoriesField]
}]
});
},
sendCurrentSelection: function()
{
var targets = [];
var users = this._userGrid.getSelectionModel().getSelection();
var userTargets = [];
var me = this;
Ext.Array.forEach(users, function(user) {
userTarget = Ext.create('Ametys.message.MessageTarget', {
id: me._userTargetId,
parameters: {
id: user.get('login'),
populationId: user.get('populationId')
}
});
userTargets.push(userTarget);
});
var group = this._groupGrid.getSelectionModel().getSelection()[0];
if (group)
{
var groupTarget = {
id: this._groupTargetId,
parameters: {
id: group.get('groupId'),
groupDirectory: group.get('groupDirectory')
},
subtargets: userTargets
};
targets.push(groupTarget);
}
Ext.create('Ametys.message.Message', {
type: Ametys.message.Message.SELECTION_CHANGED,
targets: targets
});
},
/**
* @private
* Get the configuration object for creating the combobox for the group directories
* @return {Object} The configuration
*/
_getGroupDirectoryComboboxCfg: function()
{
return {
xtype: 'combobox',
fieldLabel: "{{i18n PLUGINS_CORE_UI_TOOL_GROUPS_GROUP_DIRECTORY_COMBOBOX}}",
name: "groupDirectories",
cls: 'ametys',
labelWidth: 150,
store: {
fields: ['id', {name: 'label', type: 'string'}],
proxy: {
type: 'ametys',
plugin: 'core-ui',
url: 'group-directories.json',
reader: {
type: 'json',
rootProperty: 'groupDirectories'
}
},
sorters: [{property: 'label', direction: 'ASC'}],
listeners: {
'beforeload': {fn: this._onBeforeLoadDirectories, scope: this},
'load': {fn: this._onLoadDirectories, scope: this}
}
},
valueField: 'id',
displayField: 'label',
queryMode: 'local',
forceSelection: true,
triggerAction: 'all',
listeners: {change: Ext.bind(this._onGroupDirectoryChange, this)}
};
},
/**
* @private
* Load the groups when the current group directory has changed
*/
_onGroupDirectoryChange: function()
{
this._groupGrid.getStore().load();
},
/**
* Function called before loading the group directory 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
*/
_onBeforeLoadDirectories: function(store, operation)
{
operation.setParams( Ext.apply(operation.getParams() || {}, {
contexts: this._contexts
}));
},
/**
* Listener invoked after loading group directories
* @param {Ext.data.Store} store The store
* @param {Ext.data.Model[]} records The records of the store
* @private
*/
_onLoadDirectories: function(store, records)
{
if (this._enableAllDirectoriesOption)
{
// Add an option in the directories combobox for searching over all the directories
store.add({
id: this._allDirectoriesOptionId,
label: "{{i18n PLUGINS_CORE_UI_TOOL_GROUPS_GROUP_DIRECTORY_COMBOBOX_OPTION_ALL}}"
});
}
},
/**
* @private
* Load the group directories
* @param {Function} [callback] The callback function
*/
_loadDirectories: function(callback)
{
this._groupDirectoriesField.getStore().load({
scope: this,
callback: function(records) {
// When store loaded, select the 'all' option if it is available
if (this._enableAllDirectoriesOption)
{
this._groupDirectoriesField.select(this._allDirectoriesOptionId);
}
// Otherwise select the fist data
else if (records.length > 0)
{
this._groupDirectoriesField.select(records[0].get('id'));
}
// If there is one and only one directory, hide the combobox
this._groupDirectoriesField.setHidden(records.length == 1);
// Callback function
if (Ext.isFunction(callback))
{
callback();
}
}
});
},
/**
* @private
* Create the group store that the grid should use as its data source.
* @return {Ext.data.Store} The created store
*/
_createGroupStore: function ()
{
// Merge default store configuration with inherited configuration (provided by #getStoreConfig).
var storeConfig = Ext.merge({
remoteSort: false,
autoLoad: false,
model: 'Ametys.plugins.coreui.groups.GroupsTool.Group',
proxy: {
type: 'ametys',
reader: {
type: 'json',
rootProperty: 'groups'
}
},
sortOnLoad: true,
sorters: [{property: 'label', direction:'ASC'}],
listeners: {
beforeload: {fn: this._onBeforeLoadGroups, scope: this}
}
}, this.getGroupStoreConfig());
return Ext.create('Ext.data.Store', storeConfig);
},
/**
* Returns the elements of configuration of group store to be overridden.
* Override this function if you want to override the group store configuration.
* @return {Object} The elements of store configuration to be overridden
*/
getGroupStoreConfig: function()
{
return {
proxy: {
role:"org.ametys.plugins.core.group.GroupDAO",
// No method name because we don't always call the same one
cancelOutdatedRequest: true,
extraParams: {
limit: null // Default server limit
}
}
};
},
/**
* @private
* Load the group store
*/
_search: function()
{
this._groupGrid.getStore().load();
},
/**
* @private
* Create the user store that the user grid will use as its data source.
* @return {Ext.data.Store} The created store
*/
_createUserStore: function ()
{
// Merge default store configuration with inherited configuration (provided by #getStoreConfig).
var storeConfig = Ext.merge({
remoteSort: false,
autoLoad: false,
model: 'Ametys.plugins.coreui.groups.GroupsTool.UserGroup',
proxy: {
type: 'ametys',
reader: {
type: 'json',
rootProperty: 'users'
}
},
sortOnLoad: true,
sorters: [{property: 'displayName', direction:'ASC'}],
listeners: {
beforeload: {fn: this._onBeforeLoadUsers, scope: this}
}
}, this.getUserStoreConfig());
return Ext.create('Ext.data.Store', storeConfig);
},
/**
* Returns the elements of configuration of user store to be overridden.
* Override this function if you want to override the user store configuration.
* @return {Object} The elements of store configuration to be overridden
*/
getUserStoreConfig: function()
{
return {
proxy: {
plugin: 'core',
url: 'group/users',
extraParams: {
limit: null // No pagination
}
}
};
},
/**
* Create the target of the drop operation relation.
* @param {Ext.data.Model[]} groups The target group of the drop operation.
* @param {Object} item The default drag data that will be transmitted. You have to add a 'target' item in it:
* @param {Object} item.target The target (in the relation way) of the drop operation. A Ametys.relation.RelationPoint config.
*/
getGroupDropInfo: function(groups, item)
{
if (groups.length > 0)
{
var me = this;
item.target = {
relationTypes: [Ametys.relation.Relation.REFERENCE],
targets: {
id: me._groupTargetId,
parameters: {
id: groups[0].get('groupId'),
groupDirectory: groups[0].get('groupDirectory')
}
}
};
}
},
/**
* @private
* Add the 'source' of the drag.
* @param {Object} item The default drag data that will be transmitted. You have to add a 'source' item in it:
* @param {Ametys.relation.RelationPoint} item.source The source (in the relation way) of the drag operation.
*/
getUserDragInfo: function(item)
{
var targets = [];
Ext.Array.each(item.records, function(record) {
targets.push({
id: this._userTargetId,
parameters: {
id: record.get('login'),
populationId: record.get('populationId')
}
});
}, this);
if (targets.length > 0)
{
item.source = {
relationTypes: [Ametys.relation.Relation.REFERENCE],
targets: targets
};
}
},
/**
* Create the target of the drop operation relation.
* @param {Object} target The target of the drop operation.
* @param {Object} item The default drag data that will be transmitted. You have to add a 'target' item in it:
* @param {Object} item.target The target (in the relation way) of the drop operation. A Ametys.relation.RelationPoint config.
*/
getUserDropInfo: function(target, item)
{
var selectedGroup = this._groupGrid.getSelectionModel().getSelection()[0];
if (selectedGroup)
{
var me = this;
item.target = {
relationTypes: [Ametys.relation.Relation.REFERENCE],
targets: {
id: me._groupTargetId,
parameters: {
id: selectedGroup.get('groupId'),
groupDirectory: selectedGroup.get('groupDirectory')
}
}
};
}
},
/**
* @private
* Function to render the name of an user
* @param {Object} value The data value
* @param {Object} metaData A collection of data about the current cell
* @param {Ext.data.Model} record The record
* @return {String} The html value to render.
*/
_renderUserName: function(value, metaData, record)
{
if (this._userTargetId == Ametys.message.MessageTarget.USER)
{
return Ametys.grid.GridColumnHelper.renderUser.apply(this, arguments);
}
else
{
return`<img src="${Ametys.helper.Users.getUserImage(null, null, 16)}" class="a-grid-icon a-grid-icon-user"/>` + Ext.String.escapeHtml(value);
}
},
/**
* Gets the id of the group directory selected in the group directory combobox of this tool.
* @return {Number} The id of the group directory selected in the group directory combobox
*/
getDirectoryComboValue: function()
{
return this._groupDirectoriesField.getValue();
},
/**
* Function called before loading the group 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
*/
_onBeforeLoadGroups: function(store, operation)
{
// If the directory combobox value is invalid, cancel the loading
if (this.getDirectoryComboValue() == null)
{
return false;
}
var proxy = operation.getProxy();
// 'all' option is selected
if (this.getDirectoryComboValue() == this._allDirectoriesOptionId)
{
proxy.methodName = "searchGroupsByContexts";
proxy.methodArguments = ['contexts', 'limit', 'offset', 'searchCriteria'];
proxy.setExtraParam('contexts', this._contexts);
proxy.setExtraParam('searchCriteria', this._groupSearchField.getValue());
return true;
}
else
{
proxy.methodName = "searchGroupsByDirectory";
proxy.methodArguments = ['groupDirectoryId', 'searchCriteria', 'limit', 'offset'];
proxy.setExtraParam('groupDirectoryId', this.getDirectoryComboValue());
proxy.setExtraParam('searchCriteria', this._groupSearchField.getValue());
}
},
/**
* Function called before loading the user 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
*/
_onBeforeLoadUsers: function (store, operation)
{
operation.setParams(operation.getParams() || {});
if (!operation.getParams().groupID || !operation.getParams().groupDirectoryId)
{
var group = this._groupGrid.getSelectionModel().getSelection()[0];
if (group)
{
operation.setParams(Ext.apply(operation.getParams(), {
groupDirectoryId: group.get('groupDirectory'),
groupID: group.get('groupId')
}));
}
}
},
/**
* Fires a event of selection on message bus, from the selected contents in the grid.
* Add load the user store given for the selected group.
* @param {Ext.selection.Model} model The selection model
* @param {Ext.data.Model[]} selected The selected records
* @param {Object} eOpts Event options
* @private
*/
_onGroupSelectionChange: function(model, selected, eOpts)
{
this._userGrid.getStore().removeAll();
var group = selected[0];
if (group)
{
this._userGrid.getStore().load({
params: {
groupID: group.get('groupId')
}
});
}
this.sendCurrentSelection();
},
/**
* Called when selection change in the user grid
* @param {Ext.selection.Model} model The selection model
* @param {Ext.data.Model[]} selected The selected records
* @param {Object} eOpts Event options
* @private
*/
_onUserSelectionChange: function(model, selected, eOpts)
{
this.sendCurrentSelection();
},
/**
* Listener when a Ametys.message.Message#CREATED message was received
* @param {Ametys.message.Message} message The received message
* @private
*/
_onMessageCreated: function(message)
{
// Case creation of a group directory
var groupDirectoryTargets = message.getTargets(Ametys.message.MessageTarget.GROUP_DIRECTORY);
if (groupDirectoryTargets.length > 0)
{
this.showOutOfDate();
}
// Case creation of a group
var groupTarget = message.getTarget(new RegExp('^' + this._groupTargetId + '$'), 1);
if (groupTarget)
{
var groupDirectoryId = groupTarget.getParameters().groupDirectory;
if (this.getDirectoryComboValue() == this._allDirectoriesOptionId || this.getDirectoryComboValue() == groupDirectoryId)
{
// The group grid is concerned by the message
this._groupGrid.getStore().load();
}
}
},
/**
* Listener when a Ametys.message.Message#MODIFIED message was received
* @param {Ametys.message.Message} message The received message
* @private
*/
_onMessageEdited: function(message)
{
// Case edition of a group directory
var groupDirectoryTargets = message.getTargets(Ametys.message.MessageTarget.GROUP_DIRECTORY);
if (groupDirectoryTargets.length > 0)
{
this.showOutOfDate();
}
// Case edition of a group
var groupTarget = message.getTarget(this._groupTargetType);
if (groupTarget != null)
{
var id = groupTarget.getParameters().id;
var groupDirectoryId = groupTarget.getParameters().groupDirectory;
if (this.getDirectoryComboValue() == this._allDirectoriesOptionId || this.getDirectoryComboValue() == groupDirectoryId)
{
if (message.getParameters().major)
{
this._groupGrid.getStore().load();
}
else
{
// Reload users' group
this._userGrid.getStore().load({
params: {
groupID: id
}
});
}
}
}
},
/**
* Listener when a Ametys.message.Message#DELETED message was received
* @param {Ametys.message.Message} message The received message
* @private
*/
_onMessageDeleted: function(message)
{
// Case deletion of a group directory
var groupDirectoryTargets = message.getTargets(Ametys.message.MessageTarget.GROUP_DIRECTORY);
if (groupDirectoryTargets.length > 0)
{
this.showOutOfDate();
}
// Case deletion of a group
var groupTargets = message.getTargets(this._groupTargetType);
var store = this._groupGrid.getStore();
Ext.Array.forEach(groupTargets, function(target) {
var groupId = target.getParameters().id;
var groupDirectory = target.getParameters().groupDirectory;
var group = this._findGroupRecord(groupId, groupDirectory);
if (group)
{
store.remove(group);
}
}, this);
},
/**
* @private
* Gets the first record from the group store that matches the given group id and directory id.
* @param {String} groupId The group id
* @param {String} groupDirectory The group directory id
* @return {Ext.data.Model} the found record, or null if not found.
*/
_findGroupRecord: function(groupId, groupDirectory)
{
var foundRecord;
Ext.Array.each(this._groupGrid.getStore().getRange(), function(record) {
if (record.get('groupId') == groupId && record.get('groupDirectory') == groupDirectory)
{
foundRecord = record;
return false;
}
}, this);
return foundRecord;
}
});