2011-10-07 01:44:55 +02:00
|
|
|
/**
|
|
|
|
* eGroupWare eTemplate2 - JS Custom fields object
|
|
|
|
*
|
|
|
|
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
|
|
|
|
* @package etemplate
|
|
|
|
* @subpackage api
|
|
|
|
* @link http://www.egroupware.org
|
|
|
|
* @author Nathan Gray
|
|
|
|
* @copyright Nathan Gray 2011
|
|
|
|
* @version $Id$
|
|
|
|
*/
|
|
|
|
|
|
|
|
"use strict";
|
|
|
|
|
|
|
|
/*egw:uses
|
|
|
|
lib/tooltip;
|
|
|
|
jquery.jquery;
|
|
|
|
et2_core_xml;
|
|
|
|
et2_core_DOMWidget;
|
|
|
|
et2_core_inputWidget;
|
|
|
|
*/
|
|
|
|
|
2012-06-12 22:58:25 +02:00
|
|
|
var et2_customfields_list = et2_valueWidget.extend([et2_IDetachedDOM, et2_IInput], {
|
2011-10-07 01:44:55 +02:00
|
|
|
|
|
|
|
attributes: {
|
|
|
|
'customfields': {
|
|
|
|
'name': 'Custom fields',
|
|
|
|
'description': 'Auto filled'
|
|
|
|
},
|
|
|
|
'fields': {
|
|
|
|
'name': 'Custom fields',
|
2011-10-17 18:34:58 +02:00
|
|
|
'description': 'Auto filled',
|
|
|
|
"default": {}
|
2011-10-07 01:44:55 +02:00
|
|
|
},
|
|
|
|
'value': {
|
|
|
|
'name': 'Custom fields',
|
|
|
|
'description': 'Auto filled'
|
|
|
|
},
|
2012-03-22 00:23:07 +01:00
|
|
|
'type_filter': {
|
|
|
|
'name': 'Field filter',
|
|
|
|
"default": "",
|
|
|
|
"type": "string",
|
|
|
|
"description": "Filter displayed custom fields by their 'type2' attribute"
|
|
|
|
}
|
2011-10-07 01:44:55 +02:00
|
|
|
},
|
|
|
|
|
2012-03-22 00:23:07 +01:00
|
|
|
legacyOptions: ["type_filter"],
|
|
|
|
|
2011-10-07 01:44:55 +02:00
|
|
|
prefix: '#',
|
|
|
|
|
|
|
|
init: function() {
|
2011-10-11 20:18:58 +02:00
|
|
|
// Some apps (infolog edit) don't give ID, so assign one to get settings
|
|
|
|
if(!arguments[1].id) arguments[1].id = "custom_fields";
|
|
|
|
|
2011-10-07 01:44:55 +02:00
|
|
|
this._super.apply(this, arguments);
|
|
|
|
|
|
|
|
// Create the table body and the table
|
|
|
|
this.tbody = $j(document.createElement("tbody"));
|
|
|
|
this.table = $j(document.createElement("table"))
|
|
|
|
.addClass("et2_grid");
|
|
|
|
this.table.append(this.tbody);
|
|
|
|
|
|
|
|
this.rows = {};
|
|
|
|
this.widgets = {};
|
|
|
|
this.detachedNodes = [];
|
|
|
|
|
2012-03-22 00:23:07 +01:00
|
|
|
if(this.options.type_filter && typeof this.options.type_filter == "string")
|
|
|
|
{
|
|
|
|
this.options.type_filter = this.options.type_filter.split(",");
|
|
|
|
}
|
|
|
|
if(this.options.type_filter)
|
|
|
|
{
|
|
|
|
this.options.fields = {};
|
|
|
|
for(var field_name in this.options.customfields)
|
|
|
|
{
|
|
|
|
if(!this.options.customfields[field_name].type2)
|
|
|
|
{
|
|
|
|
// No restrictions
|
|
|
|
this.options.fields[field_name] = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
var types = this.options.customfields[field_name].type2.split(",");
|
|
|
|
this.options.fields[field_name] = false;
|
2012-07-11 22:30:33 +02:00
|
|
|
for(var i = 0; i < types.length; i++)
|
2012-03-22 00:23:07 +01:00
|
|
|
{
|
2012-07-11 22:30:33 +02:00
|
|
|
if(jQuery.inArray(types[i],this.options.type_filter) > -1)
|
2012-03-22 00:23:07 +01:00
|
|
|
{
|
|
|
|
this.options.fields[field_name] = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-13 19:01:55 +01:00
|
|
|
this.setDOMNode(this.table[0]);
|
2011-10-07 01:44:55 +02:00
|
|
|
},
|
|
|
|
|
|
|
|
destroy: function() {
|
|
|
|
this._super.apply(this, arguments);
|
2011-10-11 20:18:58 +02:00
|
|
|
this.tbody = null;
|
|
|
|
this.table = null;
|
2011-10-07 01:44:55 +02:00
|
|
|
this.rows = null;
|
|
|
|
this.widgets = null;
|
|
|
|
},
|
|
|
|
|
2012-03-13 19:01:55 +01:00
|
|
|
/**
|
|
|
|
* What does this do? I don't know, but when everything is done the second
|
|
|
|
* time, this makes it work. Otherwise, all custom fields are lost.
|
|
|
|
*/
|
|
|
|
assign: function(_obj) {
|
|
|
|
this.loadFields();
|
|
|
|
},
|
|
|
|
|
2011-10-07 01:44:55 +02:00
|
|
|
getDOMNode: function(_sender) {
|
|
|
|
|
|
|
|
// Check whether the _sender object exists inside the management array
|
|
|
|
if(this.rows && _sender.id && this.rows[_sender.id])
|
|
|
|
{
|
|
|
|
return this.rows[_sender.id];
|
|
|
|
}
|
|
|
|
|
2012-03-13 19:01:55 +01:00
|
|
|
return this._super.apply(this, arguments);
|
2011-10-07 01:44:55 +02:00
|
|
|
},
|
|
|
|
|
2011-10-11 20:18:58 +02:00
|
|
|
/**
|
|
|
|
* Initialize widgets for custom fields
|
|
|
|
*/
|
2011-10-07 01:44:55 +02:00
|
|
|
loadFields: function() {
|
|
|
|
if(!this.options || !this.options.customfields) return;
|
2011-10-11 20:18:58 +02:00
|
|
|
|
|
|
|
// Already set up - avoid duplicates in nextmatch
|
|
|
|
if(this._type == 'customfields-list' && !this.isInTree()) return;
|
2011-10-18 18:32:35 +02:00
|
|
|
if(!jQuery.isEmptyObject(this.widgets)) return;
|
2011-10-07 01:44:55 +02:00
|
|
|
|
2011-10-17 18:34:58 +02:00
|
|
|
// Check for global setting changes (visibility)
|
|
|
|
var global_data = this.getArrayMgr("modifications").getRoot().getEntry('~custom_fields~');
|
2012-03-22 00:23:07 +01:00
|
|
|
if(global_data.fields && !this.options.fields) this.options.fields = global_data.fields;
|
2011-10-17 18:34:58 +02:00
|
|
|
|
2011-10-18 22:09:48 +02:00
|
|
|
// For checking app entries
|
2012-03-02 11:44:56 +01:00
|
|
|
var apps = this.egw().link_app_list();
|
2011-10-18 22:09:48 +02:00
|
|
|
|
2011-10-07 01:44:55 +02:00
|
|
|
// Create the table rows
|
|
|
|
for(var field_name in this.options.customfields)
|
|
|
|
{
|
2012-03-22 00:23:07 +01:00
|
|
|
// Skip fields if we're filtering
|
|
|
|
if(!jQuery.isEmptyObject(this.options.fields) && !this.options.fields[field_name]) continue;
|
|
|
|
|
2011-10-17 18:34:58 +02:00
|
|
|
var field = this.options.customfields[field_name];
|
2012-03-23 00:35:25 +01:00
|
|
|
|
2012-06-12 22:58:25 +02:00
|
|
|
var id = this.prefix+field_name;
|
|
|
|
|
2012-03-23 00:35:25 +01:00
|
|
|
// Need curlies around ID for nm row expansion
|
|
|
|
if(this.id == '$row')
|
|
|
|
{
|
|
|
|
id = "{" + this.id + "}" + "["+this.prefix + field_name+"]";
|
|
|
|
}
|
2011-10-17 18:34:58 +02:00
|
|
|
|
2011-10-11 20:18:58 +02:00
|
|
|
// Avoid creating field twice
|
2011-10-18 18:32:35 +02:00
|
|
|
if(!this.rows[id])
|
|
|
|
{
|
|
|
|
|
|
|
|
var row = jQuery(document.createElement("tr"))
|
|
|
|
.appendTo(this.tbody);
|
|
|
|
var cf = jQuery(document.createElement("td"))
|
2011-10-07 01:44:55 +02:00
|
|
|
.appendTo(row);
|
2011-10-18 22:09:48 +02:00
|
|
|
var setup_function = '_setup_'+(apps[field.type] ? 'link_entry' : field.type.replace("-","_"));
|
|
|
|
|
2011-10-18 18:32:35 +02:00
|
|
|
var attrs = {
|
|
|
|
'id': id,
|
|
|
|
'statustext': field.help,
|
|
|
|
'required': field.needed,
|
2012-03-22 00:23:07 +01:00
|
|
|
'readonly': this.options.readonly,
|
|
|
|
'value': this.options.value[this.prefix+field_name]
|
2011-10-18 18:32:35 +02:00
|
|
|
};
|
|
|
|
if(this[setup_function]) {
|
2012-03-22 00:23:07 +01:00
|
|
|
var no_skip = this[setup_function].call(this, field_name, field, attrs);
|
|
|
|
if(!no_skip) continue;
|
2011-10-18 18:32:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if(this._type == 'customfields-list') {
|
|
|
|
// No label, cust widget
|
|
|
|
attrs.readonly = true;
|
|
|
|
this.detachedNodes.push(cf[0]);
|
|
|
|
} else {
|
|
|
|
// Label in first column, widget in 2nd
|
|
|
|
cf.text(field.label + "");
|
|
|
|
cf = jQuery(document.createElement("td"))
|
|
|
|
.appendTo(row);
|
|
|
|
}
|
|
|
|
this.rows[id] = cf[0];
|
|
|
|
|
|
|
|
// Create widget
|
2011-10-18 22:09:48 +02:00
|
|
|
var widget = this.widgets[field_name] = et2_createWidget(attrs.type ? attrs.type : field.type, attrs, this);
|
2011-10-07 01:44:55 +02:00
|
|
|
}
|
2011-10-12 22:56:11 +02:00
|
|
|
|
2011-10-18 18:32:35 +02:00
|
|
|
// Field is not to be shown
|
|
|
|
if(!this.options.fields || jQuery.isEmptyObject(this.options.fields) || this.options.fields[field_name] == true)
|
|
|
|
{
|
|
|
|
jQuery(this.rows[field_name]).show();
|
|
|
|
} else {
|
|
|
|
jQuery(this.rows[field_name]).hide();
|
|
|
|
}
|
2011-10-11 20:18:58 +02:00
|
|
|
|
2011-10-07 01:44:55 +02:00
|
|
|
}
|
|
|
|
},
|
2011-10-11 20:18:58 +02:00
|
|
|
|
2012-03-13 19:01:55 +01:00
|
|
|
/**
|
|
|
|
* Read needed info on available custom fields from various places it's stored.
|
|
|
|
*/
|
2011-10-07 01:44:55 +02:00
|
|
|
transformAttributes: function(_attrs) {
|
|
|
|
this._super.apply(this, arguments);
|
|
|
|
|
2011-10-11 20:18:58 +02:00
|
|
|
// Add in settings that are objects
|
|
|
|
if(!_attrs.customfields)
|
|
|
|
{
|
2011-10-14 19:57:16 +02:00
|
|
|
// Customized settings for this widget (unlikely)
|
2011-10-11 20:18:58 +02:00
|
|
|
var data = this.getArrayMgr("modifications").getEntry(this.id);
|
2011-10-14 19:57:16 +02:00
|
|
|
// Check for global settings
|
2011-10-17 18:34:58 +02:00
|
|
|
var global_data = this.getArrayMgr("modifications").getRoot().getEntry('~custom_fields~', true);
|
2012-06-06 06:13:19 +02:00
|
|
|
if(global_data)
|
|
|
|
{
|
|
|
|
for(var key in data)
|
|
|
|
{
|
|
|
|
if(global_data[key])
|
|
|
|
{
|
|
|
|
data[key] = jQuery.extend(true, {}, data[key], global_data[key]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-10-11 20:18:58 +02:00
|
|
|
for(var key in data)
|
|
|
|
{
|
2012-03-06 14:22:01 +01:00
|
|
|
if(typeof data[key] === 'object' && ! _attrs[key]) _attrs[key] = data[key];
|
2011-10-11 20:18:58 +02:00
|
|
|
}
|
2012-06-12 22:58:25 +02:00
|
|
|
for(var key in global_data)
|
|
|
|
{
|
|
|
|
if(typeof global_data[key] === 'object' && ! _attrs[key]) _attrs[key] = global_data[key];
|
|
|
|
}
|
2011-10-11 20:18:58 +02:00
|
|
|
}
|
|
|
|
|
2011-10-07 01:44:55 +02:00
|
|
|
if (this.id)
|
|
|
|
{
|
|
|
|
// Set the value for this element
|
|
|
|
var contentMgr = this.getArrayMgr("content");
|
|
|
|
if (contentMgr != null) {
|
|
|
|
var val = contentMgr.getEntry(this.id);
|
2011-10-11 20:18:58 +02:00
|
|
|
_attrs["value"] = {};
|
2011-10-07 01:44:55 +02:00
|
|
|
if (val !== null)
|
|
|
|
{
|
|
|
|
// Only set the values that match desired custom fields
|
|
|
|
for(var key in val)
|
|
|
|
{
|
|
|
|
if(key.indexOf(this.prefix) == 0) {
|
|
|
|
_attrs["value"][key] = val[key];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//_attrs["value"] = val;
|
|
|
|
}
|
2011-10-11 20:18:58 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
// Check for custom fields directly in record
|
|
|
|
for(var key in _attrs.customfields)
|
|
|
|
{
|
|
|
|
_attrs["value"][this.prefix + key] = contentMgr.getEntry(this.prefix + key);
|
|
|
|
}
|
|
|
|
}
|
2011-10-07 01:44:55 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
2012-03-13 19:01:55 +01:00
|
|
|
|
|
|
|
loadFromXML: function(_node) {
|
|
|
|
this.loadFields();
|
|
|
|
|
|
|
|
// Load the nodes as usual
|
|
|
|
this._super.apply(this, arguments);
|
|
|
|
},
|
|
|
|
|
2011-10-07 01:44:55 +02:00
|
|
|
set_value: function(_value) {
|
|
|
|
if(!this.options.customfields) return;
|
|
|
|
for(var field_name in this.options.customfields)
|
|
|
|
{
|
2012-03-15 23:57:55 +01:00
|
|
|
// Make sure widget is created, and has the needed function
|
2011-10-07 01:44:55 +02:00
|
|
|
if(!this.widgets[field_name] || !this.widgets[field_name].set_value) continue;
|
2011-10-07 18:59:50 +02:00
|
|
|
var value = _value[this.prefix + field_name] ? _value[this.prefix + field_name] : null;
|
2012-03-22 17:34:45 +01:00
|
|
|
|
2012-06-12 22:58:25 +02:00
|
|
|
// Check if ID was missing
|
|
|
|
if(value == null && this.id == 'custom_fields' && this.getArrayMgr("content").getEntry(this.prefix + field_name))
|
|
|
|
{
|
|
|
|
value = this.getArrayMgr("content").getEntry(this.prefix + field_name);
|
|
|
|
}
|
|
|
|
|
2012-03-22 17:34:45 +01:00
|
|
|
switch(this.options.customfields[field_name].type)
|
|
|
|
{
|
|
|
|
case 'date':
|
|
|
|
// Date custom fields are always in Y-m-d, which seldom matches user's preference
|
|
|
|
// which fails when sent to date widget. This is only used for nm rows, when possible
|
|
|
|
// this is fixed server side
|
|
|
|
if(value && isNaN(value))
|
|
|
|
{
|
|
|
|
value = jQuery.datepicker.parseDate("yy-mm-dd",value);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2011-10-11 20:18:58 +02:00
|
|
|
this.widgets[field_name].set_value(value);
|
2011-10-07 01:44:55 +02:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2012-06-12 22:58:25 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* et2_IInput so the custom field can be it's own widget.
|
|
|
|
*/
|
|
|
|
getValue: function() {
|
|
|
|
// Not using an ID means we have to grab all the widget values, and put them where server knows to look
|
|
|
|
if(this.id != 'custom_fields')
|
|
|
|
{
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
var value = {};
|
2012-06-13 17:18:34 +02:00
|
|
|
for(var field_name in this.widgets)
|
2012-06-12 22:58:25 +02:00
|
|
|
{
|
|
|
|
if(this.widgets[field_name].getValue)
|
|
|
|
{
|
|
|
|
value[this.prefix + field_name] = this.widgets[field_name].getValue();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return value;
|
|
|
|
},
|
|
|
|
|
|
|
|
isDirty: function() {
|
|
|
|
var dirty = true;
|
2012-06-13 17:18:34 +02:00
|
|
|
for(var field_name in this.widgets)
|
2012-06-12 22:58:25 +02:00
|
|
|
{
|
|
|
|
if(this.widgets[field_name].isDirty)
|
|
|
|
{
|
|
|
|
dirty = dirty && this.widgets[field_name].isDirty();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return dirty;
|
|
|
|
},
|
|
|
|
|
|
|
|
resetDirty: function() {
|
2012-06-13 17:18:34 +02:00
|
|
|
for(var field_name in this.widgets)
|
2012-06-12 22:58:25 +02:00
|
|
|
{
|
|
|
|
if(this.widgets[field_name].resetDirty)
|
|
|
|
{
|
|
|
|
this.widgets[field_name].resetDirty();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2011-10-07 18:59:50 +02:00
|
|
|
/**
|
|
|
|
* Adapt provided attributes to match options for widget
|
|
|
|
*/
|
|
|
|
_setup_text: function(field_name, field, attrs) {
|
2012-03-22 00:23:07 +01:00
|
|
|
// No label on the widget itself
|
|
|
|
delete(attrs.label);
|
|
|
|
|
2011-10-07 18:59:50 +02:00
|
|
|
field.type = 'textbox';
|
|
|
|
attrs.rows = field.rows;
|
|
|
|
attrs.size = field.len;
|
2012-03-22 00:23:07 +01:00
|
|
|
return true;
|
2011-10-07 18:59:50 +02:00
|
|
|
},
|
|
|
|
_setup_select: function(field_name, field, attrs) {
|
2012-03-22 00:23:07 +01:00
|
|
|
// No label on the widget itself
|
|
|
|
delete(attrs.label);
|
|
|
|
|
2011-10-12 22:56:11 +02:00
|
|
|
attrs.select_options = field.values;
|
2012-03-22 00:23:07 +01:00
|
|
|
return true;
|
|
|
|
},
|
2012-06-06 06:13:19 +02:00
|
|
|
_setup_htmlarea: function(field_name, field, attrs) {
|
|
|
|
attrs.config = field.config ? field.config : {};
|
|
|
|
attrs.config.toolbarStartupExpanded = false;
|
|
|
|
if(field.len)
|
|
|
|
{
|
|
|
|
var options = field.len.split(',');
|
|
|
|
if(options.length) attrs.config.width = options[0];
|
|
|
|
if(options.length > 1) attrs.config.mode = options[1];
|
|
|
|
if(options.length > 2) attrs.config.toolbarStartupExpanded = options[2];
|
|
|
|
}
|
|
|
|
attrs.config.height = ((field.rows ? field.rows : 5) *16) +'px';
|
|
|
|
return true;
|
|
|
|
},
|
2012-03-23 19:57:13 +01:00
|
|
|
_setup_radio: function(field_name, field, attrs) {
|
|
|
|
// No label on the widget itself
|
|
|
|
delete(attrs.label);
|
|
|
|
|
|
|
|
field.type = 'radiogroup';
|
|
|
|
attrs.options = field.values;
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
|
2012-03-23 00:35:25 +01:00
|
|
|
_setup_checkbox: function(field_name, field, attrs) {
|
|
|
|
// Read-only checkbox is just text
|
|
|
|
if(attrs.readonly)
|
|
|
|
{
|
|
|
|
attrs.ro_true = field.label;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
|
2012-03-22 00:23:07 +01:00
|
|
|
/**
|
|
|
|
* People set button attributes as
|
|
|
|
* label: javascript
|
|
|
|
*/
|
|
|
|
_setup_button: function(field_name, field, attrs) {
|
|
|
|
// No label on the widget itself
|
|
|
|
delete(attrs.label);
|
|
|
|
|
|
|
|
attrs.label = field.label;
|
|
|
|
for(var key in field.values)
|
|
|
|
{
|
|
|
|
attrs.label = key;
|
|
|
|
attrs.onclick = field.values[key];
|
|
|
|
}
|
|
|
|
return !attrs.readonly;
|
2011-10-07 18:59:50 +02:00
|
|
|
},
|
2011-10-18 22:09:48 +02:00
|
|
|
_setup_link_entry: function(field_name, field, attrs) {
|
2012-03-22 00:23:07 +01:00
|
|
|
// No label on the widget itself
|
|
|
|
delete(attrs.label);
|
|
|
|
|
2011-10-18 22:09:48 +02:00
|
|
|
attrs.type = "link-entry";
|
|
|
|
attrs.application = field.type;
|
2012-03-22 00:23:07 +01:00
|
|
|
return true;
|
2011-10-18 22:09:48 +02:00
|
|
|
},
|
2011-10-07 18:59:50 +02:00
|
|
|
|
2012-03-19 20:23:23 +01:00
|
|
|
/**
|
|
|
|
* Set which fields are visible, by name
|
|
|
|
*
|
|
|
|
* Note: no # prefix on the name
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
set_visible: function(_fields) {
|
|
|
|
for(var name in _fields)
|
|
|
|
{
|
|
|
|
if(this.rows[this.prefix + name])
|
|
|
|
{
|
|
|
|
if(_fields[name])
|
|
|
|
{
|
2012-03-19 20:57:02 +01:00
|
|
|
jQuery(this.rows[this.prefix+name]).show();
|
2012-03-19 20:23:23 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-03-19 20:57:02 +01:00
|
|
|
jQuery(this.rows[this.prefix+name]).hide();
|
2012-03-19 20:23:23 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
this.options.fields[name] = _fields[name];
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2011-10-07 01:44:55 +02:00
|
|
|
/**
|
2012-03-06 17:29:18 +01:00
|
|
|
* Code for implementing et2_IDetachedDOM
|
|
|
|
*/
|
2011-10-07 01:44:55 +02:00
|
|
|
getDetachedAttributes: function(_attrs)
|
|
|
|
{
|
|
|
|
_attrs.push("value", "class");
|
|
|
|
},
|
|
|
|
|
|
|
|
getDetachedNodes: function()
|
|
|
|
{
|
|
|
|
return this.detachedNodes ? this.detachedNodes : [];
|
|
|
|
},
|
|
|
|
|
|
|
|
setDetachedAttributes: function(_nodes, _values)
|
|
|
|
{
|
2011-10-18 18:32:35 +02:00
|
|
|
// This doesn't need to be implemented.
|
|
|
|
// Individual widgets are detected and handled by the grid, but the interface is needed for this to happen
|
2011-10-07 01:44:55 +02:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
et2_register_widget(et2_customfields_list, ["customfields", "customfields-list"]);
|
|
|
|
|