/*
* 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.
*/
/**
* @private
* This tool displays the entries of a form
*/
Ext.define('Ametys.plugins.forms.content.tool.FormsTool', {
extend: 'Ametys.tool.Tool',
/**
* @private
* @property {Ext.grid.Panel} _grid the results grid panel
*/
/**
* @private
* @property {String} _contentId the id of the content holding the form
*/
/**
* @private
* @property {String} _formId the id of the current form
*/
/**
* @private
* @property {String} _workflowName the name of the workflow used for the entries of this form. Can be null.
*/
/**
* @private
* @property {String} _formLabel the label of the form
*/
/**
* @private
* @property {Object[]} _fields the fields of the grid
*/
/**
* @property {Number} [PAGE_SIZE=50] The number of records to display by 'page'
* @readonly
*/
PAGE_SIZE: 50,
constructor: function(config)
{
this.callParent(arguments);
Ametys.message.MessageBus.on(Ametys.message.Message.DELETED, this._onEntryDeleted, this);
Ametys.message.MessageBus.on(Ametys.message.Message.MODIFIED, this._onModified, this);
Ametys.message.MessageBus.on(Ametys.message.Message.WORKFLOW_CHANGED, this._onModified, this);
},
createPanel: function()
{
var store = Ext.create('Ext.data.Store', {
fields: [],
data: [],
pageSize: this.PAGE_SIZE,
totalProperty: 'total'
});
this._grid = Ext.create('Ext.grid.Panel', {
store: store,
columns: [],
scrollable: true,
dockedItems: [{
xtype: 'pagingtoolbar',
store: store,
dock: 'bottom',
displayInfo: true,
displayMsg: "{{i18n plugin.forms:PLUGINS_FORMS_UITOOL_ENTRY_CONTENT_RESULT_1}}{0}{{i18n plugin.forms:PLUGINS_FORMS_UITOOL_ENTRY_CONTENT_RESULT_2}}{1}{{i18n plugin.forms:PLUGINS_FORMS_UITOOL_ENTRY_CONTENT_RESULT_3}}{2}",
emptyMsg: "{{i18n plugin.forms:PLUGINS_FORMS_UITOOL_ENTRY_NO_RESULT}}"
}],
selModel : {
mode: 'MULTI'
},
listeners: {
selectionchange: Ext.bind(this.sendCurrentSelection, this)
}
});
return this._grid;
},
getMBSelectionInteraction: function()
{
return Ametys.tool.Tool.MB_TYPE_ACTIVE;
},
setParams: function(params)
{
this.callParent(arguments);
this._formId = params.id;
this._formLabel = params.label;
this._workflowName = params.workflowName;
this._contentId = params.contentId;
if (params.label)
{
this.setTitle(this.getInitialConfig("title") + ": " + params.label);
}
this.refresh();
},
sendCurrentSelection : function()
{
var me = this;
var subtargets = [];
var hasEntries = this._grid.getStore().getTotalCount() > 0;
Ext.each (this._grid.getSelectionModel().getSelection(), function (record) {
subtargets.push({
id: Ametys.message.MessageTarget.CONTENT_FORM_ENTRY,
parameters: {
id: record.getId(),
form: me._formId
}
});
});
Ext.create("Ametys.message.Message", {
type: Ametys.message.Message.SELECTION_CHANGED,
targets: {
id: Ametys.message.MessageTarget.CONTENT_FORM,
parameters: {
id: me._formId,
label: me._formLabel,
hasEntries: hasEntries,
workflowName: me._workflowName,
contentId: me.contentId
},
subtargets: subtargets
}
});
},
refresh: function()
{
this.showRefreshing();
Ametys.data.ServerComm.callMethod({
role: "org.ametys.plugins.forms.content.table.FormTableManager",
methodName: "getColumns",
parameters: [Ametys.getAppParameter('siteName'), this._contentId, this._formId],
callback: {
scope: this,
handler: this._getColumnsCb
},
errorMessage: "{{i18n PLUGINS_FORMS_UITOOL_GET_COLUMNS_ERROR}}"
});
},
/**
* Callback function invoked after retrieving form's properties (columns and fields).
* Loads entries after reconfiguring the grid.
* @param {Object} data The form's fields
* @private
*/
_getColumnsCb: function (data)
{
var oldPage = this._grid.getStore() && this._grid.getStore().currentPage || 1;
var oldSelection = this._grid.getSelection();
var columns = [
{text: "{{i18n PLUGINS_FORMS_UITOOL_COLUMN_ID}}", dataIndex: 'id', sortable: true, hideable: false, width: 50, align: 'center'},
{text: "{{i18n PLUGINS_FORMS_UITOOL_COLUMN_SUBMISSION_DATE}}", dataIndex: 'submission-date', sortable: true, hideable: true, width: 150, renderer: Ametys.grid.GridColumnHelper.renderDateTime},
{text: "{{i18n PLUGINS_FORMS_UITOOL_COLUMN_SUBMISSION_USER}}", dataIndex: 'user', sortable: true, hideable: true, width: 150, renderer: Ametys.grid.GridColumnHelper.renderUser}
];
var me = this;
Ext.Array.each (data, function (column) {
columns.push (me._getColumnConfig(column))
});
// Workflow step
columns.push({
text: "{{i18n PLUGINS_FORMS_UITOOL_COLUMN_WORKFLOW}}",
dataIndex: "workflowStep",
renderer: Ametys.plugins.cms.search.SearchGridHelper.renderWorkflowStep,
sortable: true,
hideable: true,
width: 120,
align: 'center'
});
this._fields = [];
this._fields.push({'name': 'entryId', type: 'int', mapping: 'id'});
this._fields.push({'name': 'submission-date', type: 'date', mapping: 'submission-date'});
Ext.Array.each (data, function (column) {
this._fields.push (me._getFieldConfig(column));
}, this);
this._fields.push({name: 'workflowStep', convert: Ametys.plugins.cms.search.SearchGridHelper.convertWorkflowStep});
var store = Ext.create('Ext.data.Store', {
autoLoad: false,
proxy: {
type: 'ametys',
url: 'form-content/entries.json',
plugin: 'forms',
reader: {
type: 'json',
rootProperty: 'entries',
totalProperty: 'total'
},
extraParams: {
siteName: Ametys.getAppParameter("siteName"),
id: this._formId,
contentId: this._contentId
}
},
pageSize: this.PAGE_SIZE,
fields: this._fields
});
this._grid.reconfigure(store, columns);
this._grid.getDockedItems('toolbar[dock="bottom"]')[0].bindStore(store);
store.loadPage(oldPage, {
callback: function(){
var newSelections = [];
for (var i = 0; i < oldSelection.length; i++)
{
var newSelection = this._grid.getStore().getById(oldSelection[i].getId());
if (newSelection)
{
newSelections.push(newSelection);
}
}
if (newSelections.length > 0)
{
this._grid.getSelectionModel().select(newSelections);
}
else
{
this.sendCurrentSelection();
}
this.showRefreshed();
},
scope: this
});
},
/**
* @private
* Return a column configuration from a definition described in JSON
* @param {Object} column the column definition object
* @return {Object} The column configuration
*/
_getColumnConfig: function (column)
{
var cfg = {
text: "<img style='vertical-align:middle;' src='" + this._getIconPath(column) + "' alt=''/> " + (column.label != '' ? column.label : column.name),
dataIndex: column.id,
align: 'left',
width: 200
};
var type = column.type.toLowerCase();
switch (type) {
case 'checkbox':
cfg.xtype = 'booleancolumn';
cfg.renderer = Ametys.grid.GridColumnHelper.renderBooleanIcon;
break;
case 'password':
cfg.renderer = this._renderPassword;
break;
case 'file':
cfg.renderer = Ext.bind(this._renderFile, this);
break;
case 'text':
case 'textarea':
case 'select':
case 'radio':
case 'hidden':
default:
break;
}
var regexptype = column.properties.regexptype;
switch (regexptype) {
case 'date':
cfg.xtype = 'datecolumn';
cfg.format = Ext.Date.patterns.LongDate;
cfg.width = 120;
break;
case 'datetime':
cfg.xtype = 'datecolumn';
cfg.format = Ext.Date.patterns.FriendlyDateTime;
cfg.width = 120;
break;
case 'time':
cfg.align = 'right';
cfg.width = 80;
break;
case 'int':
cfg.xtype = 'numbercolumn';
cfg.align = 'right';
cfg.width = 80;
cfg.format = '0';
break;
case 'float':
cfg.xtype = 'numbercolumn';
cfg.align = 'right';
cfg.width = 80;
break;
default:
break;
}
return cfg;
},
/**
* @private
* Return a field configuration from a definition described in JSON
* @param {Object} data The field definition
* @return {Object} The configuration object
*/
_getFieldConfig: function (data)
{
var cfg = {
name: data.id,
type: 'string'
}
var regexptype = data.properties.regexptype;
switch (regexptype) {
case 'date':
case 'datetime':
cfg.type = 'date';
break;
case 'int':
cfg.type = 'int';
break;
case 'float':
cfg.type = 'float';
break;
default:
break;
}
return cfg;
},
/**
* @private
* Get the path of the icon of a column
* @param {Object} column the column's properties
* @return the path of the node's icon
*/
_getIconPath: function (column)
{
var type = column.type;
if (type == "TEXT" || type == "RADIO" || type == "CHECKBOX" || type == "PASSWORD" || type == "FILE" || type == "HIDDEN")
{
return Ametys.getPluginResourcesPrefix('forms') + "/img/edition/input_" + type.toLowerCase() + "_16.png";
}
else if (type == "TEXTEREA")
{
return Ametys.getPluginResourcesPrefix('forms') + "/img/edition/textarea_16.png";
}
else if (type == "SELECT")
{
return Ametys.getPluginResourcesPrefix('forms') + "/img/edition/select_16.png";
}
return Ametys.getPluginResourcesPrefix('forms') + "/img/edition/input_text_16.png";
},
/**
* @private
* Renderer function for a password
* @param {String} value the value
* @return the html string for the given password value
*/
_renderPassword: function (value)
{
var pwd = "";
for (var i = 0; i < value.length; i++)
{
pwd += "*"
}
return pwd;
},
/**
* @private
* Renderer function for a file
* @param {String} value the value
* @param {Object} metaData A collection of metadata about the current cell
* @param {Ext.data.Model} record The record for the current row
* @param {Number} rowIndex The index of the current row
* @param {Number} colIndex The index of the current column
* @param {Ext.data.Store} store The data store
* @param {Ext.view.Table} view The data view
* @return the html string for the given file value
*/
_renderFile: function (value, metaData, record, rowIndex, colIndex , store, view)
{
if (value == null || value == '')
{
return value;
}
var extension = "unknown";
var index = value.lastIndexOf('.');
if (index > 0)
{
extension = value.substring(index + 1).toLowerCase();
}
var iconPath = Ametys.getPluginDirectPrefix('explorer') + "/icon/" + extension + ".png"
var fieldId = metaData.column.dataIndex || this._fields[colIndex - 1].name;
var entryId = record.data.id;
return "<a class='download' href='" + Ametys.getPluginDirectPrefix('forms') + "/" + Ametys.getAppParameter('siteName') + "/download/" + this._formId + "/" + entryId + "/" + fieldId + "/" + value + "'>" + "<img src='" + iconPath + "'/> " + value + "</a>";
},
/**
* @private
* Retrieve the form id
* @return the form id
*/
getFormId: function ()
{
return this._formId;
},
/**
* @private
* Listener upon reception of a deletion message
* @param {Ametys.message.Message} message the deletion message
*/
_onEntryDeleted: function(message)
{
var targets = message.getTargets(Ametys.message.MessageTarget.CONTENT_FORM_ENTRY);
if (targets.length > 0)
{
Ext.Array.forEach(targets, function(target) {
if (this._formId == target.getParameters().form)
{
this.showOutOfDate();
}
}, this);
}
},
/**
* @private
* Listener upon reception of a modification message
* @param {Ametys.message.Message} message the deletion message
*/
_onModified: function (message)
{
var target = message.getTarget(Ametys.message.MessageTarget.CONTENT_FORM);
if (target != null && this._formId == target.getParameters().id)
{
this.showOutOfDate();
}
}
});
Ext.define("Ametys.message.FormMessageTarget", {
override: "Ametys.message.MessageTarget",
statics:
{
/**
* @member Ametys.message.MessageTarget
* @readonly
* @property {String} CONTENT_FORM The target type is a form contained in a rich-text of a content. Parameters are:
* @property {String} CONTENT_FORM.id The unique identifier of the form
* @property {String} CONTENT_FORM.label The label of the form
* @property {Boolean} CONTENT_FORM.hasEntries does this form have any entries ?
* @property {String} CONTENT_FORM.workflowName the name of the workflow used for the submissions of this form
*/
CONTENT_FORM: "content-form",
/**
* @member Ametys.message.MessageTarget
* @readonly
* @property {String} CONTENT_FORM_ENTRY The target type is a entry of a content's form. Parameters are:
* @property {Number} CONTENT_FORM_ENTRY.id The identifier of the entry
* @property {String} CONTENT_FORM_ENTRY.form The identifier of the parent form
*/
CONTENT_FORM_ENTRY: "content-form-entry"
}
});