/*
* 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.
*/
/**
* <p>This tool displays a tree if log categories, and allows the administrator to perform several operations such as :</p>
* <ul>
* <li> change the log levels </li>
* <li> force log level inheritance </li>
* <li> inherit log level </li>
* </ul>
* @private
*/
Ext.define('Ametys.plugins.admin.logs.LogsLevelTool', {
extend: 'Ametys.tool.Tool',
/**
* @cfg {String[]} [expandCategories] A list of categories and select to expand on load
*/
/**
* @private
* @property {Ext.tree.Panel} _logsTree The logs tree panel
*/
/**
* @private
* @property {String[]} _expandCategories paths to expand on load
*/
constructor: function(config)
{
this.callParent(arguments);
Ametys.message.MessageBus.on(Ametys.message.Message.MODIFIED, this._onModified, this);
},
getMBSelectionInteraction: function()
{
return Ametys.tool.Tool.MB_TYPE_ACTIVE;
},
createPanel: function ()
{
this._logsTree = this._drawLogsLevelPanel();
return this._logsTree;
},
sendCurrentSelection: function()
{
var targets = [];
var selectedCategories = this._logsTree.getSelectionModel().getSelection();
Ext.Array.forEach(selectedCategories, function(selectedCategory) {
target = Ext.create('Ametys.message.MessageTarget', {
id: Ametys.message.MessageTarget.LOG_CATEGORY,
parameters: {id: selectedCategory.data.category, level: selectedCategory.data.level}
});
targets.push(target);
});
Ext.create('Ametys.message.Message', {
type: Ametys.message.Message.SELECTION_CHANGED,
targets: targets
});
},
setParams: function(params)
{
this._expandCategories = params.expandCategories;
this.showOutOfDate();
},
/**
* Refreshes the tool
*/
refresh: function ()
{
this.showRefreshing();
this._logsTree.getStore().load({node: this._logsTree.getRootNode()});
},
/**
* Get the tree of the tool
* @return {Ext.tree.Panel} The main tree component of the tool
*/
getTree: function()
{
return this._logsTree;
},
/**
* @private
* Draw the tree panel for the log levels
*/
_drawLogsLevelPanel: function()
{
var store = Ext.create('Ext.data.TreeStore', {
model: 'Ametys.plugins.admin.logs.LogsLevelTool.Category',
root: {
expanded: false
},
proxy: {
type: 'ametys',
plugin: 'admin',
url: 'logs-level',
reader: {
type: 'json',
rootProperty: 'children'
}
},
listeners: {
'load': Ext.bind(this._onLoad, this)
},
sorters: [ { property: 'text', direction: "ASC" }]
});
return new Ext.tree.Panel({
store: store,
rootVisible: false,
dockedItems: [
this._getFilterToolbarConfig(),
{
dock: 'top',
xtype: 'button',
ui: 'tool-hintmessage',
hidden: true,
itemId: 'no-result',
text: "{{i18n PLUGINS_ADMIN_PLUGINS_FILTER_NO_MATCH}}" + "{{i18n PLUGINS_ADMIN_PLUGINS_FILTER_NO_MATCH_ACTION}}",
scope: this,
handler: this._clearSearchFilter
},
{
xtype: 'component',
cls: 'hint',
hidden: true,
dock: 'top',
html: "{{i18n PLUGINS_ADMIN_PLUGINS_CHANGES_PENDING}}"
}
],
border: false,
scrollable: true,
listeners: {
'selectionchange': Ext.bind(this.sendCurrentSelection, this)
}
});
},
/**
* @private
* Get the filter toolbar config
* @return {Object} The filter toolbar config
*/
_getFilterToolbarConfig: function()
{
return {
dock: 'top',
xtype: 'toolbar',
layout: {
type: 'hbox',
align: 'stretch'
},
border: false,
defaultType: 'button',
items: [{
// Filter input
xtype: 'textfield',
cls: 'ametys',
flex: 1,
maxWidth: 400,
itemId: 'plugins-filter-input',
emptyText: "{{i18n PLUGINS_ADMIN_PLUGINS_FILTER}}",
minLength: 3,
minLengthText: "{{i18n PLUGINS_ADMIN_PLUGINS_FILTER_INVALID}}",
msgTarget: 'qtip',
listeners: {change: Ext.Function.createBuffered(this._filter, 500, this)},
style: {
marginRight: '0px'
}
},
{
// Clear filter
tooltip: "{{i18n PLUGINS_ADMIN_PLUGINS_CLEAR_FILTER}}",
handler: Ext.bind (this._clearSearchFilter, this),
icon: Ametys.getPluginResourcesPrefix('admin') + '/img/plugins/clear.gif',
cls: 'a-btn-light'
},
{
xtype: 'tbspacer',
flex: 0.0001
},
{
// Expand all
tooltip: "{{i18n PLUGINS_ADMIN_PLUGINS_EXPAND_ALL}}",
handler: Ext.bind (this._expandAll, this, [], false),
icon: Ametys.getPluginResourcesPrefix('admin') + '/img/plugins/expand-all.gif',
cls: 'a-btn-light'
},
{
// Collapse all
tooltip: "{{i18n PLUGINS_ADMIN_PLUGINS_COLLAPSE_ALL}}",
handler: Ext.bind (this._collapseAll, this, [], false),
icon: Ametys.getPluginResourcesPrefix('admin') + '/img/plugins/collapse-all.gif',
cls: 'a-btn-light'
}
]
};
},
/**
* @private
* Handler for the expand amm button
*/
_expandAll: function ()
{
this._logsTree.expandAll();
},
/**
* @private
* Handler for the collapse all button
*/
_collapseAll: function ()
{
this._logsTree.collapseAll();
},
/**
* @private
* Filters the tree nodes by entered text.
* @param {Ext.form.field.Text} field This text field
*/
_filter: function(field)
{
this._filterField = field;
this._logsTree.getStore().clearFilter();
var val = Ext.String.escapeRegex(field.getRawValue());
if (val.length > 2)
{
this._regexFilter = new RegExp(val, 'i');
this._logsTree.getStore().filter({
filterFn: Ext.bind(this._filterByTextAndChildren, this)
});
}
else
{
this._regexFilter = null;
}
this._logsTree.getDockedItems()[1].setVisible(!this._logsTree.getStore().getCount());
},
/**
* @private
* Filter function that check if a node in the tree should be visible or not.
* @param {Ext.data.Model} record The record to check.
* @return {boolean} True if the record should be visible.
*/
_filterByTextAndChildren: function (record)
{
var isVisible = this._regexFilter == null || this._regexFilter.test(record.data.text);
if (!isVisible)
{
// if the record does not match, we check if any child is visible. If at least one is, this record should not be hidden.
// This is efficient because the data is in the store, and is not loaded in the view.
for (var i = 0; !isVisible && i < record.childNodes.length; i++) {
isVisible = this._filterByTextAndChildren(record.childNodes[i]);
}
}
if (isVisible)
{
this._logsTree.expandNode(record);
}
return isVisible;
},
/**
* @private
* Handler for the clear search
*/
_clearSearchFilter: function ()
{
if (this._filterField)
{
this._filterField.reset();
}
this._regexFilter = null;
this._logsTree.getStore().clearFilter();
this._logsTree.getDockedItems()[1].setVisible(false);
var selection = this._logsTree.getSelectionModel().getSelection()[0];
if (selection)
{
this._logsTree.ensureVisible(selection.getPath());
}
},
/**
* @private
* Listener when store is loaded
*/
_onLoad: function()
{
var rootNode = this._logsTree.getRootNode();
// Set the parent level field on each node of the tree
if (rootNode.firstChild)
{
this._setParentLevels(rootNode.firstChild.data.level, rootNode.firstChild.childNodes);
}
// Expand first nodes
this._logsTree.getRootNode().expandChildren(false, this._onRootNodesChangedAndExpanded, this);
this.showRefreshed();
},
/**
* @private
* Set the parent levels on the tree nodes
* @param {String} parentLevel the level of the parent node
* @param {Ext.data.NodeInterface[]} children the list of child nodes
*/
_setParentLevels: function(parentLevel, children)
{
var me = this;
Ext.Array.forEach(children, function(child) {
child.set('parentLevel', parentLevel);
if (child.childNodes)
{
var subParentLevel = child.data.level == "inherit" ? parentLevel : child.data.level;
me._setParentLevels(subParentLevel, child.childNodes);
}
});
},
/**
* @private
* When the newly loaded root nodes are expanded
*/
_onRootNodesChangedAndExpanded: function()
{
if (this._expandCategories)
{
var rootNode = this._logsTree.getRootNode().firstChild;
this._expandCategories.forEach(function (path) {
var node = this._logsTree.getStore().findNode('category', path);
if (node == null)
{
this.getLogger().warn("The server log category '" + path + "' was not existing at the time the tool was opened");
}
else
{
if (path == this._expandCategories[0])
{
this._logsTree.getSelectionModel().select(node);
}
while (node = node.parentNode)
{
node.expand();
}
}
}, this);
this._logsTree.ensureVisible(this._expandCategories[0], {field: 'text', separator: '.'});
}
else
{
// Select first node
this._logsTree.getSelectionModel().select(this._logsTree.getRootNode().firstChild);
}
},
/**
* @private
* Listener on the message bus of type modified
* @param {Ametys.message.Message} message The message
*/
_onModified: function(message)
{
var targets = message.getTargets("log-category");
if (targets.length > 0)
{
var store = this._logsTree.getStore();
var me = this;
Ext.Array.forEach(targets, function(target) {
var name = target.getParameters().id;
var index = store.find("category", name);
if (index != -1)
{
var level = target.getParameters().level,
modifiedCategory = store.getAt(index);
if (message.getParameters().major)
{
var level = modifiedCategory.getResolvedLevel();
for (var i = 0; i < modifiedCategory.childNodes.length; i++)
{
var childNode = modifiedCategory.childNodes[i];
me._updateNode(childNode, level, true, true);
}
}
else if (level == 'INHERIT')
{
var level = modifiedCategory.parentNode.getResolvedLevel();
me._updateNode(modifiedCategory, level, true, false);
}
else
{
me._updateNode(modifiedCategory, level, false, false);
}
}
});
}
},
/**
* @private
* Update node info
* @param {Ametys.plugins.admin.logs.LogsLevelTool.Category} node The node to update
* @param {String} level The level to set
* @param {Boolean} inherited Is the level inherited
* @param {Boolean} force True to force recursiverly the level inheritance
*/
_updateNode: function (node, level, inherited, force)
{
var inherited = inherited == true;
node.set('level', inherited ? "inherit" : level);
// Find the first parent containing the appropriate level information
var parentNode = node.parentNode;
while (parentNode.data.level.toLowerCase() == "inherit")
{
parentNode = parentNode.parentNode;
}
node.set('parentLevel', parentNode.data.level);
for (var i = 0; i < node.childNodes.length; i++)
{
var childNode = node.childNodes[i];
if (childNode.get('level').toLowerCase() == "inherit" || force)
{
this._updateNode(childNode, level, true, force);
}
}
}
});