/*
* 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 filter based on available metadata.
*/
Ext.define('Ametys.cms.form.widget.SelectMetadataToFilterBy', {
extend: 'Ametys.form.AbstractFieldsWrapper',
/**
* @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}
*/
/**
* @private
* @property {Object} _filters The available filters.
*/
_filters: {
NONE: '',
EQUAL: 'ft-eq',
DIFFERENT: 'ft-neq',
STARTSWITH: 'ft-sw',
ENDSWITH: 'ft-ew',
CONTAINS: 'ft-co',
NOTCONTAINS: 'ft-nco',
EMPTY: 'ft-em',
NONEMPTY: 'ft-nem',
LESSTHAN: 'ft-lt',
GREATERTHAN: 'ft-gt',
PAST: 'ft-past',
FUTURE: 'ft-future',
TRUE: 'ft-true',
FALSE: 'ft-false'
},
/**
* @private
* @property {Ext.data.Store} _filterStore The local store holding current filter for each metadata
*/
initComponent: function()
{
this.cls = 'x-form-selectmetadatafilter-widget';
this._contentTypesFieldName = this.contentTypesField || null;
this._metadataSetFieldName = this.metadataSetField || null;
this._filterStore = Ext.create('Ext.data.ArrayStore', this.getFilterStoreCfg());
this.items = [{
xtype: 'container',
itemId: 'container',
width: '100%',
maxHeight: 200,
scrollable: true,
bodyPadding: '7 7 0 7',
defaults : {
cls: 'ametys',
labelAlign: 'left',
labelPad: 5,
labelSeparator: '',
msgTarget: 'side',
anchor: '100%'
}
}];
this.callParent();
this.form.onRelativeFieldsChange([this._contentTypesFieldName, this._metadataSetFieldName], this, this._relativeFieldChange);
},
/**
* Get the store configuration
* @param {Object} config The current configuration object
* @return {Object} The store configuration
*/
getFilterStoreCfg: function(config)
{
return {
type: 'store', // Ext.data.Store
proxy: {
type: 'ametys',
plugin: 'cms',
url: 'common-attributes.json',
reader: {
type: 'json',
rootProperty: 'attributes'
},
extraParams: {
acceptedTypes: ['DATE', 'DATETIME'], // FIXME currently only available for date and datetime metadata
withLastValidation: true,
withFullLabel: true
}
},
fields: [
{name: 'id', mapping: 'name'},
{name: 'name', mapping: 'name'},
{name: 'label', mapping: 'label',},
{name: 'fullLabel', mapping: 'fullLabel', type: 'string'},
{name: 'type', mapping: 'type'}
],
sortOnLoad: true,
sorters: [{property: 'label', direction:'ASC'}],
listeners: {
beforeload: {fn: this._onFilterStoreBeforeLoad, scope: this},
load: {fn: this._onFilterStoreLoad, scope: this}
}
};
},
/**
* Listener called when the value of a relative field changes
*/
_relativeFieldChange: function()
{
this._filterStore.load();
},
/**
* 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
*/
_onFilterStoreBeforeLoad: 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
}));
},
/**
* Set the value after the store load
* @param {Ext.data.Store} store The store.
* @param {Ext.data.Model[]} records The loaded records.
* @private
*/
_onFilterStoreLoad: function(store, records)
{
var form = this.getComponent('container'),
fieldsToKeep = [], recordsToInsert = [];
var addedOrRemoved = false,
field, name;
// Supsend layouts between field addition/removal
Ext.suspendLayouts();
this._filterStore.each(function(record) {
name = record.get('name');
field = form.getComponent(name);
if (!field)
{
recordsToInsert.push(record);
addedOrRemoved = true;
}
fieldsToKeep.push(name);
}, this);
var fieldsToRemove = form.items.filterBy(function(field) {
return !Ext.Array.contains(fieldsToKeep, field.getItemId());
});
addedOrRemoved = addedOrRemoved || fieldsToRemove.getCount() > 0;
fieldsToRemove.each(function(field) {
form.remove(field, true);
});
// Iterates in sort order to keep field sorted.
var index;
Ext.Array.forEach(recordsToInsert, function(record) {
index = this._filterStore.indexOf(record);
form.insert(index, this._getFilterComboboxCfg(record));
}, this);
// Resume layouts
Ext.resumeLayouts(addedOrRemoved);
if (this._valuesToSet)
{
var valuesSet = [];
Ext.Object.each(this._valuesToSet, function(key, value) {
field = form.getComponent(key);
if (field)
{
field.setValue(value);
valuesSet.push(key);
}
});
Ext.Array.forEach(valuesSet, function(name) {
delete this._valuesToSet[name]; // remove from the values to set
}, this);
}
},
/**
* Get the configuration of a filter combobox
* @param {Ext.data.Model} metadata The metadata corresponding to the filter to create
*/
_getFilterComboboxCfg: function(metadata)
{
return {
xtype: 'combobox',
name: metadata.get('name'),
itemId: metadata.get('name'),
fieldLabel: metadata.get('label'),
editable: false,
forceSelection: true,
valueField: 'name',
displayField: 'label',
queryMode: 'local',
value: '',
store: {
type: 'array', // Ext.data.ArrayStore
fields: ['name', 'label'],
data: this._getLocalFilterData(metadata.get('type'))
}
};
},
/**
* Retrieves filter data for a given metadata type
* @param {String} type The metadata type
*/
_getLocalFilterData: function(type)
{
switch (type.toLowerCase())
{
case 'string':
return this._createComboboxFilterData(
['NONE', 'EQUAL', 'DIFFERENT', 'STARTSWITH', 'ENDSWITH', 'CONTAINS', 'NOTCONTAINS']
);
case 'double':
case 'long':
return this._createComboboxFilterData(
['NONE', 'LESSTHAN', 'GREATERTHAN']
);
case 'date':
case 'datetime':
return this._createComboboxFilterData(
['NONE', 'PAST', 'FUTURE']
);
case 'boolean':
return this._createComboboxFilterData(
['NONE', 'TRUE', 'FALSE']
);
default:
// ie.
// case 'rich-text':
// case 'binary':
// case 'password':
return this._createComboboxFilterData(
['NONE', 'EMPTY', 'NONEMPTY']
);
}
},
/**
* Create the desired filter data given an array of filter names
* @param {String[]} filterNames ordered array of filter name
* @return {String[][]} A bidimensionnal array that can be used as data for a local combobox store
*/
_createComboboxFilterData: function(filterNames)
{
return Ext.Array.map(filterNames, function(name) {
return [this._filters[name], this._getFilterLabel(name)];
}, this);
},
/**
* @private
* Get a label associated to a filter value
* @param {String} filterName A filter name such as 'NONE', 'EQUAL'...
* @return {String} The label of an existing filter name or undefined
*/
_getFilterLabel: function(filterName)
{
switch (filterName)
{
case 'NONE':
return "{{i18n PLUGINS_CMS_WIDGET_SELECTMETADATATOFILTERBY_FILTER_NONE}}";
case 'EQUAL':
return "{{i18n PLUGINS_CMS_WIDGET_SELECTMETADATATOFILTERBY_FILTER_EQUAL}}";
case 'DIFFERENT':
return "{{i18n PLUGINS_CMS_WIDGET_SELECTMETADATATOFILTERBY_FILTER_DIFFERENT}}";
case 'STARTSWITH':
return "{{i18n PLUGINS_CMS_WIDGET_SELECTMETADATATOFILTERBY_FILTER_STARTSWITH}}";
case 'ENDSWITH':
return "{{i18n PLUGINS_CMS_WIDGET_SELECTMETADATATOFILTERBY_FILTER_ENDSWITH}}";
case 'CONTAINS':
return "{{i18n PLUGINS_CMS_WIDGET_SELECTMETADATATOFILTERBY_FILTER_CONTAINS}}";
case 'NOTCONTAINS':
return "{{i18n PLUGINS_CMS_WIDGET_SELECTMETADATATOFILTERBY_FILTER_NOTCONTAINS}}";
case 'EMPTY':
return "{{i18n PLUGINS_CMS_WIDGET_SELECTMETADATATOFILTERBY_FILTER_EMPTY}}";
case 'NONEMPTY':
return "{{i18n PLUGINS_CMS_WIDGET_SELECTMETADATATOFILTERBY_FILTER_NONEMPTY}}";
case 'LESSTHAN':
return "{{i18n PLUGINS_CMS_WIDGET_SELECTMETADATATOFILTERBY_FILTER_LESSTHAN}}";
case 'GREATERTHAN':
return "{{i18n PLUGINS_CMS_WIDGET_SELECTMETADATATOFILTERBY_FILTER_GREATERTHAN}}";
case 'PAST':
return "{{i18n PLUGINS_CMS_WIDGET_SELECTMETADATATOFILTERBY_FILTER_PAST}}";
case 'FUTURE':
return "{{i18n PLUGINS_CMS_WIDGET_SELECTMETADATATOFILTERBY_FILTER_FUTURE}}";
case 'TRUE':
return "{{i18n PLUGINS_CMS_WIDGET_SELECTMETADATATOFILTERBY_FILTER_TRUE}}";
case 'FALSE':
return "{{i18n PLUGINS_CMS_WIDGET_SELECTMETADATATOFILTERBY_FILTER_FALSE}}";
default:
return undefined;
}
},
getValue: function()
{
return Ext.JSON.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 'filter', the filter name
*/
getValuesAsObject: function ()
{
var form = this.getComponent('container'),
values = {}, value, record;
form.items.each(function(field) {
value = field.getValue();
if (value)
{
record = this._filterStore.findRecord('name', field.getItemId());
if (record && record.get('type') == 'STRING')
{
// lowercase for string filters
value += '_LC';
}
values[field.getName()] = value;
}
}, this);
return values;
},
setValue: function (value)
{
// retrieves the value object
value = value || null;
if (value)
{
if (Ext.isString(value))
{
value = Ext.JSON.decode(value);
}
// value should be an object at this point
if (!Ext.isObject(value))
{
value = null;
}
}
if (value)
{
var form = this.getComponent('container'),
field;
Ext.Object.each(value, function(key, value) {
field = form.getComponent(key);
if (Ext.String.endsWith(value, '_LC'))
{
value = value.slice(0, -3); // remove the last 3 characters, ie. the '_LC' part.
}
if (field)
{
field.setValue(value);
}
else
{
// Field might not currently exists. The value will be set
// once the corresponding field is created
this._valuesToSet = this._valuesToSet || {};
this._valuesToSet[key] = value;
}
}, this);
}
},
getSubmitData: function() {
var data = null;
if (!this.disabled && this.submitValue)
{
data = {};
data[this.getName()] = this.getValue();
}
return data;
},
getErrors: function (value)
{
var form = this.getComponent('container'),
errors = this.callParent(arguments);
form.items.each(function(field) {
errors = errors.concat(field.getErrors());
});
return errors;
}
});