/*
* 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.
*/
/**
* This tool displays the dashboard.
* Composed with a grid and a tabpanel.
* @private
*/
Ext.define('Ametys.plugins.odf.dashboard.DashboardTool', {
extend: 'Ametys.tool.Tool',
/**
* @private
* @property {Object} _grids All the grids of this tool indexed by their id.
*/
/**
* @private
* @property {Ext.panel.Panel} _mainPanel The main panel of the tool.
*/
/**
* @private
* @property {Ext.data.Model[]} _selection The current selection.
*/
/**
* @cfg {String} modelClass (required) The model classname for the store.
*/
/**
* @property {String} _model See {@link #cfg-modelClass}
* @private
*/
/**
* @cfg {String} proxyUrl (required) The URL of the proxy for the stores of the grids.
*/
/**
* @property {String} _proxyUrl See {@link #cfg-proxyUrl}
* @private
*/
/**
* @cfg {String} tasksUrl (required) The URL of the tasks.
*/
/**
* @property {String} _tasksUrl See {@link #cfg-tasksUrl}
* @private
*/
constructor: function(config)
{
this.callParent(arguments);
this._model = this.getInitialConfig('modelClass');
this._proxyUrl = this.getInitialConfig('proxyUrl');
this._tasksUrl = this.getInitialConfig('tasksUrl');
Ametys.message.MessageBus.on(Ametys.message.Message.WORKFLOW_CHANGED, this._refreshIfNeeded, this);
Ametys.message.MessageBus.on(Ametys.message.Message.MODIFIED, this._refreshIfNeeded, this);
Ametys.message.MessageBus.on(Ametys.message.Message.DELETED, this._refreshIfNeeded, this);
},
createPanel: function()
{
this._mainPanel = Ext.create('Ext.panel.Panel', {
layout: {
type: 'vbox',
align: 'stretch'
},
border: false,
items: [
{
xtype: 'panel',
margin: '0 0 8 0',
itemId: 'recent-drafts-panel',
border: false,
ui: 'light',
title: '',
layout: 'fit',
minHeight: 100,
maxHeight: 250,
stateful: true,
stateId: this.self.getName() + "$recent-drafs",
items: [{
xtype: 'grid',
itemId: 'recent-drafts-grid',
scrollable: true,
deferRowRender: false,
selModel: {
mode: 'MULTI'
}
}]
},
{
split: true,
margin: '8 0 0 0',
flex: 1,
cls: 'ametys-form-tab-item',
xtype: 'tabpanel',
itemId: 'tabs-panel',
activeTab: 0,
deferredRender: false,
border: false,
items: [] // will be set on refresh
}
]
});
return this._mainPanel;
},
getMBSelectionInteraction: function()
{
return Ametys.tool.Tool.MB_TYPE_ACTIVE;
},
sendCurrentSelection: function()
{
var records = this._selection;
var targets = [];
if (records && records.length > 0)
{
targets.push({
id: Ametys.message.MessageTarget.CONTENT,
parameters: {
ids: records.map(function(record){
return record.getId();
})
}
});
}
else
{
targets = [];
}
// Send the message on the bus
Ext.create('Ametys.message.Message', {
type: Ametys.message.Message.SELECTION_CHANGED,
targets: targets
});
},
/**
* Listener for change of selection in any grid.
* @param {String} selectedTaskId The id of the grid where the selection change happened.
* @param {Ext.data.Model} model The model.
* @param {Ext.data.Model[]} selected The selected records.
* @private
*/
_onSelectionChange: function(selectedTaskId, model, selected)
{
for (var taskId in this._grids)
{
if (taskId != selectedTaskId)
{
this._grids[taskId].getSelectionModel().deselectAll(true); // deselect all the other grids
}
}
this._selection = Ext.isArray(selected) ? selected : [selected];
this.sendCurrentSelection();
},
setParams: function(params)
{
this.callParent(arguments);
this.refresh();
// Register the tool on the history tool
var toolId = this.getFactory().getId();
var toolParams = this.getParams();
Ametys.navhistory.HistoryDAO.addEntry({
id: this.getId(),
label: this.getTitle(),
description: this.getDescription(),
iconSmall: this.getSmallIcon(),
iconMedium: this.getMediumIcon(),
iconLarge: this.getLargeIcon(),
type: Ametys.navhistory.HistoryDAO.TOOL_TYPE,
action: Ext.bind(Ametys.tool.ToolsManager.openTool, Ametys.tool.ToolsManager, [toolId, toolParams], false)
});
},
refresh: function()
{
this.showRefreshing();
Ametys.data.ServerComm.send({
plugin: 'odf',
url: this._tasksUrl,
parameters: {},
priority: Ametys.data.ServerComm.PRIORITY_MAJOR,
waitMessage: false,
responseType: 'xml',
callback: {
handler: this._refreshCb,
scope: this
}
});
},
/**
* Does the real refresh after retrieving the tasks from the server.
* Draws and loads the grids.
* @param {HTMLElement} response The text response provided by the {@link Ametys.data.ServerComm}
* @private
*/
_refreshCb: function(response)
{
var tasks = Ext.dom.Query.selectNode('tasks', response);
this._grids = {};
// Recent drafts panel
var recentDraftPanel = this._mainPanel.items.get('recent-drafts-panel');
var recentDraftsGrid = recentDraftPanel.items.get('recent-drafts-grid');
// Tabs panel
var taskPanels = [];
// Retrieve the tasks
var taskNodes = Ext.dom.Query.select("task", tasks);
for (var i = 0; i < taskNodes.length; i++)
{
var taskId = Ext.dom.Query.selectValue('@id', taskNodes[i], '');
var taskLabel = Ext.dom.Query.getNodeValue(taskNodes[i]);
var max = Ext.dom.Query.selectValue('@length', taskNodes[i]);
if (taskId == 'recent-drafts')
{
this._reconfigureRecentDraftsGrid(recentDraftsGrid, 'recent-drafts', taskLabel, max, taskNodes[i]);
recentDraftPanel.items.add(recentDraftsGrid);
}
else
{
taskPanels.push({
xtype: 'panel',
itemId: taskId + '-panel',
title: taskLabel,
border: false,
layout: 'fit',
items: [this._drawGridPanel(taskId, taskLabel, max, taskNodes[i])]
});
}
}
this._mainPanel.items.get('tabs-panel').removeAll();
for (var i = 0; i < taskPanels.length; i++)
{
this._mainPanel.items.get('tabs-panel').add(taskPanels[i]).show();
}
// Load the grids
for (var taskId in this._grids)
{
this._grids[taskId].getStore().load({
params: {
taskId: taskId,
login: Ametys.getAppParameter('user').login,
lang: Ametys.getAppParameter('user').locale
}
});
}
this.showRefreshed();
},
/**
* Changes the store and the columns of the recent drafts grid.
* @param {String} grid The grid to reconfigure.
* @param {String} taskId The id of the task.
* @param {String} taskLabel The label of the task.
* @param {Number} max The max items to display.
* @param {Object} taskNode The DOM representing the task
* @private
*/
_reconfigureRecentDraftsGrid: function(grid, taskId, taskLabel, max, taskNode)
{
var store = Ext.create('Ext.data.Store', {
model: this._model,
proxy: {
type: 'ametys',
plugin: 'odf',
url: this._proxyUrl,
reader: {
type: 'xml',
record: 'content'
}
},
sorters: [{property: 'lastModified', direction: 'DESC'}],
taskId: taskId,
taskLabel: taskLabel
});
var columns = this._getRecentDraftColumnsConfig(taskNode);
grid.reconfigure(store, columns);
// Events
grid.on('selectionchange', this._onSelectionChange, this, {args: [taskId]});
grid.on('celldblclick', this._openContent, this);
grid.getStore().on('load', this._onLoad, this, [taskId, taskLabel]);
this._grids[taskId] = grid;
},
/**
* @protected
* Gets the configuration of the columns for a recent drafts
* @param {Object} taskNode The DOM representing the task
* @return {Object[]} The configuration of the columns for a task tab
*/
_getRecentDraftColumnsConfig: function(taskNode)
{
return columns = [
{stateId: 'grid-column-title', header: "{{i18n PLUGINS_ODF_DASHBOARD_COLUMN_CONTENT_TITLE}}", width: 270, sortable: true, dataIndex: 'title', renderer: Ametys.grid.GridColumnHelper.renderTextWithIcon},
{stateId: 'grid-column-language', header: "{{i18n PLUGINS_ODF_DASHBOARD_COLUMN_LANGUAGE}}", width: 60, sortable: true, dataIndex: 'language', renderer: this._renderLanguage},
{stateId: 'grid-column-code', header: "{{i18n PLUGINS_ODF_DASHBOARD_COLUMN_CODE}}", width: 100, sortable: true, dataIndex: 'displayCode'},
{stateId: 'grid-column-ametyscode', header: "{{i18n PLUGINS_ODF_DASHBOARD_COLUMN_AMETYS_CODE}}", width: 100, sortable: true, dataIndex: 'code', hidden: true},
{stateId: 'grid-column-catalog', header: "{{i18n PLUGINS_ODF_DASHBOARD_COLUMN_CATALOG}}", width: 120, sortable: true, dataIndex: 'catalog'},
{stateId: 'grid-column-lastModified', header: "{{i18n PLUGINS_ODF_DASHBOARD_COLUMN_LASTMODIFIED}}", width: 160, sortable: true, dataIndex: 'lastModified', renderer: Ametys.grid.GridColumnHelper.renderDateTime},
{stateId: 'grid-column-workflow-step', header: "{{i18n PLUGINS_ODF_DASHBOARD_COLUMN_WORKFLOW_STEP}}", width: 50, sortable: true, dataIndex: 'workflow-step', renderer: this._renderWorkflowStep}
];
},
/**
* Draws a grid for a tab.
* @param {String} taskId The id of the task.
* @param {String} taskLabel The label of the task.
* @param {Number} max The max items to display.
* @param {Object} taskNode the DOM representing the task
* @return {Ext.grid.Panel} The grid.
* @private
*/
_drawGridPanel: function(taskId, taskLabel, max, taskNode)
{
var store = Ext.create('Ext.data.Store', {
model: this._model,
proxy: {
type: 'ametys',
plugin: 'odf',
url: this._proxyUrl,
reader: {
type: 'xml',
record: 'content'
}
},
sorters: [{property: 'lastModified', direction: 'DESC'}],
taskId: taskId,
taskLabel: taskLabel
});
var itemId = taskId + '-grid';
var grid = Ext.create('Ext.grid.Panel', {
itemId: itemId,
store: store,
region: 'center',
deferRowRender: true,
cls: 'mask-below-menu',
selModel: {
mode: 'MULTI'
},
stateful: true,
stateId: this.self.getName() + "$" + itemId,
columns: this._getTaskColumnsConfig(taskNode)
});
// Events
grid.on('selectionchange', this._onSelectionChange, this, {args: [taskId]});
grid.on('celldblclick', this._openContent, this);
grid.getStore().on('load', this._onLoad, this, [taskId, taskLabel]);
this._grids[taskId] = grid;
return grid;
},
/**
* @protected
* Gets the configuration of the columns for a task tab
* @param {Object} taskNode The DOM representing the task
* @return {Object[]} The configuration of the columns for a task tab
*/
_getTaskColumnsConfig: function(taskNode)
{
return [
{stateId: 'grid-column-title', header: "{{i18n PLUGINS_ODF_DASHBOARD_COLUMN_CONTENT_TITLE}}", width: 270, sortable: true, dataIndex: 'title', renderer: Ametys.grid.GridColumnHelper.renderTextWithIcon},
{stateId: 'grid-column-language', header: "{{i18n PLUGINS_ODF_DASHBOARD_COLUMN_LANGUAGE}}", width: 60, sortable: true, dataIndex: 'language', renderer: this._renderLanguage},
{stateId: 'grid-column-code', header: "{{i18n PLUGINS_ODF_DASHBOARD_COLUMN_CODE}}", width: 100, sortable: true, dataIndex: 'displayCode'},
{stateId: 'grid-column-ametyscode', header: "{{i18n PLUGINS_ODF_DASHBOARD_COLUMN_AMETYS_CODE}}", width: 100, sortable: true, dataIndex: 'code', hidden: true},
{stateId: 'grid-column-catalog', header: "{{i18n PLUGINS_ODF_DASHBOARD_COLUMN_CATALOG}}", width: 120, sortable: true, dataIndex: 'catalog'},
{stateId: 'grid-column-contributor', header: "{{i18n PLUGINS_ODF_DASHBOARD_COLUMN_CONTRIBUTOR}}", width: 270, sortable: true, dataIndex: 'contributor'},
{stateId: 'grid-column-lastModified', header: "{{i18n PLUGINS_ODF_DASHBOARD_COLUMN_LASTMODIFIED}}", width: 160, sortable: true, dataIndex: 'lastModified', renderer: Ametys.grid.GridColumnHelper.renderDateTime}
];
},
/**
* Renders a workflow step.
* @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 for rendering the workflow step.
* @private
*/
_renderWorkflowStep: function(value, metaData, record)
{
var step;
var iconSmall = record.get('workflow-icon-small');
if (iconSmall)
{
step = '<img src="'
+ Ametys.CONTEXT_PATH
+ iconSmall
+ '" style="float: left; margin-right: 5px" alt="'
+ record.get('workflow-step')
+ '" title="'
+ record.get('workflow-step')
+ '"/>';
}
else
{
step = record.get('workflow-step');
}
return step;
},
/**
* Renders a language.
* @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 for rendering the workflow step.
* @private
*/
_renderLanguage: function(value, metaData, record)
{
return '<img src="' + Ametys.CONTEXT_PATH + record.get('language-image') + '" style="float:left; margin-right: 5px" alt="' + value + '"/>';
},
/**
* Opens the selected content.
* @param {Ext.view.Table} table The table.
* @param {HTMLElement} td The TD element for the cell.
* @param {Number} cellIndex The cell index.
* @param {Ext.data.Model} record The record to open.
* @private
*/
_openContent: function(table, td, cellIndex, record)
{
var id = record.get('id');
Ametys.tool.ToolsManager.openTool('uitool-content', {'id': id});
},
/**
* Listener when a store reads data.
* @param {Ext.data.Store} store The store.
* @param {Ext.data.Model[]} records An array of records.
* @private
*/
_onLoad: function(store, records)
{
this._modifyPanelTitle(store.taskId, store.taskLabel, records.length);
},
/**
* Updates the title of a panel.
* @param {String} taskId The id of the panel to update.
* @param {String} taskLabel The new title.
* @param {String} count The number to set in brackets at the end of the new title.
* @private
*/
_modifyPanelTitle: function(taskId, taskLabel, count)
{
if (taskId && taskLabel)
{
var panel = this._mainPanel.getComponent(taskId + '-panel') || this._mainPanel.getComponent('tabs-panel').getComponent(taskId + '-panel');
if (panel != null)
{
panel.setTitle(taskLabel + ' (' + count + ')');
}
}
},
/**
* Listener on CONTENT message.
* @param {Ametys.message.Message} message The content message.
* @private
*/
_refreshIfNeeded: function(message)
{
var targets = message.getTargets(Ametys.message.MessageTarget.CONTENT);
if (targets)
{
var contentIds = this._convertToId(targets);
if (this._areInStore(contentIds))
{
// We are concerned by this message
this.showOutOfDate();
}
}
},
/**
* Gives the ids of the given targets.
* @param {Object[]} targets The targets.
* @return {String[]} The ids.
* @private
*/
_convertToId: function(targets)
{
return targets.map(function(target){
return target.getParameters().id;
});
},
/**
* Tells if one of the contentIds are currently somewhere in the tool's grids.
* @param {String[]} contentIds The ids to test.
* @return {Boolean} True if at least one id is in one of the stores.
* @private
*/
_areInStore: function(contentIds)
{
var atLeastOneIsInStore = false;
Ext.each(contentIds, function(contentId) {
for (var key in this._grids)
{
var record = this._grids[key].getStore().getById(contentId);
if (record != null)
{
atLeastOneIsInStore = true;
return false;
}
}
}, this);
return atLeastOneIsInStore;
}
});