/*
* Copyright 2017 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 details of an extraction
* @private
*/
Ext.define('Ametys.plugins.extraction.execution.ExtractionDetailsTool', {
extend: "Ametys.tool.Tool",
/**
* @readonly
* @property {Boolean} extractionTabCompatible Specify this tool is compatible with the 'extraction tab'.
*/
extractionTabCompatible: true,
/**
* @private
* @property {Ext.tree.Panel} _tree The tree panel displaying the extraction details
*/
/**
* @private
* @property {String} _definitionFilePath Definition file path.
*/
/**
* @private
* @property {String} _definitionFileName Definition file name.
*/
/**
* @private
* @property {String} _descriptionId Identifier of the description.
*/
/**
* @private
* @property {Object} _author the extraction definition author
* @property {String} _author.login the author's login
* @property {String} _author.populationId the author's population identifier
*/
/**
* @private
* @property {String} _canRead true if the current user can read the extraction definition
*/
/**
* @private
* @property {String} _canWrite true if the current user can write the extraction definition
*/
/**
* @private
* @property {String} _canAssignRights true if the current user can edit rights of the extraction definition
*/
/**
* @private
* @property {Boolean} _hasCreatedNodes <code>true</code> if some nodes have been created, <code>false</code> otherwise.
*/
/**
* @private
* @property {Boolean} _hasDeletedNodes <code>true</code> if some nodes have been deleted, <code>false</code> otherwise.
*/
/**
* @private
* @property {Boolean} _isConcernedByCurrentModification <code>true</code> if this tool is concerned by the las MODIFYNG sent message, <code>false</code> otherwise.
*/
/**
* @private
* @property {String} _oldFilePath the extraction definition old file path
*/
constructor: function(config)
{
this.callParent(arguments);
this._hasCreatedNodes = false;
this._hasDeletedNodes = false;
this._isConcernedByCurrentModification = false;
// Bus messages listeners
Ametys.message.MessageBus.on(Ametys.message.Message.MODIFYING, this._onDefinitionFilesModifying, this);
Ametys.message.MessageBus.on(Ametys.message.Message.MODIFIED, this._onDefinitionFilesModified, this);
Ametys.message.MessageBus.on(Ametys.message.Message.DELETED, this._onDefinitionFilesDeleted, this);
},
createPanel: function()
{
this._tree = Ext.create("Ext.tree.Panel", {
scrollable: true,
animate: true,
flex: 1,
cls : 'extraction-tree',
store: this._createTreeStore(),
root: {
text: "{{i18n PLUGINS_EXTRACTION_TREE_ROOT_NODE_DEFULT_TEXT}}",
tag: 'root',
iconCls: "ametysicon-puzzle-piece1",
expanded: true,
loaded: true
},
viewConfig: {
markDirty: false
},
listeners: {
selectionchange: Ext.bind(this.sendCurrentSelection, this),
itemdblclick: Ext.bind(this._editNode, this)
}
});
return this._tree;
},
getMBSelectionInteraction: function()
{
return Ametys.tool.Tool.MB_TYPE_ACTIVE;
},
/**
* @private
* Create the tree store
* @return {Ext.data.TreeStore} The created tree store
*/
_createTreeStore: function()
{
var store = Ext.create('Ext.data.TreeStore', {
model: 'Ametys.plugins.extraction.execution.ExtractionNodeEntry',
proxy: {
type: 'ametys',
serverCaller: this,
methodName: 'getExtractionDefinitionDetails',
methodArguments: ['definitionFilePath'],
reader: {
type: 'json',
messageProperty: 'file-error'
}
},
listeners: {
'beforeload': this._onBeforeStoreLoad,
'update': this._onStoreUpdate,
scope: this
},
autoLoad: false
});
return store;
},
/**
* @private
* Fired when a Model instance has been updated.
* @param {Ext.data.Store} store The store
* @param {Ext.data.Model} record The Model instance that was updated
* @param {String} operation The update operation being performed. Value may be one of:
*
* Ext.data.Model.EDIT
* Ext.data.Model.REJECT
* Ext.data.Model.COMMIT
* @param {String[]} modifiedFieldNames Array of field names changed during edit.
* @param {Object} details An object describing the change. See the {@link Ext.util.Collection#event-itemchange itemchange event} of the store's backing collection
*/
_onStoreUpdate: function(store, record, operation, modifiedFieldNames, details)
{
this.setDirty(store.findBy(function(n) { return n.dirty; }) != -1 || this._hasCreatedNodes || this._hasDeletedNodes);
},
/**
* Listener when definition files are beeing modifying
* @param {Ametys.message.Message} message The edition message.
* @private
*/
_onDefinitionFilesModifying: function(message)
{
var target = message.getTarget(Ametys.message.MessageTarget.EXTRACTION_DEFINITION_FILE) || message.getTarget(Ametys.message.MessageTarget.EXTRACTION_DEFINITION_FOLDER);
if (target != null)
{
this._isConcernedByCurrentModification = Ext.String.startsWith(this._definitionFilePath, target.getParameters().path);
this._oldFilePath = target.getParameters().path;
}
},
/**
* Listener when definition files have been edited
* Will refresh the tool
* @param {Ametys.message.Message} message The edition message.
* @private
*/
_onDefinitionFilesModified: function(message)
{
if (this._isConcernedByCurrentModification)
{
var fileTarget = message.getTarget(Ametys.message.MessageTarget.EXTRACTION_DEFINITION_FILE);
var folderTarget = message.getTarget(Ametys.message.MessageTarget.EXTRACTION_DEFINITION_FOLDER);
if (fileTarget != null)
{
this._definitionFilePath = fileTarget.getParameters().path;
this._definitionFileName = fileTarget.getParameters().name;
// Get the description id from the target
var descriptionTarget = fileTarget.getSubtarget(Ametys.message.MessageTarget.CONTENT);
var description = descriptionTarget ? descriptionTarget.getParameters().content : undefined;
this._descriptionId = description ? description.getId() : undefined
this._definitionAuthor = fileTarget.getParameters().auhtor;
this._definitionCanRead = fileTarget.getParameters().canRead;
this._definitionCanWrite = fileTarget.getParameters().canWrite;
this._definitionCanDelete = fileTarget.getParameters().canDelete;
this._definitionCanEditRight = fileTarget.getParameters().canAssignRights;
this.refresh(false, message.getParameters().major);
}
else if (folderTarget != null)
{
this._definitionFilePath = this._definitionFilePath.replace(this._oldFilePath, folderTarget.getParameters().path);
this.refresh(false, message.getParameters().major);
}
}
},
/**
* Listener when definition files have been deleted
* @param {Ametys.message.Message} message The deletion message.
* @private
*/
_onDefinitionFilesDeleted: function(message)
{
var target = message.getTarget(Ametys.message.MessageTarget.EXTRACTION_DEFINITION_FILE) || message.getTarget(Ametys.message.MessageTarget.EXTRACTION_DEFINITION_FOLDER);
if (target != null)
{
if (Ext.String.startsWith(this._definitionFilePath, target.getParameters().path))
{
this._closeCb();
}
}
},
sendCurrentSelection: function()
{
var targetParameters = {
path: this._definitionFilePath,
name: this._definitionFileName,
descriptionId: this._descriptionId,
author: this._author,
canRead: this._canRead,
canWrite: this._canWrite,
canAssignRights: this._canAssignRights
}
var selection = this._tree.getSelectionModel().getSelection();
if (selection.length > 0)
{
var node = selection[0];
targetParameters.nodeTag = node.get('tag');
}
Ext.create('Ametys.message.Message', {
type: Ametys.message.Message.SELECTION_CHANGED,
targets: {
id: Ametys.message.MessageTarget.EXTRACTION_DEFINITION_FILE,
parameters: targetParameters
}
});
},
/**
* @private
* Set params to send definition file path to the callable
* @param {Ext.data.Store} store This Store
* @param {Ext.data.operation.Operation} operation The Ext.data.operation.Operation object that will be passed to the Proxy to
*/
_onBeforeStoreLoad: function(store, operation)
{
operation.setParams(Ext.apply(operation.getParams() || {}, {
'definitionFilePath': this._definitionFilePath
}));
},
setParams: function(params)
{
this.callParent(arguments);
this._definitionFilePath = params['id']
this._definitionFileName = params['name'];
this._descriptionId = params['descriptionId'];
this._author = params['author'];
this._canRead = params['canRead'];
this._canWrite = params['canWrite'];
this._canDelete = params['canDelete'];
this._canAssignRights = params['canAssignRights'];
this.refresh(false, true);
},
refresh: function(manual, isMajorModification)
{
this.showRefreshing();
this.setTitle(this._definitionFilePath);
this._tree.getRootNode().set('text', this._definitionFilePath, {dirty: false});
if (isMajorModification)
{
if (manual)
{
Ametys.Msg.confirm("{{i18n PLUGINS_EXTRACTION_REFRESH_DETAILS_TOOL_CONFIRM_DIALOG_TITLE}}",
"{{i18n PLUGINS_EXTRACTION_REFRESH_DETAILS_TOOL_CONFIRM_DIALOG_MSG}}",
Ext.bind(this._refreshTree, this),
this
);
}
else
{
this._refreshTree();
}
}
else
{
this.showRefreshed();
}
},
/**
* Refreshes the tree store
* @param {String} btn which button was pressed
*/
_refreshTree: function(btn)
{
if (!btn || btn == 'yes')
{
this._hasCreatedNodes = false;
this._hasDeletedNodes = false;
var selection = this._tree.getSelectionModel().getSelection()[0];
this._tree.getStore().load({
callback: Ext.bind(this._refreshTreeCb, this, [selection], 0)
});
}
else
{
this.showRefreshed();
}
},
/**
* @private
* Callback function after #refresh is processed
* @param {Ext.data.Model} selection The selection
* @param {Ext.data.TreeModel[]} records An array of records.
* @param {Ext.data.Operation} operation The operation that triggered this load.
* @param {Boolean} success True if the operation was successful.
*/
_refreshTreeCb: function(selection, records, operation, success)
{
if (success)
{
this._selectNode(selection)
}
else
{
if (operation.error == 'unexisting')
{
this.close();
var description = Ext.String.format("{{i18n PLUGINS_EXTRACTION_LOAD_UNEXISTING_FILE_NOTIF_DESCRIPTION}}", this._definitionFilePath);
Ametys.notify({
title: "{{i18n PLUGINS_EXTRACTION_LOAD_UNEXISTING_FILE_NOTIF_TITLE}}",
description: description
});
}
}
this.showRefreshed();
},
/**
* Create an new node below the selected node
* @param {String} tag The tag of the component to create
*/
createNode: function(tag)
{
var selection = this._tree.getSelectionModel().getSelection();
if (selection.length > 0)
{
var parentNode = selection[0];
switch (tag)
{
case 'query':
Ametys.plugins.extraction.edition.EditQueryExtractionComponentDialog.add(Ext.bind(this._createNodeCb, this, [parentNode, tag], 1));
break;
case 'thesaurus':
Ametys.plugins.extraction.edition.EditThesaurusExtractionComponentDialog.add(Ext.bind(this._createNodeCb, this, [parentNode, tag], 1));
break;
case 'count':
Ametys.plugins.extraction.edition.EditCountExtractionComponentDialog.add(Ext.bind(this._createNodeCb, this, [parentNode, tag], 1));
break;
case 'mapping-query':
Ametys.plugins.extraction.edition.EditMappingQueryExtractionComponentDialog.add(Ext.bind(this._createNodeCb, this, [parentNode, tag], 1));
break;
default:
break;
}
}
},
/**
* @private
* Callback on node creation
* @param {Object} formValues The values from creation form
* @param {Object} parentNode The parent node of the ont to create
* @param {String} tag The tag of the component to create
*/
_createNodeCb: function(formValues, parentNode, tag)
{
var iconCls;
switch (tag)
{
case 'query':
iconCls = "ametysicon-query-search";
break;
case 'thesaurus':
iconCls = "ametysicon-books";
break;
case 'count':
iconCls = "ametysicon-abacus";
break;
case 'mapping-query':
iconCls = "ametysicon-squares";
break;
default:
break;
}
var node = {
text: formValues.componentTagName || tag,
tag: tag,
data: formValues,
leaf: true,
iconCls: iconCls
};
this._hasCreatedNodes = true;
parentNode.appendChild(node, false, true);
this._selectNode(node);
},
/**
* Edit the selected extraction node
*/
editSelectedNode: function()
{
var selection = this._tree.getSelectionModel().getSelection();
if (selection.length > 0)
{
this._editNode(null, selection[0]);
}
},
/**
* @private
* Edit an extraction node on double-click event
* @param {Ext.view.View} view The tree view
* @param {Ext.data.Model} node The node to edit
*/
_editNode: function(view, node)
{
var tag = node.get('tag');
var data = node.get('data');
switch (tag)
{
case 'query':
Ametys.plugins.extraction.edition.EditQueryExtractionComponentDialog.edit(data, Ext.bind(this._editNodeCb, this, [node], 1));
break;
case 'thesaurus':
Ametys.plugins.extraction.edition.EditThesaurusExtractionComponentDialog.edit(data, Ext.bind(this._editNodeCb, this, [node], 1));
break;
case 'count':
Ametys.plugins.extraction.edition.EditCountExtractionComponentDialog.edit(data, Ext.bind(this._editNodeCb, this, [node], 1));
break;
case 'mapping-query':
Ametys.plugins.extraction.edition.EditMappingQueryExtractionComponentDialog.edit(data, Ext.bind(this._editNodeCb, this, [node], 1));
break;
case 'clauses-variables':
Ametys.plugins.extraction.edition.EditClausesVariablesNodeDialog.edit(data, Ext.bind(this._editNodeCb, this, [node], 1));
break;
case 'optional-columns':
Ametys.plugins.extraction.edition.EditOptionalColumnsNodeDialog.edit(data, Ext.bind(this._editNodeCb, this, [node], 1));
break;
default:
break;
}
},
/**
* @private
* Callback on node edition
* @param {Object} formValues The values from edition form
* @param {Object} node The node to edit
*/
_editNodeCb: function(formValues, node)
{
switch (node.get('tag'))
{
case 'query':
case 'count':
case 'mapping-query':
case 'thesaurus':
node.set('text', formValues.componentTagName || node.get('tag'));
break;
default:
break;
}
node.set('data', formValues);
},
/**
* Delete the selected extraction node
*/
deleteSelectedNode: function()
{
var selection = this._tree.getSelectionModel().getSelection();
if (selection.length > 0)
{
var node = selection[0];
var message = Ext.String.format("{{i18n PLUGINS_EXTRACTION_DELETE_NODE_CONFIRM_DIALOG_MSG}}", node.get('text'));
Ametys.Msg.confirm("{{i18n PLUGINS_EXTRACTION_DELETE_NODE_CONFIRM_DIALOG_TITLE}}",
message,
Ext.bind(this._doDeleteNode, this, [node], 1),
this
);
}
},
/**
* Delete an extraction node
* @param {String} btn which button was pressed
* @param {Object} node The node to delete
*/
_doDeleteNode: function(btn, node)
{
if (btn == 'yes')
{
this._hasDeletedNodes = true;
node.remove();
}
},
/**
* Save modifications on the current extraction
* @param {Function} [callback] The callback function to execute
* @param {Boolean} [callback.success] True if the saving succeeded
*/
saveExtraction: function(callback)
{
var encodedDefinitionFilePath = encodeURIComponent(this._definitionFilePath);
var extraction = this._convertNodeToJSON(this._tree.getRootNode());
Ext.create('Ametys.message.Message', {
type: Ametys.message.Message.MODIFYING,
targets: {
id: Ametys.message.MessageTarget.EXTRACTION_DEFINITION_FILE,
parameters: {
path: this._definitionFilePath,
name: this._definitionFileName,
descriptionId: this._descriptionId,
author: this._author,
canRead: this._canRead,
canWrite: this._canWrite,
canDelete: this._canDelete,
canAssignRights: this._canAssignRights
}
}
});
this.serverCall('saveExtraction', [encodedDefinitionFilePath, extraction], Ext.bind(this._saveExtractionCb, this, [callback], 1));
},
/**
* Converts a node to JSON
* @param {Object} node The node to convert
* @return {Object} JSON conversion of the node
*/
_convertNodeToJSON: function(node)
{
var obj = {};
obj.tag = node.get('tag');
obj.data = node.get('data');
obj.children = [];
Ext.Array.forEach(node.childNodes, function(childNode) {
obj.children.push(this._convertNodeToJSON(childNode));
}, this);
return obj;
},
/**
* @private
* Callback on extraction saving
* @param {boolean} success <code>true</code> if extraction saving succeed, <code>false</code> otherwise
* @param {Function} [callback] The callback function to execute
* @param {Boolean} [callback.success] True if the saving succeeded
*/
_saveExtractionCb: function(success, callback)
{
if (!success)
{
Ametys.Msg.show({
title: "{{i18n PLUGINS_EXTRACTION_SAVE_EXTRACTION_ERROR_DIALOG_TITLE}}",
msg: "{{i18n PLUGINS_EXTRACTION_SAVE_EXTRACTION_ERROR_DIALOG_MSG}}",
buttons: Ext.Msg.OK,
icon: Ext.MessageBox.ERROR
});
}
else
{
Ext.create('Ametys.message.Message', {
type: Ametys.message.Message.MODIFIED,
parameters: {
major: true
},
targets: {
id: Ametys.message.MessageTarget.EXTRACTION_DEFINITION_FILE,
parameters: {
path: this._definitionFilePath,
name: this._definitionFileName,
descriptionId: this._descriptionId,
author: this._author,
canRead: this._canRead,
canDelete: this._canDelete,
canWrite: this._canWrite,
canAssignRights: this._canAssignRights
}
}
});
}
if (Ext.isFunction(callback))
{
callback(success);
}
},
/**
* Cancel modifications on the current extraction
*/
cancelModifications: function()
{
this.refresh(true, true);
},
/**
* Selects the specified node.
* @param {Ext.data.Model} selection The node to select
* @private
*/
_selectNode: function(selection)
{
if (selection)
{
var node = this._tree.getStore().getNodeById(selection.id);
if (node)
{
this._tree.setSelection(node);
this._tree.ensureVisible(node.getPath());
}
else
{
this._tree.setSelection(this._tree.getRootNode());
}
}
else
{
this._tree.setSelection(this._tree.getRootNode());
}
},
close: function()
{
if (this.isDirty())
{
Ametys.Msg.confirm("{{i18n PLUGINS_EXTRACTION_CLOSE_DETAILS_TOOL_CONFIRM_DIALOG_TITLE}}",
"{{i18n PLUGINS_EXTRACTION_CLOSE_DETAILS_TOOL_CONFIRM_DIALOG_MSG}}",
Ext.bind(this._closeCb, this),
this
);
}
else
{
this._closeCb();
}
},
/**
* CLose the tool if user confirmed
* @param {String} btn which button was pressed
*/
_closeCb: function(btn)
{
if (!btn || btn == 'yes')
{
Ametys.tool.ToolsManager.removeTool(this);
}
}
});