/*
* Copyright 2025 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 allows to search elements in the trash.
*/
Ext.define('Ametys.plugins.cms.trash.TrashSearchTool', {
extend: 'Ametys.plugins.cms.search.AbstractFacetedSearchTool',
constructor: function(config)
{
this.callParent(arguments);
Ametys.message.MessageBus.on(Ametys.message.Message.DELETED, this._onMessageDeleted, this);
},
getMBSelectionInteraction: function()
{
return Ametys.tool.Tool.MB_TYPE_ACTIVE;
},
_onSelectionChanged: function(sm, selected)
{
this.sendCurrentSelection();
},
sendCurrentSelection: function()
{
Ext.create('Ametys.message.Message', {
type: Ametys.message.Message.SELECTION_CHANGED,
targets: this.grid.getSelectionModel().getSelection().map(sel => ({
id: Ametys.message.MessageTarget.TRASH_ELEMENT,
parameters: {
id: sel.getId(),
title: sel.get('title'),
type: sel.get('trashType_enum')[sel.get('trashType')].label
}
}))
});
},
_getStoreCfg: function()
{
// The proxy of this store is automatically configured in the _configureProxy method
return {
remoteSort: true,
sortOnLoad: true,
listeners: {
'beforeload': {fn: this._onBeforeLoad, scope: this},
'load': {fn: this._onLoad, scope: this}
}
};
},
_getReaderCfg: function()
{
return {
type: 'json',
rootProperty: 'items',
transform: {
fn: this._transformSearchData,
scope: this
}
};
},
/**
* Transform search data before loading it. Used to extract facet results.
* @param data {Object} The original data object.
* @return {Object} The transformed data object.
* @private
*/
_transformSearchData: function(data)
{
this._facets = data.facets;
// Return the data untouched.
return data;
},
/**
* Function called before loading the store
* @param {Ext.data.Store} store The store
* @param {Ext.data.operation.Operation} operation The object that will be passed to the Proxy to load the store
* @protected
*/
_onBeforeLoad: function(_store, operation)
{
if (this.grid && !this._updatingModel && (!(this.form instanceof Ametys.form.ConfigurableFormPanel) || this.form.isFormReady()))
{
this.grid.getView().unmask();
if (!this.form.isValid())
{
this._stopSearch();
return false;
}
operation.setParams( Ext.apply(operation.getParams() || {}, {
model: this._modelId,
values: this.form.getJsonValues()
}));
// Facet handling
this.facetValues = this._faceting ? this.getFacetValues() : {};
operation.setParams(Ext.apply(operation.getParams(), {
facetValues: this.facetValues,
}));
this._facets = null;
}
else
{
// avoid use less requests at startup (applyState...)
return false;
}
},
_onLoad: function (store, records, successful, operation)
{
if (operation.aborted)
{
// Load has been canceled. Do nothing.
return;
}
// Hack to process groups locally even if remoteSort is enabled.
store.getData().setAutoGroup(true);
// Load facet store
if (this.facetPanel.isVisible() && this._facets)
{
if (!this._faceting)
{
// TODO Create a brand new memory proxy with the data and set it, instead of just changing the 'data' property?
this.facetPanel.getStore().getProxy().setData(this._facets);
this.facetPanel.getStore().load();
this.facetPanel.getRootNode().expand();
}
else
{
this._updateFacetPanel(this._facets);
}
}
if (records.length == 0)
{
this.grid.getView().mask("{{i18n plugin.cms:UITOOL_SEARCH_PAGEBAR_NO_RESULT}}", 'ametys-mask-unloading');
}
this._setGridDisabled(false);
},
_retrieveCriteriaAndColumns: function(force)
{
Ametys.data.ServerComm.callMethod({
role: "org.ametys.cms.trash.TrashManager",
methodName: "getSearchModel",
callback: {
handler: this._getSearchModelCb,
scope: this,
},
errorMessage: {
msg: "{{i18n plugin.cms:UITOOL_SEARCH_ERROR}}",
category: Ext.getClassName(this)
}
});
},
_getSearchModelCb: function(result)
{
this._configureSearchTool({"fieldsetForDisplay": {elements: result["criteria"], role: "fieldset"}}, result, this.getParams());
},
/**
* Listener when trash elements have been deleted
* Will set the tool in "out of date" mode
* @param {Ametys.message.Message} message The message.
* @protected
*/
_onMessageDeleted: function (message)
{
let params = message.getParameters();
if (params && params.trashed)
{
// Something has been added to the trash
this.showOutOfDate();
}
else
{
let targets = message.getTargets(Ametys.message.MessageTarget.TRASH, 1); // No need to dig subtargets
// The trash has been emptied
if (targets.length > 0)
{
this.showOutOfDate();
}
// Only some elements have been deleted
else
{
targets = message.getTargets(Ametys.message.MessageTarget.TRASH_ELEMENT, 1); // No need to dig subtargets
if (targets.length > 0)
{
Ext.Array.forEach(
targets,
function(target) {
let record = this.store.getById(target.getParameters().id);
this.grid.getSelectionModel().deselect([record]);
this.store.remove(record);
},
this
);
}
}
}
}
});
Ext.define("Ametys.message.TrashMessageTarget", {
override: "Ametys.message.MessageTarget",
statics:
{
/**
* @member Ametys.message.MessageTarget
* @readonly
* @property {String} TRASH The target is the trash.
*/
TRASH: "trash",
/**
* @member Ametys.message.MessageTarget
* @readonly
* @property {String} TRASH_ELEMENT The target is a trash element. The expected parameters are:
* @property {String} TRASH_ELEMENT.id The id of the trash element
* @property {String} TRASH_ELEMENT.title The title of the trash element
* @property {String} TRASH_ELEMENT.type The label of type of the trash element
*/
TRASH_ELEMENT: "trash-element"
}
});