/*
* 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 the tree of plugins by file, and allows the administrator to perform several operations such as :</p>
* <ul>
* <li> see the documentation </li>
* <li> activate/deactivate plugins </li>
* <li> reboot the application with the changes made </li>
* </ul>
*/
Ext.define('Ametys.plugins.admin.plugins.PluginsTool', {
extend: 'Ametys.tool.Tool',
/**
* @private
* @property {Ext.tree.Panel} _tree The plugins by file tree panel
*/
/**
* @private
* @property {Boolean} _hasChanges true if changes were made, false otherwise
*/
/**
* @private
* @property {String} _proxyUrl the url of the proxy of the associated tree store
*/
constructor: function(config)
{
this.callParent(arguments);
this._proxyUrl = config.proxyUrl;
Ametys.message.MessageBus.on(Ametys.message.Message.MODIFYING, this._onModifying, this);
},
onOpen: function()
{
this.callParent(arguments);
var hasPendingChanges = Ametys.plugins.admin.plugins.PluginsDAO.hasPendingChanges();
this.setDirty(hasPendingChanges);
},
setDirty: function (dirty)
{
this.callParent(arguments);
this._tree.getDockedItems()[2].setVisible(dirty);
},
getMBSelectionInteraction: function()
{
return Ametys.tool.Tool.MB_TYPE_ACTIVE;
},
createPanel: function ()
{
this._tree = this._drawPanel();
return this._tree;
},
sendCurrentSelection: function()
{
this._onSelect();
},
/**
* Refreshes the tool
*/
refresh: function ()
{
this.showRefreshing();
this._tree.getStore().load({node: this._tree.getRootNode(), callback: this.showRefreshed, scope: this});
},
/**
* @private
* Draw the tree panel for the plugins by file tool
*/
_drawPanel: function()
{
var store = Ext.create('Ext.data.TreeStore', {
model: 'Ametys.plugins.admin.plugins.PluginsTool.Plugin',
proxy: {
type: 'ametys',
plugin: 'admin',
url: this._proxyUrl,
reader: {
type: 'json',
rootProperty: 'children'
}
},
root: {
expanded: false
},
listeners: {
'load': Ext.bind(this._onLoad, this)
}
});
return new Ext.tree.Panel({
cls: 'plugins-tool',
store: store,
rootVisible: false,
scrollable: true,
border: 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}}"
}
],
listeners: {
'selectionchange': Ext.bind(this._onSelect, 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
* Filters the tree nodes by entered text.
* @param {Ext.form.field.Text} field This text field
*/
_filter: function(field)
{
this._filterField = field;
this._tree.getStore().clearFilter();
var val = Ext.String.escapeRegex(field.getRawValue());
if (val.length > 2)
{
this._regexFilter = new RegExp(val, 'i');
this._tree.getStore().filter({
filterFn: Ext.bind(this._filterByTextAndChildren, this)
});
}
else
{
this._regexFilter = null;
}
this._tree.getDockedItems()[1].setVisible(!this._tree.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._tree.expandNode(record);
}
return isVisible;
},
/**
* @private
* Handler for the clear search
*/
_clearSearchFilter: function ()
{
if (this._filterField)
{
this._filterField.reset();
}
this._regexFilter = null;
this._tree.getStore().clearFilter();
this._tree.getDockedItems()[1].setVisible(false);
var selection = this._tree.getSelectionModel().getSelection()[0];
if (selection)
{
this._tree.ensureVisible(selection.getPath());
}
},
/**
* @private
* Handler for the expand amm button
*/
_expandAll: function ()
{
this._tree.expandAll();
},
/**
* @private
* Handler for the collapse all button
*/
_collapseAll: function ()
{
this._tree.collapseAll();
},
/**
* @private
* Listener when store is loaded
*/
_onLoad: function()
{
var rootNode = this._tree.getRootNode();
// Expand first nodes
this._tree.getRootNode().expandChildren(false, false, this._tree._onRootNodesChangedAndExpanded, this);
},
/**
* @private
* When the newly loaded root nodes are expanded
*/
_onRootNodesChangedAndExpanded: function()
{
// Select first node
this._tree.getSelectionModel().select(this._tree.getRootNode().firstChild);
},
/**
* @private
* Send selection message
*/
_onSelect: function()
{
var targets = [];
var selectedNodes = this._tree.getSelectionModel().getSelection();
Ext.Array.forEach(selectedNodes, function(selectedNode) {
var nodeData = selectedNode.data;
var activeFeature = false,
inactiveFeature = false;
if (nodeData.type == 'feature')
{
if (nodeData.cause == "")
{
activeFeature = nodeData.active;
}
else
{
inactiveFeature = nodeData.cause == "EXCLUDED";
}
}
target = Ext.create('Ametys.message.MessageTarget', {
id: 'plugin-by-file-node',
parameters: {
id: selectedNode.getId(),
type: nodeData.type,
active: nodeData.active,
activeFeature: activeFeature, // if true the "deactivate" action is enabled
inactiveFeature: inactiveFeature, // if true the "activate" action is enabled
selectable: nodeData.type == 'component', // if true the "select" action is enabled
pluginName: nodeData.pluginName,
featureName: nodeData.featureName,
componentName: nodeData.componentName,
extensionPointName: nodeData.name,
parentName: selectedNode.parentNode != null ? selectedNode.parentNode.data.name : "", // for the "select" action
extensionId: nodeData.extensionId,
componentId: nodeData.componentId
}
});
targets.push(target);
});
Ext.create('Ametys.message.Message', {
type: Ametys.message.Message.SELECTION_CHANGED,
targets: targets
});
},
/**
* @private
* Listener when the tree is being modified
* @param {Ametys.message.Message} message The modified message.
*/
_onModifying: function (message)
{
this.setDirty(Ametys.plugins.admin.plugins.PluginsDAO.hasPendingChanges());
}
});