/*
* 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 does allow the user to perform searches on resources.
* @private
*/
Ext.define('Ametys.plugins.explorer.applications.resources.ExplorerSearchTool', {
extend: 'Ametys.plugins.explorer.applications.resources.ResourceCollectionTool',
inheritableStatics: {
/**
* @property {String} INTERNAL_PREFIX an internal prefix
* @private
* @readonly
*/
INTERNAL_PREFIX: 'search-app-'
},
statics: {
/**
* @property {Number}
* @readonly
* @static
* The max height of search form
*/
FORM_MAX_HEIGHT: 300,
/**
* @property {Number}
* @readonly
* @static
* The min height of search form
*/
FORM_MIN_HEIGHT: 100,
/**
* @property {Number}
* @readonly
* @static
* The default ratio of form's height
*/
FORM_HEIGHT_RATIO: 1/3,
/**
* @property {Number}
* @readonly
* @static
* The max ratio of form's height
*/
FORM_MAX_HEIGHT_RATIO: 2/5
},
/**
* @private
* @property {Ext.Template} _hintTpl The template for hint
*/
constructor: function(config)
{
this.callParent(arguments);
this._searchPlugin = this.getInitialConfig('search-plugin') || this.getPluginName();
this._searchUrl = this.getInitialConfig('search-url') || 'resource/search';
this._hintTpl = this.getHintTemplate();
},
createPanel: function()
{
this._store = this._createStore();
this._thumbnailView = this._createThumbnailView();
this._detailsView = this._createDetailsView();
this.searchPanel = this._createFormPanel();
this.cardView = Ext.create ('Ext.Panel', {
split: true,
xtype: 'panel',
flex: 1,
border: false,
layout: 'card',
activeItem : 0,
items: [this._detailsView, this._thumbnailView]
});
var me = this;
this.mainPanel = Ext.create('Ext.panel.Panel', {
layout: {
type: 'vbox',
align: 'stretch'
},
border: false,
dockedItems: [{
dock: 'top',
xtype: 'component',
ui: 'tool-hintmessage',
itemId: 'folder-hint-message',
html: ''
}],
items: [
this.searchPanel,
this.cardView
],
listeners: {
'resize': function (p, width, height)
{
// Set the max height of form panel
var maxHeight = Math.min(Ametys.plugins.explorer.applications.resources.ExplorerSearchTool.FORM_MAX_HEIGHT, parseInt(height * Ametys.plugins.explorer.applications.resources.ExplorerSearchTool.FORM_MAX_HEIGHT_RATIO));
me.searchPanel.maxHeight = maxHeight;
// Try to keep the current ratio if exists
if (me.searchPanel._heightRatio)
{
me.searchPanel.setHeight(me.searchPanel._heightRatio * height);
}
}
}
});
return this.mainPanel;
},
/**
* Create the form panel that holds the search criteria
* @return {Ext.form.Panel} The form panel
*/
_createFormPanel: function()
{
var formCfg = {
scrollable: true,
fieldsetLayout: {
type: 'column'
},
defaultFieldConfig: {
labelWidth: 120,
width: 350/*,
margin: '0 10 4 0'*/
},
additionalWidgetsConfFromParams: {
contentType: 'contentType' // some widgets requires the contentType configuration
},
withTitleOnLabels: true,
};
this.form = Ext.create ('Ametys.form.ConfigurableFormPanel', formCfg);
this.form.configure(this._getSearchCriteria());
this.form.setValues({});
var me = this;
return Ext.create ('Ext.Panel', {
split: true,
border: false,
ui: 'light',
header: {
titlePosition: 1
},
stateful: true,
stateId: this.getId() + '$explorer-search-form',
title: "{{i18n PLUGINS_EXPLORER_UITOOL_SEARCH_CRITERIA}}",
collapsible: true,
titleCollapse: true,
animCollapse: false,
minHeight: Ametys.plugins.explorer.applications.resources.ExplorerSearchTool.FORM_MIN_HEIGHT,
maxHeight: Ametys.plugins.explorer.applications.resources.ExplorerSearchTool.FORM_MAX_HEIGHT,
items: [ this.form ],
bbar: this._getSearchFormPanelBBar(),
listeners: {
'resize': function (cmp, width, height)
{
if (this._oldHeight && height != this._oldHeight)
{
// The panel was manually resized: save new ratio
this._oldHeight = height;
this._heightRatio = height / me.mainPanel.getHeight();
}
}
},
applyState: function (state)
{
this._heightRatio = state.ratio;
},
getState: function ()
{
// Save the height ratio
return {
ratio: this._heightRatio
}
}
});
},
/**
* Get the JSON configuration for search criteria
* @return The search criteria
*/
_getSearchCriteria: function ()
{
return {
"fieldset" : {
"role": "fieldset",
"elements": {
"name" : {
label: "{{i18n PLUGINS_EXPLORER_SEARCH_NAME}}",
description: "{{i18n PLUGINS_EXPLORER_SEARCH_NAME_DESC}}",
type: 'string'
},
"fulltext" : {
label: "{{i18n PLUGINS_EXPLORER_SEARCH_TEXTFIELD}}",
description: "{{i18n PLUGINS_EXPLORER_SEARCH_TEXTFIELD_DESC}}",
type: 'string'
},
"author" : {
label: "{{i18n PLUGINS_EXPLORER_SEARCH_AUTHOR}}",
description: "{{i18n PLUGINS_EXPLORER_SEARCH_AUTHOR_DESC}}",
type: 'user'
},
"keywords" : {
label: "{{i18n PLUGINS_EXPLORER_SEARCH_KEYWORDS}}",
description: "{{i18n PLUGINS_EXPLORER_SEARCH_KEYWORDS_DESC}}",
type: 'string'
}
}
}
}
},
/**
* @protected
* Get the items of the optional bottom bar of the criteria panel
* @return {Array} The bottom bar configuration array
*/
_getSearchFormPanelBBar: function()
{
var items = [{
// Search
itemId: 'search',
text: "{{i18n PLUGINS_EXPLORER_SEARCH_BUTTON_SEARCH}}",
icon: Ametys.getPluginResourcesPrefix('explorer') + '/img/search/icon_small.png',
handler: this._launchSearch,
scope: this,
tooltip: {
title: "{{i18n PLUGINS_EXPLORER_SEARCH_BUTTON_SEARCH}}",
text: "{{i18n PLUGINS_EXPLORER_SEARCH_BUTTON_SEARCH_DESC}}",
image: Ametys.getPluginResourcesPrefix('explorer') + '/img/search/icon_large.png',
inribbon: false
}
},
'-',
{
// Cancel
itemId: 'cancel',
text: "{{i18n PLUGINS_EXPLORER_SEARCH_BUTTON_CANCEL}}",
icon: Ametys.getPluginResourcesPrefix('explorer') + '/img/search/cancel_16.png',
handler: this._stopSearch,
scope: this,
disabled: true,
tooltip: {
title: "{{i18n PLUGINS_EXPLORER_SEARCH_BUTTON_CANCEL}}",
text: "{{i18n PLUGINS_EXPLORER_SEARCH_BUTTON_CANCEL_DESC}}",
image: Ametys.getPluginResourcesPrefix('explorer') + '/img/search/cancel_48.png',
inribbon: false
}
}];
return items;
},
/**
* Launch the search via the launch button
* @private
*/
_launchSearch: function()
{
this._load();
},
/**
* @inheritdoc
*/
_createStore: function()
{
return Ext.create('Ext.data.Store', {
autoDestroy: true,
model: 'Ametys.plugins.explorer.applications.resources.ResourcesApplication.ResourceEntry',
proxy: {
type: 'ametys',
plugin: this._searchPlugin,
url: this._searchUrl,
reader: {
type: 'xml',
record: '> Node'
}
},
listeners: {
load: {fn: this._onLoad, scope: this}
},
sorters: [{property: 'name'}]
});
},
/**
* @inheritdoc
*/
_createDetailsView: function ()
{
return Ext.create('Ametys.plugins.explorer.view.SearchDetailsViewer', {
itemId : this.self.INTERNAL_PREFIX + 'resources-details-view',
stateful: true,
stateId: this.getId() + "$grid",
store : this.getStore(),
border: false,
columns: this._getColumns(),
listeners: {
activate: {fn: this._onViewActivate, scope: this},
selectionchange: {fn: this._onSelectFiles, scope: this},
itemdblclick: {fn: this._onDblClick, scope: this}
}
});
},
/**
* @protected
* Create the columns configuration array.
* @return {Object[]} An array of column definition object. See {@link Ext.grid.column.Column}
*/
_getColumns: function()
{
return [
{text: "{{i18n UITOOL_EXPLORER_APP_RESOURCES_COL_NAME}}", stateId: 'name', width: 250, dataIndex: 'name', hideable: false, renderer: this._renderFilename, scope: this,
editor: {
xtype: 'textfield',
allowBlank: false,
selectOnFocus: true
}
},
{text: "{{i18n UITOOL_EXPLORER_APP_RESOURCES_COL_KEYWORDS}}", stateId: 'keywords', width: 100, dataIndex: 'keywords', hidden: true},
{text: "{{i18n UITOOL_EXPLORER_APP_RESOURCES_COL_LOCATION}}", stateId: 'path', width: 150, dataIndex: 'path', renderer: this._renderFilePath, scope: this, hidden: false},
{text: "{{i18n UITOOL_EXPLORER_APP_RESOURCES_COL_DATE}}", stateId: 'lastModified', width: 130, dataIndex: 'lastModified', renderer: Ext.util.Format.dateRenderer('d/m/Y H:i')},
{text: "{{i18n UITOOL_EXPLORER_APP_RESOURCES_COL_AUTHOR}}", stateId: 'author', width: 200, dataIndex: 'author'},
{text: "{{i18n UITOOL_EXPLORER_APP_RESOURCES_COL_SIZE}}", stateId: 'size', width: 90, dataIndex: 'size', renderer: Ext.util.Format.fileSize, align: 'right'}
];
},
/**
* @inheritdoc
* @param {Object} params The new parameters. Depends on the tool implementation.
*/
setParams: function (params)
{
if (!this._parentPath || this._parentPath != params.path)
{
this._parentPath = params.path;
this._rootId = params.rootId;
}
this.callParent(arguments);
},
/**
* The first auto load
*/
_initLoad: function()
{
// no auto load
},
sendCurrentSelection: function()
{
var targets = [];
var selection = this._getSelection();
if (selection.length > 0)
{
ids = Ext.Array.map(selection, function(resource) {
return resource.getId();
});
targets = {
id: Ametys.message.MessageTarget.RESOURCE,
parameters: { ids: ids }
};
}
Ext.create("Ametys.message.Message", {
type: Ametys.message.Message.SELECTION_CHANGED,
targets: targets
});
},
/**
* Load and displays the file in the grid
* @param {Object} [loadParams] the load params
*/
_load: function(loadParams)
{
var form = this.form.getForm();
if (form.isValid())
{
var values = Ext.apply(form.getValues() || {}, {
rootId: this._rootId
})
loadParams = Ext.apply(loadParams || {}, {
params: values
});
this.showUpToDate();
this._setGridDisabled(true);
this.callParent([loadParams]);
this._message = Ametys.data.ServerComm._messages[Ametys.data.ServerComm._messages.length - 1];
}
},
/**
* Stop the search in progress
* @private
*/
_stopSearch: function()
{
if (this._message)
{
this._message.cancelCode = this.getId() + '$cancelCode';
this._message.uniqueId = this.getId() + '$' + Ext.id();
this._message.cancelled = true;
Ametys.data.ServerComm._lastUniqueIdForCancelCode[this._message.cancelCode] = '';
}
this._setGridDisabled(false);
},
/**
* Enable or disable the grid columns and paging toolbar
* @param {Boolean} disabled true to disable.
* @private
*/
_setGridDisabled: function (disabled)
{
this.searchPanel.getDockedItems("toolbar")[0].items.get(0).setDisabled(disabled);
this.searchPanel.getDockedItems("toolbar")[0].items.get(2).setDisabled(!disabled);
var columns = this._detailsView.getColumns();
Ext.Array.each (columns, function (column) {
column.setDisabled(disabled);
})
},
/**
* @private
* Listener called after store is loaded
* @param {Ext.data.Store} store The store
* @param {Ext.data.Model[]} records An array of records
* @param {Boolean} successful True if the operation was successful.
*/
_onLoad: function(store, records, successful)
{
this._message = null;
this._setGridDisabled(false);
},
/**
* Update tool information
* @protected
*/
_updateInfos: function()
{
var me = this;
Ametys.explorer.ExplorerNodeDAO.getExplorerNode(this._parentId, function(explorerNode) {
var data = explorerNode.getProperties();
var name = me._getFolderName(data);
me.setTitle(me.getInitialConfig('title') + " (" + name + ")");
var html = me.getHintText(data);
var hintPanel = me.getContentPanel().getDockedItems('#folder-hint-message')[0];
if (hintPanel)
{
hintPanel.update(html);
}
});
},
/**
* @protected
* Get the template for hint
* @return {Ext.Template} The template used for hint
*/
getHintTemplate: function ()
{
return Ext.create('Ext.Template', Ext.String.format("{{i18n PLUGINS_EXPLORER_SEARCH_TOOL_HINT}}", '<strong>{name}</strong>'));
},
/**
* @protected
* Get the hint text
* @param {Object} data The folder data
*/
getHintText: function (data)
{
return this._hintTpl.applyTemplate({
name: data.name
});
},
/**
* Get the folder's name
* @param {Object} data The node's properties
* @return The folder's name
*/
_getFolderName: function(data)
{
if (this._rootId == data.id)
{
return "{{i18n plugin.explorer:PLUGINS_EXPLORER_ROOT_NODE}}";
}
return data.name;
},
/**
* Listener on {@link Ametys.message.Message#CREATED} message.
* If the tool is concerned by the created object, the view will be refreshed
* @param {Ametys.message.Message} message The created message.
* @private
*/
_onResourceCreated: function (message)
{
if (this.isOutOfDate() != Ametys.tool.Tool.OOD_UPTODATE)
{
return;
}
var collectionTarget = message.getTarget(Ametys.message.MessageTarget.EXPLORER_COLLECTION);
if (collectionTarget)
{
if (this._sameRootId(collectionTarget.getParameters().rootId) && Ext.String.startsWith(collectionTarget.getParameters().path, this._parentPath))
{
this.showOutOfDate();
return;
}
}
var resourceTargets = message.getTargets(Ametys.message.MessageTarget.RESOURCE);
if (resourceTargets.length > 0)
{
for (var i = 0; i < resourceTargets.length; i++)
{
var resourceTarget = resourceTargets[i];
if (this._sameRootId(resourceTarget.getParameters().rootId) && Ext.String.startsWith(resourceTarget.getParameters().path, this._parentPath))
{
this.showOutOfDate();
return;
}
}
}
},
/**
* Listener on {@link Ametys.message.Message#DELETED} message.
* If the tool is concerned by the deleted object, the corresponding row will be removed
* @param {Ametys.message.Message} message The deleted message.
* @private
*/
_onResourceDeleted: function(message)
{
var resourceTargets = message.getTargets(Ametys.message.MessageTarget.RESOURCE);
if (resourceTargets.length > 0)
{
var ids = [];
for (var i = 0; i < resourceTargets.length; i++)
{
var resourceTarget = resourceTargets[i];
if (this._sameRootId(resourceTarget.getParameters().rootId) && Ext.String.startsWith(resourceTarget.getParameters().path, this._parentPath))
{
ids.push(resourceTarget.getParameters().id);
}
}
this._deleteEntries(ids);
}
if (this.isOutOfDate() != Ametys.tool.Tool.OOD_UPTODATE)
{
return;
}
var collectionTarget = message.getTarget(Ametys.message.MessageTarget.EXPLORER_COLLECTION);
if (collectionTarget)
{
if (collectionTarget.getParameters().id == this._parentId)
{
// Async tool removal
var me = this;
setTimeout(function() {
Ametys.tool.ToolsManager.removeTool(me);
}, 0);
}
else
{
if (this._sameRootId(collectionTarget.getParameters().rootId) && Ext.String.startsWith(collectionTarget.getParameters().path, this._parentPath))
{
this.showOutOfDate();
return;
}
}
}
},
/**
* Listener on {@link Ametys.message.Message#MOVED} message.
* If the tool is concerned by the moved object, the new parent node will be refreshed.
* @param {Ametys.message.Message} message The moved message.
* @private
*/
_onResourceMoved: function(message)
{
if (this.isOutOfDate() != Ametys.tool.Tool.OOD_UPTODATE)
{
return;
}
var collectionTarget = message.getTarget(Ametys.message.MessageTarget.EXPLORER_COLLECTION);
if (collectionTarget)
{
if (this._sameRootId(collectionTarget.getParameters().rootId) && Ext.String.startsWith(collectionTarget.getParameters().path, this._parentPath))
{
this.showOutOfDate();
return;
}
}
var resourceTargets = message.getTargets(Ametys.message.MessageTarget.RESOURCE);
if (resourceTargets.length > 0)
{
for (var i = 0; i < resourceTargets.length; i++)
{
var resourceTarget = resourceTargets[i];
if (this._sameRootId(resourceTarget.getParameters().rootId) && Ext.String.startsWith(resourceTarget.getParameters().path, this._parentPath))
{
this.showOutOfDate();
return;
}
}
}
},
/**
* Listener on {@link Ametys.message.Message#MODIFIED} message.
* If the tool is concerned by the modified object, the node of modified object will be updated.
* @param {Ametys.message.Message} message The modified message.
* @private
*/
_onResourceModified: function(message)
{
if (this.isOutOfDate() != Ametys.tool.Tool.OOD_UPTODATE)
{
return;
}
var collectionTarget = message.getTarget(Ametys.message.MessageTarget.EXPLORER_COLLECTION);
if (collectionTarget)
{
if (this._sameRootId(collectionTarget.getParameters().rootId) && Ext.String.startsWith(collectionTarget.getParameters().path, this._parentPath))
{
this.showOutOfDate();
return;
}
}
var resourceTargets = message.getTargets(Ametys.message.MessageTarget.RESOURCE);
if (resourceTargets.length > 0)
{
for (var i = 0; i < resourceTargets.length; i++)
{
var resourceTarget = resourceTargets[i];
if (this._sameRootId(resourceTarget.getParameters().rootId) && Ext.String.startsWith(resourceTarget.getParameters().path, this._parentPath))
{
this.showOutOfDate();
return;
}
}
}
},
/**
* Determines if the root id argument is the same that the root id of the search tool.
* @param {String} rootId The root id to check
* @return True if same
* @private
*/
_sameRootId: function(rootId)
{
return this._rootId = rootId;
}
});