egroupware/api/js/etemplate/et2_extension_customfields.js

635 lines
26 KiB
JavaScript
Raw Normal View History

2011-10-07 01:44:55 +02:00
/**
* EGroupware eTemplate2 - JS Custom fields object
2011-10-07 01:44:55 +02:00
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @link https://www.egroupware.org
2011-10-07 01:44:55 +02:00
* @author Nathan Gray
* @copyright Nathan Gray 2011
*/
/*egw:uses
2020-02-11 23:20:01 +01:00
lib/tooltip;
/vendor/bower-asset/jquery/dist/jquery.js;
et2_core_xml;
et2_core_DOMWidget;
et2_core_inputWidget;
2011-10-07 01:44:55 +02:00
*/
import { et2_createWidget, et2_register_widget, et2_registry } from "./et2_core_widget";
import { ClassWithAttributes } from "./et2_core_inheritance";
import { et2_valueWidget } from "./et2_core_valueWidget";
import { et2_cloneObject, et2_no_init } from "./et2_core_common";
import { egw } from "../jsapi/egw_global";
export class et2_customfields_list extends et2_valueWidget {
constructor(_parent, _attrs, _child) {
super(_parent, _attrs, ClassWithAttributes.extendAttributes(et2_customfields_list._attributes, _child || {}));
this.rows = {};
this.widgets = {};
this.detachedNodes = [];
2020-02-11 23:20:01 +01:00
// Some apps (infolog edit) don't give ID, so assign one to get settings
if (!this.id) {
this.id = _attrs.id = et2_customfields_list.DEFAULT_ID;
2020-02-11 23:20:01 +01:00
// Add all attributes hidden in the content arrays to the attributes
// parameter
this.transformAttributes(_attrs);
2020-02-11 23:20:01 +01:00
// Create a local copy of the options object
this.options = et2_cloneObject(_attrs);
2020-02-11 23:20:01 +01:00
}
// Create the table body and the table
this.tbody = jQuery(document.createElement("tbody"));
this.table = jQuery(document.createElement("table"))
2020-02-11 23:20:01 +01:00
.addClass("et2_grid et2_customfield_list");
this.table.append(this.tbody);
if (!this.options.fields)
this.options.fields = {};
if (typeof this.options.fields === 'string') {
const fields = this.options.fields.split(',');
this.options.fields = {};
2020-02-11 23:20:01 +01:00
for (var i = 0; i < fields.length; i++) {
this.options.fields[fields[i]] = true;
2020-02-11 23:20:01 +01:00
}
}
if (this.options.type_filter && typeof this.options.type_filter == "string") {
this.options.type_filter = this.options.type_filter.split(",");
2020-02-11 23:20:01 +01:00
}
if (this.options.type_filter) {
const already_filtered = !jQuery.isEmptyObject(this.options.fields);
for (let field_name in this.options.customfields) {
2020-02-11 23:20:01 +01:00
// Already excluded?
if (already_filtered && !this.options.fields[field_name])
2020-02-11 23:20:01 +01:00
continue;
if (!this.options.customfields[field_name].type2 || this.options.customfields[field_name].type2.length == 0 ||
this.options.customfields[field_name].type2 == '0') {
2020-02-11 23:20:01 +01:00
// No restrictions
this.options.fields[field_name] = true;
2020-02-11 23:20:01 +01:00
continue;
}
const types = typeof this.options.customfields[field_name].type2 == 'string' ? this.options.customfields[field_name].type2.split(",") : this.options.customfields[field_name].type2;
this.options.fields[field_name] = false;
2020-02-11 23:20:01 +01:00
for (var i = 0; i < types.length; i++) {
if (jQuery.inArray(types[i], this.options.type_filter) > -1) {
this.options.fields[field_name] = true;
2020-02-11 23:20:01 +01:00
}
}
}
}
this.setDOMNode(this.table[0]);
2020-02-11 23:20:01 +01:00
}
destroy() {
super.destroy();
2020-02-11 23:20:01 +01:00
this.rows = {};
this.widgets = {};
this.detachedNodes = [];
this.tbody = null;
}
2020-02-11 23:20:01 +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(_obj) {
2020-02-11 23:20:01 +01:00
this.loadFields();
}
getDOMNode(_sender) {
2020-02-11 23:20:01 +01:00
// Check whether the _sender object exists inside the management array
if (this.rows && _sender.id && this.rows[_sender.id]) {
return this.rows[_sender.id];
}
2021-05-13 18:01:38 +02:00
if (this.rows && _sender.id && _sender.id.indexOf("_label") && this.rows[_sender.id.replace("_label", "")]) {
return jQuery(this.rows[_sender.id.replace("_label", "")]).prev("td")[0] || null;
}
return super.getDOMNode(_sender);
}
2020-02-11 23:20:01 +01:00
/**
* Initialize widgets for custom fields
*/
loadFields() {
2020-02-11 23:20:01 +01:00
if (!this.options || !this.options.customfields)
return;
// Already set up - avoid duplicates in nextmatch
if (this.getType() == 'customfields-list' && !this.isInTree() && Object.keys(this.widgets).length > 0)
2020-02-11 23:20:01 +01:00
return;
if (!jQuery.isEmptyObject(this.widgets))
return;
// Check for global setting changes (visibility)
const global_data = this.getArrayMgr("modifications").getRoot().getEntry('~custom_fields~');
2020-02-11 23:20:01 +01:00
if (global_data && global_data.fields && !this.options.fields)
this.options.fields = global_data.fields;
// For checking app entries
const apps = this.egw().link_app_list();
2020-02-11 23:20:01 +01:00
// Create the table rows
for (let field_name in this.options.customfields) {
2020-02-11 23:20:01 +01:00
// Skip fields if we're filtering
if (this.getType() != 'customfields-list' && !jQuery.isEmptyObject(this.options.fields) && !this.options.fields[field_name])
continue;
const field = this.options.customfields[field_name];
let id = this.options.prefix + field_name;
2020-02-11 23:20:01 +01:00
// Need curlies around ID for nm row expansion
if (this.id == '$row') {
id = "{" + this.id + "}" + "[" + this.options.prefix + field_name + "]";
2020-02-11 23:20:01 +01:00
}
else if (this.id != et2_customfields_list.DEFAULT_ID) {
// Prefix this ID to avoid potential ID collisions
id = this.id + "[" + id + "]";
}
// Avoid creating field twice
if (!this.rows[id]) {
const row = jQuery(document.createElement("tr"))
2020-02-11 23:20:01 +01:00
.appendTo(this.tbody)
.addClass(this.id + '_' + id);
let cf = jQuery(document.createElement("td"))
2020-02-11 23:20:01 +01:00
.appendTo(row);
if (!field.type)
field.type = 'text";';
const setup_function = '_setup_' + (apps[field.type] ? 'link_entry' : field.type.replace("-", "_"));
const attrs = jQuery.extend({}, this.options[field_name] ? this.options[field_name] : {}, {
2020-02-11 23:20:01 +01:00
'id': id,
'statustext': field.help,
'needed': field.needed,
'readonly': this.getArrayMgr("readonlys").isReadOnly(id, "" + this.options.readonly),
'value': this.options.value[this.options.prefix + field_name]
});
2020-02-11 23:20:01 +01:00
// Can't have a required readonly, it will warn & be removed later, so avoid the warning
if (attrs.readonly === true)
delete attrs.needed;
if (this.options.onchange) {
attrs.onchange = this.options.onchange;
}
if (this[setup_function]) {
const no_skip = this[setup_function].call(this, field_name, field, attrs);
2020-02-11 23:20:01 +01:00
if (!no_skip)
continue;
}
2021-05-13 18:01:38 +02:00
this.rows[id] = cf[0];
2020-02-11 23:20:01 +01:00
if (this.getType() == 'customfields-list') {
// No label, cust widget
attrs.readonly = true;
// Widget tooltips don't work in nextmatch because of the creation / binding separation
// Set title to field label so browser will show something
// Add field label & help as data attribute to row, so it can be stylied with CSS (title should be disabled)
row.attr('title', field.label);
row.attr('data-label', field.label);
row.attr('data-field', field_name);
row.attr('data-help', field.help);
this.detachedNodes.push(row[0]);
}
else {
// Label in first column, widget in 2nd
2021-05-13 18:01:38 +02:00
jQuery(document.createElement("td"))
.prependTo(row);
et2_createWidget("label", { id: id + "_label", value: field.label, for: id }, this);
2020-02-11 23:20:01 +01:00
}
// Set any additional attributes set in options, but not for widgets that pass actual options
if (['select', 'radio', 'radiogroup', 'checkbox', 'button'].indexOf(field.type) == -1 && !jQuery.isEmptyObject(field.values)) {
const w = et2_registry[attrs.type ? attrs.type : field.type];
for (let attr_name in field.values) {
if (typeof w._attributes[attr_name] != "undefined") {
2020-02-11 23:20:01 +01:00
attrs[attr_name] = field.values[attr_name];
}
2020-02-11 23:20:01 +01:00
}
}
// Create widget
const widget = this.widgets[field_name] = et2_createWidget(attrs.type ? attrs.type : field.type, attrs, this);
2020-02-11 23:20:01 +01: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();
}
}
}
2020-02-11 23:20:01 +01:00
/**
* Read needed info on available custom fields from various places it's stored.
*/
transformAttributes(_attrs) {
super.transformAttributes(_attrs);
2020-02-11 23:20:01 +01:00
// Add in settings that are objects
// Customized settings for this widget (unlikely)
const data = this.getArrayMgr("modifications").getEntry(this.id);
2020-02-11 23:20:01 +01:00
// Check for global settings
const global_data = this.getArrayMgr("modifications").getRoot().getEntry('~custom_fields~', true);
2020-02-11 23:20:01 +01:00
if (global_data) {
for (let key in data) {
2020-02-11 23:20:01 +01:00
// Don't overwrite fields with global values
if (global_data[key] && key !== 'fields') {
data[key] = jQuery.extend(true, {}, data[key], global_data[key]);
2020-02-11 23:20:01 +01:00
}
}
}
for (var key in data) {
_attrs[key] = data[key];
}
for (let key in global_data) {
if (typeof global_data[key] != 'undefined' && !_attrs[key])
_attrs[key] = global_data[key];
2020-02-11 23:20:01 +01:00
}
if (this.id) {
// Set the value for this element
const contentMgr = this.getArrayMgr("content");
2020-02-11 23:20:01 +01:00
if (contentMgr != null) {
const val = contentMgr.getEntry(this.id);
2020-02-11 23:20:01 +01:00
_attrs["value"] = {};
let prefix = _attrs["prefix"] || et2_customfields_list.PREFIX;
2020-02-11 23:20:01 +01:00
if (val !== null) {
if (this.id.indexOf(prefix) === 0 && typeof data.fields != 'undefined' && data.fields[this.id.replace(prefix, '')] === true) {
2020-02-11 23:20:01 +01:00
_attrs['value'][this.id] = val;
}
else {
// Only set the values that match desired custom fields
for (let key in val) {
if (key.indexOf(prefix) === 0) {
_attrs["value"][key] = val[key];
2020-02-11 23:20:01 +01:00
}
}
}
//_attrs["value"] = val;
}
else {
// Check for custom fields directly in record
for (var key in _attrs.customfields) {
_attrs["value"][prefix + key] = contentMgr.getEntry(prefix + key);
2020-02-11 23:20:01 +01:00
}
}
}
}
}
loadFromXML(_node) {
2020-02-11 23:20:01 +01:00
this.loadFields();
// Load the nodes as usual
super.loadFromXML(_node);
}
set_value(_value) {
2020-02-11 23:20:01 +01:00
if (!this.options.customfields)
return;
for (let field_name in this.options.customfields) {
2020-02-11 23:20:01 +01:00
// Skip fields if we're filtering
if (!jQuery.isEmptyObject(this.options.fields) && !this.options.fields[field_name])
continue;
// Make sure widget is created, and has the needed function
if (!this.widgets[field_name] || !this.widgets[field_name].set_value)
continue;
let value = _value[this.options.prefix + field_name] ? _value[this.options.prefix + field_name] : null;
2020-02-11 23:20:01 +01:00
// Check if ID was missing
if (value == null && this.id == et2_customfields_list.DEFAULT_ID && this.getArrayMgr("content").getEntry(this.options.prefix + field_name)) {
value = this.getArrayMgr("content").getEntry(this.options.prefix + field_name);
2020-02-11 23:20:01 +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;
}
this.widgets[field_name].set_value(value);
}
}
2020-02-11 23:20:01 +01:00
/**
* et2_IInput so the custom field can be it's own widget.
*/
getValue() {
2020-02-11 23:20:01 +01:00
// Not using an ID means we have to grab all the widget values, and put them where server knows to look
if (this.id != et2_customfields_list.DEFAULT_ID) {
return null;
}
const value = {};
for (let field_name in this.widgets) {
2020-02-11 23:20:01 +01:00
if (this.widgets[field_name].getValue && !this.widgets[field_name].options.readonly) {
value[this.options.prefix + field_name] = this.widgets[field_name].getValue();
2020-02-11 23:20:01 +01:00
}
}
return value;
}
isDirty() {
let dirty = false;
for (let field_name in this.widgets) {
2020-02-11 23:20:01 +01:00
if (this.widgets[field_name].isDirty) {
dirty = dirty || this.widgets[field_name].isDirty();
2020-02-11 23:20:01 +01:00
}
}
return dirty;
}
resetDirty() {
for (let field_name in this.widgets) {
2020-02-11 23:20:01 +01:00
if (this.widgets[field_name].resetDirty) {
this.widgets[field_name].resetDirty();
}
}
}
isValid() {
2020-02-11 23:20:01 +01:00
// Individual customfields will handle themselves
return true;
}
2020-02-11 23:20:01 +01:00
/**
* Adapt provided attributes to match options for widget
*
* rows > 1 --> textarea, with rows=rows and cols=len
* !rows --> input, with size=len
* rows = 1 --> input, with size=len, maxlength=len
*/
_setup_text(field_name, field, attrs) {
2020-02-11 23:20:01 +01:00
// No label on the widget itself
delete (attrs.label);
field.type = 'textbox';
attrs.rows = field.rows > 1 ? field.rows : null;
if (field.len) {
attrs.size = field.len;
if (field.rows == 1)
attrs.maxlength = field.len;
}
return true;
}
_setup_passwd(field_name, field, attrs) {
// No label on the widget itself
delete (attrs.label);
let defaults = {
viewable: true,
plaintext: false,
suggest: 16
};
for (let key of Object.keys(defaults)) {
attrs[key] = (field.values && typeof field.values[key] !== "undefined") ? field.values[key] : defaults[key];
}
return true;
}
_setup_ajax_select(field_name, field, attrs) {
const attributes = ['get_rows', 'get_title', 'id_field', 'template'];
2020-02-11 23:20:01 +01:00
if (field.values) {
for (let i = 0; i < attributes.length; i++) {
2020-02-11 23:20:01 +01:00
if (typeof field.values[attributes[i]] !== 'undefined') {
attrs[attributes[i]] = field.values[attributes[i]];
}
}
}
return true;
}
_setup_float(field_name, field, attrs) {
2020-02-11 23:20:01 +01:00
// No label on the widget itself
delete (attrs.label);
field.type = 'float';
if (field.len) {
attrs.size = field.len;
}
return true;
}
_setup_select(field_name, field, attrs) {
2020-02-11 23:20:01 +01:00
// No label on the widget itself
delete (attrs.label);
attrs.rows = field.rows;
// select_options are now send from server-side incl. ones defined via a file in EGroupware root
attrs.tags = field.tags;
return true;
}
_setup_select_account(field_name, field, attrs) {
2020-02-11 23:20:01 +01:00
attrs.empty_label = egw.lang('Select');
if (field.account_type) {
attrs.account_type = field.account_type;
}
return this._setup_select(field_name, field, attrs);
}
_setup_date(field_name, field, attrs) {
2020-02-11 23:20:01 +01:00
attrs.data_format = field.values && field.values.format ? field.values.format : 'Y-m-d';
return true;
}
_setup_date_time(field_name, field, attrs) {
2020-02-11 23:20:01 +01:00
attrs.data_format = field.values && field.values.format ? field.values.format : 'Y-m-d H:i:s';
return true;
}
_setup_htmlarea(field_name, field, attrs) {
2020-02-11 23:20:01 +01:00
attrs.config = field.config ? field.config : {};
attrs.config.toolbarStartupExpanded = false;
if (field.len) {
attrs.config.width = field.len + 'px';
}
attrs.config.height = (((field.rows > 0 && field.rows != 'undefined') ? field.rows : 5) * 16) + 'px';
// We have to push the config modifications into the modifications array, or they'll
// be overwritten by the site config from the server
const data = this.getArrayMgr("modifications").getEntry(this.options.prefix + field_name);
2020-02-11 23:20:01 +01:00
if (data)
jQuery.extend(data.config, attrs.config);
return true;
}
_setup_radio(field_name, field, attrs) {
2020-02-11 23:20:01 +01:00
// 'Empty' label will be first
delete (attrs.label);
if (field.values && field.values['']) {
attrs.label = field.values[''];
delete field.values[''];
}
field.type = 'radiogroup';
attrs.options = field.values;
return true;
}
_setup_checkbox(field_name, field, attrs) {
2020-02-11 23:20:01 +01:00
// Read-only checkbox is just text
if (attrs.readonly && this.getType() !== "customfields") {
2020-02-11 23:20:01 +01:00
attrs.ro_true = field.label;
}
else if (field.hasOwnProperty('ro_true')) {
attrs.ro_true = field.ro_true;
}
if (field.hasOwnProperty('ro_false')) {
attrs.ro_false = field.ro_false;
}
2020-02-11 23:20:01 +01:00
return true;
}
2020-02-11 23:20:01 +01:00
/**
* People set button attributes as
* label: javascript
*/
_setup_button(field_name, field, attrs) {
2020-02-11 23:20:01 +01:00
// No label on the widget itself
delete (attrs.label);
attrs.label = field.label;
if (this.getType() == 'customfields-list') {
// No buttons in a list, it causes problems with detached nodes
return false;
}
// Simple case, one widget for a custom field
if (!field.values || typeof field.values != 'object' || Object.keys(field.values).length == 1) {
for (let key in field.values) {
attrs.label = key;
attrs.onclick = field.values[key];
2020-02-11 23:20:01 +01:00
}
if (!attrs.label) {
attrs.label = 'No "label=onclick" in values!';
attrs.onclick = function () { return false; };
}
return !attrs.readonly;
}
else {
// Complicated case, a single custom field you get multiple widgets
// Handle it all here, since this is the exception
const row = jQuery('tr', this.tbody).last();
let cf = jQuery('td', row);
2020-02-11 23:20:01 +01:00
// Label in first column, widget in 2nd
cf.text(field.label + "");
cf = jQuery(document.createElement("td"))
.appendTo(row);
for (var key in field.values) {
const button_attrs = jQuery.extend({}, attrs);
2020-02-11 23:20:01 +01:00
button_attrs.label = key;
button_attrs.onclick = field.values[key];
button_attrs.id = attrs.id + '_' + key;
// This controls where the button is placed in the DOM
this.rows[button_attrs.id] = cf[0];
// Do not store in the widgets list, one name for multiple widgets would cause problems
/*this.widgets[field_name] = */ et2_createWidget(attrs.type ? attrs.type : field.type, button_attrs, this);
2020-02-11 23:20:01 +01:00
}
return false;
}
}
_setup_link_entry(field_name, field, attrs) {
2020-02-11 23:20:01 +01:00
if (field.type === 'filemanager') {
return this._setup_filemanager(field_name, field, attrs);
}
// No label on the widget itself
delete (attrs.label);
attrs.type = "link-entry";
attrs.only_app = typeof field.only_app == "undefined" ? field.type : field.only_app;
2020-02-11 23:20:01 +01:00
return true;
}
_setup_filemanager(field_name, field, attrs) {
2020-02-11 23:20:01 +01:00
attrs.type = 'vfs-upload';
delete (attrs.label);
if (this.getType() == 'customfields-list') {
// No special UI needed?
return true;
}
else {
// Complicated case, a single custom field you get multiple widgets
// Handle it all here, since this is the exception
const row = jQuery('tr', this.tbody).last();
let cf = jQuery('td', row);
2020-02-11 23:20:01 +01:00
// Label in first column, widget in 2nd
cf.text(field.label + "");
cf = jQuery(document.createElement("td"))
.appendTo(row);
// Create upload widget
let widget = this.widgets[field_name] = et2_createWidget(attrs.type ? attrs.type : field.type, attrs, this);
2020-02-11 23:20:01 +01:00
// This controls where the widget is placed in the DOM
this.rows[attrs.id] = cf[0];
jQuery(widget.getDOMNode(widget)).css('vertical-align', 'top');
// Add a link to existing VFS file
const select_attrs = jQuery.extend({}, attrs,
2020-02-11 23:20:01 +01:00
// Filemanager select
{
label: '',
mode: widget.options.multiple ? 'open-multiple' : 'open',
method: 'EGroupware\\Api\\Etemplate\\Widget\\Link::ajax_link_existing',
method_id: attrs.path,
button_label: egw.lang('Link')
}, { type: 'vfs-select' });
select_attrs.id = attrs.id + '_vfs_select';
// This controls where the button is placed in the DOM
this.rows[select_attrs.id] = cf[0];
// Do not store in the widgets list, one name for multiple widgets would cause problems
widget = et2_createWidget(select_attrs.type, select_attrs, this);
2020-02-11 23:20:01 +01:00
jQuery(widget.getDOMNode(widget)).css('vertical-align', 'top').prependTo(cf);
}
return false;
}
/**
* Display links in list as CF name
* @param field_name
* @param field
* @param attrs
*/
_setup_url(field_name, field, attrs) {
if (this.getType() == 'customfields-list') {
attrs.label = field.label;
}
return true;
}
2020-02-11 23:20:01 +01:00
/**
* Set which fields are visible, by name
*
* Note: no # prefix on the name
*
*/
set_visible(_fields) {
for (let name in _fields) {
if (this.rows[this.options.prefix + name]) {
if (_fields[name]) {
jQuery(this.rows[this.options.prefix + name]).show();
2020-02-11 23:20:01 +01:00
}
else {
jQuery(this.rows[this.options.prefix + name]).hide();
2020-02-11 23:20:01 +01:00
}
}
this.options.fields[name] = _fields[name];
2020-02-11 23:20:01 +01:00
}
}
2020-02-11 23:20:01 +01:00
/**
* Code for implementing et2_IDetachedDOM
*/
getDetachedAttributes(_attrs) {
2020-02-11 23:20:01 +01:00
_attrs.push("value", "class");
}
getDetachedNodes() {
2020-02-11 23:20:01 +01:00
return this.detachedNodes ? this.detachedNodes : [];
}
setDetachedAttributes(_nodes, _values) {
2020-02-11 23:20:01 +01:00
// Individual widgets are detected and handled by the grid, but the interface is needed for this to happen
// Show the row if there's a value, hide it if there is no value
for (let i = 0; i < _nodes.length; i++) {
2020-02-11 23:20:01 +01:00
// toggle() needs a boolean to do what we want
const key = _nodes[i].getAttribute('data-field');
jQuery(_nodes[i]).toggle(_values.fields[key] && _values.value[this.options.prefix + key] ? true : false);
2020-02-11 23:20:01 +01:00
}
}
}
et2_customfields_list._attributes = {
'customfields': {
'name': 'Custom fields',
'description': 'Auto filled',
'type': 'any'
},
'fields': {
'name': 'Custom fields',
'description': 'Auto filled',
'type': 'any'
},
'value': {
'name': 'Custom fields',
'description': 'Auto filled',
'type': "any"
},
'type_filter': {
'name': 'Field filter',
"default": "",
"type": "any",
"description": "Filter displayed custom fields by their 'type2' attribute"
},
'private': {
ignore: true,
type: 'boolean'
},
'sub_app': {
'name': 'sub app name',
'type': "string",
'description': "Name of sub application"
},
// Allow onchange so you can put handlers on the sub-widgets
'onchange': {
"name": "onchange",
"type": "string",
"default": et2_no_init,
"description": "JS code which is executed when the value changes."
},
// Allow changing the field prefix. Normally it's the constant but importexport filter changes it.
"prefix": {
name: "prefix",
type: "string",
default: "#",
description: "Custom prefix for custom fields. Default #"
}
};
et2_customfields_list.legacyOptions = ["type_filter", "private", "fields"]; // Field restriction & private done server-side
et2_customfields_list.PREFIX = '#';
et2_customfields_list.DEFAULT_ID = "custom_fields";
et2_register_widget(et2_customfields_list, ["customfields", "customfields-list"]);
2020-02-11 23:20:01 +01:00
//# sourceMappingURL=et2_extension_customfields.js.map