/*
* Copyright 2019 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 tree panel for ODF contents.<br>
* Add the hability to display indicators on tree nodes.
*/
Ext.define('Ametys.plugins.odf.tree.ODFContentsTreePanel', {
extend: "Ametys.plugins.contentstree.ContentsTreePanel",
statics: {
/**
* Default function to determine if an indicator is available for a record
* @param {String} id the id of indicator
* @param {Ext.data.Record} record the record
* @return {Boolean} true if the indicator is available for this record
*/
matchIndicator: function(id, record)
{
return record.get(id) != null;
},
/**
* Default function to display an active indicator
* @param {Ametys.plugins.odf.tree.ODFContentsTreePanel} tree the tree
* @param {String} id the id of indicator
* @param {Ext.data.Record} record the record
* @return {String|HTML} The display value
*/
applyIndicator: function(tree, id, record)
{
var data = record.get(id);
return `<span data-qtip="${data.tooltip || ''}" class="${tree.indicatorCls} ${data.cssClass || ''}">${data.text || ''}</span>`;
}
},
/**
* @property {String} indicatorCls The css class name for indicators
*/
indicatorCls: "odf-indicator",
/**
* @property {String} loadingCls The css class loading state
*/
loadingCls: 'loading',
/**
* @property {String} resultOkCls The css class when result is ok
*/
validationCls: 'validation-panel',
/**
* @property {String} validationOkCls The css class when validation is ok
*/
validationOkCls: 'validation-ok',
/**
* @property {String} validationInfoCls The css class when validation contains some warnings
*/
validationInfoCls: 'validation-info',
/**
* @cfg {Object[]} indicators The configuration of available indicators
*/
/**
* @property {Object[]} _indicators See {@link #cfg-indicators}.
* @private
*/
/**
* @cfg {Object[]} activeIndicators The current active indicators
*/
/**
* @private
* @property {Object[]} _activeIndicators The active indicators
*/
_activeIndicators: [],
/**
* @cfg {Boolean} [enableIndicatorMenu=true] False to hide the indicator menu
*/
/**
* @private
* @property {Ext.Template} _boxLabelWithGlyphTpl Template for inserting the icon as a glyph
*/
_boxLabelWithGlyphTpl: Ext.create('Ext.Template', '<span class="{iconGlyph}" style="margin-right: 5px"/><span>{label}</span>'),
/**
* @private
* @property {Ext.Template} _resultTxtTpl Template for result panel
*/
_resultTxtTpl: Ext.create('Ext.XTemplate', '<span class="icon {iconGlyph}"></span><span data-qtip="{text}">{text}</span>'),
/**
* @private
* @property {Ext.Container} _validationResultPanel The panel to launch and see the global validation state.
*/
/**
* @cfg {Boolean} [enableValidationCheck=false] Set to 'true' to enable the global validation state on the root content
*/
constructor: function(config)
{
this._indicators = config.indicators || [];
this._activeIndicators = config.activeIndicators || ['odf-indicator-code'];
if (config.displayToolbar)
{
config.dockedItems = config.dockedItems || [];
if (config.enableValidationCheck)
{
config.toolbarPosition = 1;
// panel for launch and see validation state
this._validationResultPanel = this._getValidationPanel();
config.dockedItems.push(this._validationResultPanel);
}
}
this.callParent(arguments);
var me = this;
var view = this.view.lockedView || this.view;
var tpl = new Ext.XTemplate(view.cellTpl.html.replace('</div></td>',
'<tpl for="values.column.getItemId().startsWith(\'tree\') ? this.getActiveIndicators() : []">' // startsWith(\'tree\') to apply to the main column only
+ '<tpl if="this.matchIndicator(values, parent.record)">'
+ '{% out.push(this.applyIndicator(values, parent.record)) %}'
+ '</tpl>'
+ '</tpl>'
+ '</div></td>'),
{
priority: view.cellTpl.priority,
getActiveIndicators: function() {
// We could directly return me activeIndicators; but the order would change following the order of clicking on the checkbox
return me._indicators.map(function(i) { return i.id;})
.filter(function(i) { return me._activeIndicators.indexOf(i) != -1; });
},
matchIndicator: function(id, record) {
var indicator = Ext.Array.findBy(me._indicators, function(v) { return v.id == id;});
return eval(indicator.matchFn || 'Ametys.plugins.odf.tree.ODFContentsTreePanel.matchIndicator').call(me, id, record)
},
applyIndicator: function(id, record) {
var indicator = Ext.Array.findBy(me._indicators, function(v) { return v.id == id;});
return eval(indicator.applyFn || 'Ametys.plugins.odf.tree.ODFContentsTreePanel.applyIndicator').call(me, me, id, record)
}
});
if (this.view.lockedView)
{
this.view.lockedView.cellTpl = tpl;
}
else
{
this.view.cellTpl = tpl;
}
},
/**
* @private
*/
_getValidationPanel: function ()
{
return Ext.create({
dock: 'top',
xtype: 'container',
hidden: false,
layout: {
type: 'hbox',
align: 'middle'
},
cls: this.validationCls,
items: [
{
flex: 1,
xtype: 'panel',
layout: 'card',
activeItem: 1,
items: [
// button to check validation
{
xtype: 'button',
handler: this.checkValidationState,
scope: this,
text: "{{i18n PLUGINS_ODF_CONTENTS_TREE_CHECK_VALIDATION_STATE_BTN}}",
tooltip: "{{i18n PLUGINS_ODF_CONTENTS_TREE_CHECK_VALIDATION_STATE_BTN}}"
},
// validation ok
{
xtype: 'container',
html: this._resultTxtTpl.apply({iconGlyph: 'ametysicon-sign-check-black', text: "{{i18n PLUGINS_ODF_CONTENTS_TREE_CHECK_VALIDATION_STATE_OK}}"})
},
// invalidated contents
{
xtype: 'container',
html: this._resultTxtTpl.apply({iconGlyph: 'ametysicon-sign-info', text: "{{i18n PLUGINS_ODF_CONTENTS_TREE_CHECK_VALIDATION_STATE_INVALIDATED_CONTENTS}}"})
}
]
},
{
// show invalidated contents
xtype: 'button',
hidden: true,
iconCls: 'ametysicon-body-part-eye',
tooltip: "{{i18n PLUGINS_ODF_CONTENTS_TREE_SHOW_INVALIDATED_CONTENTS}}",
scope: this,
handler: this.showInvalidatedContents
},
{
// check validation state
xtype: 'button',
hidden: true,
iconCls: 'ametysicon-arrow-circle-right-double',
tooltip: "{{i18n PLUGINS_ODF_CONTENTS_TREE_CHECK_VALIDATION_STATE}}",
scope: this,
handler: this.checkValidationState
}
]
});
},
applyState: function (state)
{
this.callParent(arguments);
var me = this;
this._activeIndicators = state.activeIndicators || ['odf-indicator-code'];
var indicatorMenu = this.down('toolbar > button[itemId="indicators-menu"]');
if (indicatorMenu)
{
indicatorMenu.getMenu().items.each(function(item) {
item.setChecked(Ext.Array.contains(me._activeIndicators, item.name), true);
});
}
},
getState: function ()
{
var state = this.callParent(arguments) || {};
// save indicators
state.activeIndicators = this._activeIndicators
return state;
},
_getToolBarConfig: function (config)
{
var toolbarCfg = this.callParent(arguments);
if (this._indicators.length > 0 && config.enableIndicatorMenu !== false)
{
var me = this;
var menuItems = [];
Ext.Array.forEach(this._indicators, function(indicator) {
menuItems.push({
xtype: 'menucheckitem',
iconCls: indicator.iconGlyph,
text: indicator.label,
tooltip: indicator.description,
name: indicator.id,
itemId: indicator.id,
checkHandler: me._selectIndicator,
checked: Ext.Array.contains(me._activeIndicators, indicator.id),
scope: me
})
});
toolbarCfg.items.push({
tooltip: "{{i18n PLUGINS_ODF_CONTENTS_TREE_INDICATORS}}",
iconCls: 'a-btn-glyph ametysicon-puzzle33 size-16',
cls: 'a-btn-light',
itemId: 'indicators-menu',
menu: {
xtype: 'menu',
items: menuItems
}
});
}
return toolbarCfg;
},
/**
* @private
* Listener when an indicator is checked/unchecked
* @param {Ext.menu.CheckItem} item the item
* @param {Boolean} checked the checked status
*/
_selectIndicator: function(item, checked)
{
var hasChanges = false;
if (checked && !Ext.Array.contains(this._activeIndicators, item.name))
{
this._activeIndicators.push(item.name);
hasChanges = true;
}
else if (!checked && Ext.Array.contains(this._activeIndicators, item.name))
{
Ext.Array.remove(this._activeIndicators, item.name);
hasChanges = true;
}
if (hasChanges)
{
this.saveState();
this.view.refresh();
}
},
updateNodeUI: function(node)
{
this.callParent(arguments);
this.view.refreshNode(node);
},
/**
* Reset the validation state result
* @param {Boolean} [refreshing] true to set refreshing
*/
resetValidationState: function(refreshing)
{
this._invalidatedContents = [];
this._validationResultPanel.removeCls([this.validationOkCls, this.validationInfoCls]);
var cardResult = this._validationResultPanel.items.get(0);
cardResult.setActiveItem(0);
var btn = cardResult.items.get(0);
if (refreshing)
{
btn.addCls(this.loadingCls);
btn.setText("{{i18n PLUGINS_ODF_CONTENTS_TREE_CHECK_VALIDATION_STATE_WAITING}}");
btn.setDisabled(true);
}
else
{
btn.removeCls(this.loadingCls);
btn.setText("{{i18n PLUGINS_ODF_CONTENTS_TREE_CHECK_VALIDATION_STATE_BTN}}");
btn.setDisabled(false);
}
// hide buttons
this._validationResultPanel.items.get(1).setVisible(false);
this._validationResultPanel.items.get(2).setVisible(false);
},
/**
* Check the validation state of the root content
*/
checkValidationState:function()
{
this.resetValidationState(true);
var contentId = this.getRootNode().get('contentId');
Ametys.data.ServerComm.callMethod({
role: "org.ametys.odf.workflow.ODFWorkflowHelper",
methodName: "getGlobalValidationStatus",
parameters: [contentId],
callback: {
handler: this._checkValidationStateCb,
scope: this,
arguments: [contentId]
},
waitMessage: false,
errorMessage: "{{i18n PLUGINS_ODF_WORKFLOW_ACTION_CHECK_VALIDATION_ACTION_ERROR}}"
});
},
/**
* @private
* Callback function after retrieving the validation state
* @param {Object} result the result
* @param {Object[]} args the callback arguments
*/
_checkValidationStateCb: function(result, args)
{
var contentId = args[0];
if (contentId != this.getRootNode().get('contentId'))
{
// The current root was changed during process, ignore the response.
return;
}
var cardResult = this._validationResultPanel.items.get(0);
if (result.globalValidated)
{
this._validationResultPanel.addCls(this.validationOkCls);
cardResult.setActiveItem(1);
}
else
{
this._invalidatedContents = result.invalidatedContents;
this._validationResultPanel.addCls(this.validationInfoCls);
cardResult.setActiveItem(2);
this._validationResultPanel.items.get(1).setVisible(true);
}
this._validationResultPanel.items.get(2).setVisible(true);
},
/**
* Show the list of elements needing validation
*/
showInvalidatedContents: function()
{
Ametys.odf.helper.Content.showInvalidatedContents({
id: this.getRootNode().get('contentId'),
title: this.getRootNode().get('title'),
invalidatedContents: this._invalidatedContents
});
}
});