/*
* 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 class provides a widget to define a sort order based on available metadata.
*/
Ext.define('Ametys.cms.form.widget.SelectMetadataToSortBy', {
extend: 'Ametys.form.AbstractQueryableComboBox',
/**
* @cfg {String} [metadataSetName=main] The desired metadataset name
*/
metadataSetName: 'main',
/**
* @cfg {String} [contentTypesField] The relative path the content types
* field, to allow the list of metadata to be updated given the
* value of the content types field
*/
/**
* @private
* @property {String} _contentTypesFieldName The property related to {@link #cfg-contentTypesField}
*/
/**
* @cfg {String} [metadataSetField] The relative path the content field, to
* allow the list of metadata to be updated given the value of the
* metadata set field
*/
/**
* @private
* @property {String} _metadataSetFieldName The property related to {@link #cfg-metadataSetField}
*/
valueField: 'name',
displayField: 'fullLabel',
/**
* @private
* @property {String} iconSortPlugin The plugin having the sort icons
*/
iconSortPlugin: 'cms',
/**
* @private
* @property {String} iconSortASC The page in the resources dir of the #iconSortPlugin plugin for the ASC sort image
*/
iconSortASC: 'img/widgets/ascending.png',
/**
* @private
* @property {String} iconSortDESC The page in the resources dir of the #iconSortPlugin plugin for the DESC sort image
*/
iconSortDESC: 'img/widgets/descending.png',
initComponent: function()
{
this.cls = 'x-form-selectmetadatasort-widget';
// true by default
this.displaySortIcon = this.displaySortIcon != false && this.displaySortIcon != 'false';
this.iconSorts = {
'ASC': Ametys.getPluginResourcesPrefix(this.iconSortPlugin) + '/' + this.iconSortASC,
'DESC': Ametys.getPluginResourcesPrefix(this.iconSortPlugin) + '/' + this.iconSortDESC
};
this._sorts = {};
// workaround to handle multiple site on the widget side (but not on the storage side because the stored value is a JSON string)
// true by default
this.multiple = this.multipleSort != false && this.multipleSort != 'false';
this._contentTypesFieldName = this.contentTypesField || null;
this._metadataSetFieldName = this.metadataSetField || null;
this.form.onRelativeFieldsChange([this._contentTypesFieldName, this._metadataSetFieldName], this, this._relativeFieldChange);
this.callParent(arguments);
},
initEvents: function()
{
this.callParent(arguments);
// Click listener on the combobox to deals with clickable entries.
this.combobox.mon(this.combobox.itemList, 'click', this._onComboboxItemListClick, this);
},
/**
* @private
* Event when the combobox itemlist is clicked to open the content if required
* @param {Event} evt The click event
* @param {Ext.Element} el The item list
* @param {Object} o Options. Emtpy.
*/
_onComboboxItemListClick: function(evt, el, o)
{
// Handle clickable entry, in order to open the corresponding content.
var itemEl = evt.getTarget('.x-tagfield-item'),
clickValid = itemEl ? evt.getTarget('.clickable') : false,
record, name, sort;
if (clickValid)
{
record = this.combobox.getRecordByListItemNode(itemEl);
if (record)
{
name = record.get('name');
sort = this._sorts[name] || 'ASC';
sort = sort == 'ASC' ? 'DESC' : 'ASC'; // invert sort
this._sorts[name] = sort;
record.set('sort', sort);
}
}
},
getLabelTpl: function()
{
var labelTpl = [],
sort;
if (this.displaySortIcon == true)
{
labelTpl.push('<tpl if="values.sort == \'ASC\'"><img class="clickable" width="16" height="16" src="' + this.iconSorts.ASC + '"/></tpl>');
labelTpl.push('<tpl if="values.sort == \'DESC\'"><img class="clickable" width="16" height="16" src="' + this.iconSorts.DESC + '{smallIcon}"/></tpl>');
labelTpl.push('<span class="clickable">{[values.text]}</span>');
}
else
{
labelTpl.push('{[values.text]}');
}
return labelTpl;
},
getTipTpl: function()
{
var tipTpl = ['<div class="metadata-sort-tooltip">'];
tipTpl.push('<b>{[values.text]} ({[values.name]})</b><br/>');
tipTpl.push("<u>{{i18n PLUGINS_CMS_WIDGET_SELECTMETADATATOSORTBY_TOOLTIP_FULLLABEL}}</u> : {[values.fullLabel]}<br/>");
tipTpl.push('</div>');
return tipTpl;
},
getComboBoxConfig: function()
{
var cfg = this.callParent(arguments);
cfg.queryMode = 'local';
return cfg;
},
getStore: function()
{
var me = this;
return Ext.create('Ext.data.Store', {
proxy: {
type: 'ametys',
plugin: 'cms',
url: 'common-attributes.json',
reader: {
type: 'json',
rootProperty: 'attributes'
},
extraParams: {
onlySortable: true,
withLastValidation: true,
withCreationDate: true,
withFullLabel: true
}
},
fields: [
{name: 'id', mapping: 'name'},
{name: 'name', mapping: 'name'},
{name: 'text', mapping: 'label', type: 'string'},
{name: 'fullLabel', mapping: 'fullLabel', type: 'string'},
{name: 'type', mapping: 'type'},
{name: 'sort', calculate: function(data) {
var name = data.name;
if (!(name in me._sorts))
{
me._sorts[name] = 'ASC';
}
return me._sorts[name];
}}
],
sortOnLoad: true,
sorters: [{property: 'fullLabel', direction:'ASC'}],
listeners: {
beforeload: {fn: this._onStoreBeforeLoad, scope: this},
load: {fn: this._onStoreLoad, scope: this}
}
});
},
/**
* Set the value after the store load
* @param {Ext.data.Store} store The store.
* @param {Ext.data.Model[]} records The loaded records.
* @private
*/
_onStoreLoad: function(store, records)
{
var value = Ext.Array.from(this.getValuesAsObject()),
store = this.combobox.getStore();
// filter out record that are not in the store anymore
value = Ext.Array.filter(value, function(entry) {
return store.find('name', entry.name) != -1;
});
this.setValue(value);
},
/**
* Set the request parameters before loading the store.
* @param {Ext.data.Store} store The store.
* @param {Ext.data.operation.Operation} operation The Ext.data.Operation object that will be passed to the Proxy to load the Store.
* @private
*/
_onStoreBeforeLoad: function(store, operation)
{
var relativeFields = this.form.getRelativeFields([this._contentTypesFieldName, this._metadataSetFieldName], this),
contentTypesField = relativeFields[0],
metadataSetField = relativeFields[1];
var params = operation.getParams() || {};
operation.setParams(Ext.apply(params, {
ids: contentTypesField ? contentTypesField.getValue() : null,
viewName: metadataSetField ? metadataSetField.getValue() : this.metadataSetName
}));
},
/**
* Listener called when the value of a relative field changes
*/
_relativeFieldChange: function()
{
this.combobox.getStore().load();
},
setValue: function (value)
{
// retrieves the value object
value = value || null;
if (value)
{
if (Ext.isString(value))
{
value = Ext.JSON.decode(value);
}
if (Ext.isObject(value))
{
value = Ext.Array.from(value);
}
// value should an array at this point
if (!Ext.isArray(value))
{
value = null;
}
}
if (value)
{
var sort;
var names = Ext.Array.map(value, function(entry) {
sort = entry.sort;
if (Ext.String.endsWith(sort, '_LC'))
{
sort = sort.slice(0, -3); // remove the last 3 characters, ie. the '_LC' part.
}
this._sorts[entry.name] = sort;
return entry.name;
}, this);
names = this.multiple ? names : names[0];
this.combobox.setValue(names);
}
else
{
this.combobox.setValue(null);
}
},
getValue: function()
{
return Ext.encode(this.getValuesAsObject());
},
/**
* Returns the current value of the widget as an object.
* @return {Object/Object[]} Each object represents an entry. Object keys are: 'name', the metadata name and 'sort', the sort type ('ASC' or 'DESC').
*/
getValuesAsObject: function()
{
var value = Ext.Array.from(this.combobox.getValue()),
store = this.combobox.getStore(),
sort, record;
var valuesAsObject = Ext.Array.map(value, function(name) {
sort = this._sorts[name];
record = store.findRecord('name', name);
if (record && record.get('type') == 'STRING')
{
// lowercase for string sorts
sort += '_LC';
}
return {
name: name,
sort: sort
};
}, this);
return this.multiple ? valuesAsObject : valuesAsObject[0];
}
});