/*
* Copyright 2016 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 for the mapping of a collection of a synchronizable contents.
*/
Ext.define('Ametys.plugins.contentio.widget.AbstractMapping', {
extend: 'Ametys.form.AbstractField',
/**
* @cfg {String} [contentTypesField] The relative path of the content type field
*/
/**
* @private
* @property {String} _contentTypesFieldName The property related to {@link #cfg-contentTypesField}
*/
/**
* @private
* @property {String} _contentTypeId The id of content type
*/
/**
* @private
* @property {Ext.grid.Panel} _grid The wrapped grid in this field
*/
/**
* @private
* @property {String} _metadataSetName The metadataSetName for the mapping
*/
/**
* @private
* @property {String[]} _excludedRecords The ids of the records to exclude (i.e. to not set) when setting the data into the store (in {@link #_fillGridWithMetadata})
*/
initComponent: function()
{
this._contentTypesFieldName = this.contentTypesField || null;
this._metadataSetName = this.metadataSetName || "main";
this._excludedRecords = [];
if (this.excludedMetadata)
{
this._excludedRecords = this.excludedMetadata.split(",");
}
this.form.onRelativeFieldsChange([this._contentTypesFieldName], this, this._onContentTypeChanged);
this.callParent(arguments);
this._fillGridWithMetadata = Ext.Function.createInterceptor(this._fillGridWithMetadata, this._isNotDestroyed, this);
},
/**
* @private
* Listener called when the value of the content type field changes
* @param {Ext.form.field.Field} field The relative field that has triggered the on change event.
* @param {Object} newValue The new value
* @param {Object} oldValue The old value
*/
_onContentTypeChanged: function(field, newValue, oldValue)
{
if (this._contentTypeId != newValue)
{
this._contentTypeId = newValue;
this._updateContentTypeModel();
}
},
/**
* Returns true if the grid is initialized and can received values
* @return true if initialized
*/
_isInitialized : function ()
{
return this._contentTypeId != null;
},
/**
* Initialize or reinitialize the grid with the model of selected content type
* @param {Function} [callback] the callback function
* @private
*/
_initializeGrid: function (callback)
{
this._updateContentTypeModel(callback);
},
/**
* Update the grid store with the model of selected content type
* @param {Function} [callback] the callback function
* @private
*/
_updateContentTypeModel: function (callback)
{
this._contentTypeId = this._contentTypeId == null ? this.form.getRelativeField(this._contentTypesFieldName, this).getValue() : this._contentTypeId;
if (this._contentTypeId != null)
{
Ametys.data.ServerComm.send({
plugin: 'cms',
url: 'common-attributes.json',
parameters: {
withFullLabel: true,
ids: [this._contentTypeId],
viewName: this._metadataSetName,
includeSubRepeaters: false
},
priority: Ametys.data.ServerComm.PRIORITY_MAJOR,
callback: {
handler: this._fillGridWithMetadata,
scope: this,
arguments: [callback]
},
waitMessage: false,
errorMessage: true,
responseType: 'text'
});
}
else if (Ext.isFunction(callback))
{
callback();
}
},
/**
* @private
* Is not destroyed?
* @return {Boolean} true if the abstract mapping is destroyed, false otherwise
*/
_isNotDestroyed: function()
{
return !this.destroyed;
},
/**
* @private
* Fills grid with metadata of the selected content type
* @param {Object} response The server response
* @param {Object[]} args The transmitted arguments
* @param {Function} args.0 A callback function
*/
_fillGridWithMetadata: function(response, args)
{
this._metadata = {};
var metadata = Ext.decode(response.firstChild.textContent).attributes;
var me = this,
data = [];
Ext.Array.each(metadata, function(item) {
var name = item.name;
me._metadata[name] = item.fullLabel;
if (!Ext.Array.contains(me._excludedRecords, name))
{
data.push({
"metadata-ref": name
});
}
});
// this._excludedRecords = [];
this._grid.getStore().suspendEvent("update", "datachanged");
this._grid.getStore().setData(data);
this._grid.getStore().resumeEvent("update", "datachanged");
if (Ext.isFunction(args[0]))
{
args[0]();
}
},
/**
* @private
* Renderer for metadata
* @param {String} value the metadata path
* @return {String} the rendered metadata
*/
_renderMetadata: function(value)
{
return this._getMetadataLabel(value) || value;
},
/**
* @protected
* Gets the label of the metadata
* @param {String} metadataPath the metadata path
* @return {String} the metadata label, or null if not found
*/
_getMetadataLabel: function(metadataPath)
{
return this._metadata && this._metadata[metadataPath];
},
/**
* @protected
* Create the grid of mapping
*/
_createGrid: function()
{
this._grid = Ext.create('Ext.grid.Panel', {
store: {
model: this._getModel(),
sortOnLoad: true,
listeners: {
'update': Ext.bind(this._onGridStoreChange, this),
'datachanged': Ext.bind(this._onGridStoreChange, this)
}
},
columns: this._getColumnsCfg(),
viewConfig: {
markDirty: false
},
disableSelection: true
});
// FIXME Workaround for centering the checkboxes in the cells : https://www.sencha.com/forum/showthread.php?307438
var gridId = this._grid.getId();
var stylesheetId = gridId + '$custom-stylesheet';
Ext.util.CSS.createStyleSheet('#' + gridId + ' .x-grid-cell .x-form-cb {left: inherit !important;}', stylesheetId);
this._grid.on({
'destroy': function(grid)
{
Ext.util.CSS.removeStyleSheet(stylesheetId);
}
});
},
/**
* @protected
* @template
* Gets the model for the store of the grid
* @return {String} The name of the model for the grid store
*/
_getModel: function()
{
throw new Error("This method is not implemented in " + this.self.getName());
},
/**
* @protected
* @template
* Gets the columns of the grid
* @return {Object[]} The the columns of the grid
*/
_getColumnsCfg: function()
{
throw new Error("This method is not implemented in " + this.self.getName());
},
/**
* @private
* Unckeck all the other checkboxes of the column. Must be a column of boolean type
* @param {String} fieldName The name of the field to update in the record
* @param {Ext.form.field.Field} widget The widget
* @param {Object} newValue The new value
*/
_uncheckOthers: function(fieldName, widget, newValue)
{
if (newValue && widget.getWidgetRecord)
{
var changingRecord = widget.getWidgetRecord();
this._grid.getStore().getData().each(function(currentRecord) {
if (currentRecord != changingRecord && currentRecord.get(fieldName))
{
currentRecord.set(fieldName, false);
}
});
}
},
/**
* @private
* Listener when a widget value has changed, in order to update the grid store.
* @param {String} fieldName The name of the field to update in the record
* @param {Ext.form.field.Field} widget The widget
* @param {Object} newValue The new value
*/
_onWidgetChange: function(fieldName, widget, newValue)
{
if (widget.getWidgetRecord)
{
var rec = widget.getWidgetRecord();
rec.set(fieldName, newValue);
}
},
getValue: function()
{
var me = this,
value = [];
this._grid.getStore().getData().each(function(record) {
var v = me.getValueFromRecord(record);
if (v != null)
{
value.push(v);
}
}, this);
return Ext.encode(value);
},
/**
* Get the values associated to a record
* @param {Ext.data.Model} record The record to analyser
* @return {Object} An object of the data of the record
*/
getValueFromRecord: function (record)
{
return Ext.clone(record.getData());
},
setValue: function(value)
{
if (Ext.isString(value))
{
value = Ext.decode(value);
}
var me = this,
grid = this._grid,
store = this._grid.getStore();
function _internalSetValue()
{
store.suspendEvent("update");
store.suspendEvent("datachanged");
Ext.Array.each (value, function(v) {
var index = store.findExact('metadata-ref', v['metadata-ref']);
if (index != -1)
{
me.updateRecord(store.getAt(index), v);
}
})
store.resumeEvent("datachanged");
store.resumeEvent("update");
grid.getView().refresh();
}
if (!this._isInitialized())
{
this._initializeGrid(_internalSetValue);
}
else
{
_internalSetValue();
}
this.callParent(arguments);
},
/**
* @protected
* @template
* Update a record from the received value
* @param {Ext.data.Model} record The record
* @param {Object} value the value for this record
*/
updateRecord: function (record, value)
{
// Nothing by default
},
/**
* @private
* Listener when records are added or removed in the store, or when a record has been updated
* @param {Ext.data.Store} store the store
*/
_onGridStoreChange: function(store)
{
this.checkChange();
}
});