/*
* Copyright 2024 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.
*/
/**
* Tool to display the result of all relevant AccessController for the current selection
*/
Ext.define('Ametys.plugins.coreui.profiles.ContextPermissionsTool', {
extend: "Ametys.plugins.coreui.rights.RightContextSelectionTool",
statics : {
/**
* @private
* Renderer for the assignment cells, which draws an icon representing the assignment
* @param {Object} value The data value for the current cell
* @param {Object} metaData A collection of metadata about the current cell
* @param {Ext.data.Model} record The record for the current row
* @return {String} The HTML string to be rendered
*/
_renderAssignment: function(value, metaData, record)
{
var value = value || {};
var glyph = Ametys.plugins.coreui.profiles.ContextPermissionsTool._computeGlyph(value.accessResult);
var tooltip = Ametys.plugins.coreui.profiles.ProfilesGridHelper.computeExplanation(value.accessExplanations);
metaData.tdAttr = `data-qtip="${Ext.String.htmlEncode(tooltip)}" data-qclass="uitool-context-permissions-tooltip"`; // surround with quote to avoid breaking rendering
return '<span class="a-grid-access-result-glyph ' + glyph + '"/>';
},
/**
* @private
* Gets a list of css classes based on an access result.
* @param accessResult a string representing an access result
* @return {String} The HTML string to be rendered
*/
_computeGlyph: function (accessResult)
{
switch (accessResult)
{
case Ametys.plugins.coreui.rights.AccessResult.ANONYMOUS_ALLOWED:
case Ametys.plugins.coreui.rights.AccessResult.ANY_CONNECTED_ALLOWED:
case Ametys.plugins.coreui.rights.AccessResult.USER_ALLOWED:
case Ametys.plugins.coreui.rights.AccessResult.GROUP_ALLOWED:
return "ametysicon-check-1 allowed";
case Ametys.plugins.coreui.rights.AccessResult.ANONYMOUS_DENIED:
case Ametys.plugins.coreui.rights.AccessResult.ANY_CONNECTED_DENIED:
case Ametys.plugins.coreui.rights.AccessResult.USER_DENIED:
case Ametys.plugins.coreui.rights.AccessResult.GROUP_DENIED:
return "ametysicon-cross-1 denied";
case Ametys.plugins.coreui.rights.AccessResult.UNKNOWN:
default:
// Undetermined
return "ametysicon-minus-symbol unknown disabled";
}
}
},
createPanel: function() {
return Ext.create('Ext.container.Container', {
layout: {
type: 'card'
},
cls: 'uitool-context-permissions',
items: [
{
itemId: 'invalid-selection-card',
xtype: 'component',
cls: 'a-panel-text-empty',
border: false,
html: ''
},
this._createAssignmentsGrid()
]
});
},
/**
* @private
* Create a grid displaying all the assignments of a user
* @param {Object[]} a list of object representing the profiles
* @returns {Ext.grid.Panel} the assignment grid panel
*/
_createAssignmentsGrid: function() {
this._store = Ext.create('Ametys.plugins.coreui.profiles.PermissionTargetStore', this._getStoreCfg());
this._assignmentsGrid = Ext.create("Ametys.plugins.coreui.profiles.PermissionTargetGrid", {
dockedItems: [
{
xtype: 'component',
tpl: '{{i18n PLUGINS_CORE_UI_TOOL_CONTEXT_PERMISSION_HEADER_CONTEXT_TPL}} <strong data-qtip="{contextId}">{[Ext.String.escapeHtml(values.contextLabel)]}</strong><tpl if="contextType"> ({contextType})</tpl>',
ui: 'tool-hintmessage'
}, {
xtype: 'component',
html: "{{i18n PLUGINS_CORE_UI_TOOL_CONTEXT_PERMISSION_HEADER_HINT}}",
ui: 'tool-hintmessage',
listeners: {
afterrender: function(me) {
// Register the new tip with an element's ID
Ext.tip.QuickTipManager.register({
glyphIcon: "ametysicon-sign-question",
target: me.getId(), // Target button's ID
text: "{{i18n PLUGINS_CORE_UI_TOOL_CONTEXT_PERMISSION_HEADER_HINT_TOOLTIP}}",
inribbon: false
});
},
destroy: function(me) {
Ext.tip.QuickTipManager.unregister(me.getId());
}
}
},
this._getToolbarCfg()
],
itemId: 'context-permissions-grid',
store: this._store,
enableColumnHide: false, // disable column hiding by the user. Column visibility is handled by the right filter
stateful: true,
stateId: this.self.getName() + "$assignment-grid",
});
// reset column visibility
// column are stateful, but the visibility is linked to the right filter and not the user interaction
Ext.Array.forEach(this._assignmentsGrid.getColumns(), function(column, index) {
column.setVisible(true);
}, this);
return this._assignmentsGrid;
},
/**
* @private
* Get the store configuration for the assignments grid
* @param {Object[]} a list of object representing all the existing profiles
* @returns {Object} the store configuration
*/
_getStoreCfg: function()
{
return {
proxy: {
type: 'ametys',
role: this.getServerRole(),
serverId : this.getServerId(),
methodName: 'getContextPermissions',
methodArguments: ['context', 'convertor'],
reader: {
type: 'json',
rootProperty: 'data'
}
},
listeners: {
scope: this,
'beforeload': function(store, operation)
{
// Get the parameters, or create empty object if needed
var parameters = operation.getParams() || {};
var targets = this.getCurrentSelectionTargets();
// Add the method arguments
parameters = Ext.apply(parameters, {
context: this.getContextId(targets[0]),
convertor: this.getConvertorId()
});
// Set the parameters
operation.setParams(parameters);
},
'metachange': function(store, meta) {
let columns = Ametys.plugins.coreui.profiles.ProfilesGridHelper.getColumnsFromPermissions(meta.permissions, Ametys.plugins.coreui.profiles.ContextPermissionsTool._renderAssignment);
this._assignmentsGrid.reconfigure(store, this._assignmentsGrid.getInitialConfig('columns').concat(columns));
}
}
}
},
_getToolbarCfg: function()
{
return {
dock: 'top',
xtype: 'toolbar',
layout: {
type: 'hbox',
align: 'middle'
},
border: false,
defaultType: 'textfield',
items: [
{
xtype: 'component',
html: '{{i18n PLUGINS_CORE_UI_TOOL_CONTEXT_PERMISSION_TOOLBAR_FILTERS}}'
}, {
xtype: 'edition.right',
itemId: 'profile-filter',
name: 'profile-filter',
cls: 'ametys',
allowBlank: true,
includeReader: true,
multiple: false,
stacked: "false",
flex: 1,
maxWidth: 400,
emptyText: "{{i18n PLUGINS_CORE_UI_TOOL_CONTEXT_PERMISSION_TOOLBAR_RIGHT_FILTER}}",
listeners: {change: Ext.bind(this._filterByRight, this)}
}, {
itemId: 'user-group-filter',
name: 'user-group-filter',
cls: 'ametys',
flex: 1,
maxWidth: 400,
emptyText: "{{i18n PLUGINS_CORE_UI_TOOL_CONTEXT_PERMISSION_TOOLBAR_USERGROUP_FILTER}}",
listeners: {change: Ext.Function.createBuffered(this._filterByUserOrGroup, 500, this)}
}, {
xtype: 'button',
// Clear filter
tooltip: "{{i18n PLUGINS_CORE_UI_TOOL_CONTEXT_PERMISSION_TOOLBAR_CLEAR_USERGROUP_FILTER}}",
handler: Ext.bind (this._clearFilter, this, ['user-group-filter'], false),
iconCls: 'a-btn-glyph ametysicon-eraser11 size-16',
cls: 'a-btn-light'
}
]
}
},
/**
* @private
* Filters the columns by right
* @param {Ametys.form.widget.Right} field The right field
*/
_filterByRight: function(field)
{
Ext.suspendLayouts();
var rightId = field.getValue();
if (Ext.isEmpty(rightId))
{
Ext.Array.forEach(this._assignmentsGrid.getColumns(), function(column, index) {
column.setVisible(true);
}, this);
this._store.removeFilter('emptyProfile');
}
else
{
// Computes the columns to let visible
var matchingColumns = [];
// Hide the others (except the first column)
Ext.Array.forEach(this._assignmentsGrid.getColumns(), function(column, index) {
let rights = column.getInitialConfig("rights");
if (index == 0)
{
// Do nothing on label column
}
else if (rights == undefined || Ext.Array.contains(rights, rightId))
{
column.setVisible(true);
matchingColumns.push(column.dataIndex);
}
else
{
column.setVisible(false);
}
}, this);
this._store.addFilter([{
id: 'emptyProfile',
filterFn: function(record)
{
for (var dataIndex of matchingColumns)
{
var assignment = record.get(dataIndex)
if (assignment != undefined && assignment.accessResult != Ametys.plugins.coreui.rights.AccessResult.UNKNOWN)
{
return true;
}
}
return false;
}
}]);
}
Ext.resumeLayouts(true);
},
/**
* @private
* Filters the records by their user login/label or group id/label
* @param {Ext.form.field.Text} field The text field
*/
_filterByUserOrGroup: function(field)
{
this._store.removeFilter('userOrGroup');
var text = Ext.String.escapeRegex(field.getRawValue());
if (text.length == 0)
{
return;
}
var fn = function(record, text)
{
var regExp = new RegExp('.*' + text + '.*', 'i');
return regExp.test(record.get('login'))
|| regExp.test(record.get('groupId'))
|| regExp.test(record.get('sortableLabel'));
};
this._store.addFilter({
id: 'userOrGroup',
filterFn: Ext.bind(fn, this, [text], 1)
});
},
/**
* @private
* Clear the current text filter
* @param {String} id The input id
*/
_clearFilter: function(id)
{
this.getContentPanel().down("#"+id).reset();
},
setNoSelectionMatchState: function (message)
{
var mainPanel = this.getContentPanel()
mainPanel.setActiveItem('invalid-selection-card');
mainPanel.getComponent('invalid-selection-card').setHtml(message);
// call parent to ensure normal operation
this.callParent(arguments);
},
refresh: function (manual)
{
this.showRefreshing();
var targets = this.getCurrentSelectionTargets(); // [][0] == undefined
if (targets.length > 1
|| targets.length == 0 && Ametys.message.MessageBus.getCurrentSelectionMessage().getTargets().length != 0)
{
// if selection multiple or not empty, do nothing
this.showUpToDate(true);
return;
}
this.getContentPanel().setActiveItem('context-permissions-grid');
var contextInfo = this.getContextInfo(targets[0]);
this._assignmentsGrid.getDockedComponent(0).setData({contextLabel: contextInfo.label, contextId: this.getContextId(targets[0]), contextType: contextInfo.type});
var me = this
this._store.load(function() {me.showRefreshed()});
},
});