|
1
|
|
|
/** |
|
2
|
|
|
* EGroupware eTemplate2 - JS Custom fields object |
|
3
|
|
|
* |
|
4
|
|
|
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License |
|
5
|
|
|
* @package etemplate |
|
6
|
|
|
* @subpackage api |
|
7
|
|
|
* @link http://www.egroupware.org |
|
8
|
|
|
* @author Nathan Gray |
|
9
|
|
|
* @copyright Nathan Gray 2011 |
|
10
|
|
|
* @version $Id$ |
|
11
|
|
|
*/ |
|
12
|
|
|
|
|
13
|
|
|
/*egw:uses |
|
14
|
|
|
lib/tooltip; |
|
15
|
|
|
/vendor/bower-asset/jquery/dist/jquery.js; |
|
16
|
|
|
et2_core_xml; |
|
17
|
|
|
et2_core_DOMWidget; |
|
18
|
|
|
et2_core_inputWidget; |
|
19
|
|
|
*/ |
|
20
|
|
|
|
|
21
|
|
|
/** |
|
22
|
|
|
* @augments et2_dataview |
|
23
|
|
|
*/ |
|
24
|
|
|
var et2_customfields_list = (function(){ "use strict"; return et2_valueWidget.extend([et2_IDetachedDOM, et2_IInput], |
|
25
|
|
|
{ |
|
26
|
|
|
attributes: { |
|
27
|
|
|
'customfields': { |
|
28
|
|
|
'name': 'Custom fields', |
|
29
|
|
|
'description': 'Auto filled', |
|
30
|
|
|
'type': 'any' |
|
31
|
|
|
}, |
|
32
|
|
|
'fields': { |
|
33
|
|
|
'name': 'Custom fields', |
|
34
|
|
|
'description': 'Auto filled', |
|
35
|
|
|
'type': 'any' |
|
36
|
|
|
}, |
|
37
|
|
|
'value': { |
|
38
|
|
|
'name': 'Custom fields', |
|
39
|
|
|
'description': 'Auto filled', |
|
40
|
|
|
'type': "any" |
|
41
|
|
|
}, |
|
42
|
|
|
'type_filter': { |
|
43
|
|
|
'name': 'Field filter', |
|
44
|
|
|
"default": "", |
|
45
|
|
|
"type": "any", // String or array |
|
46
|
|
|
"description": "Filter displayed custom fields by their 'type2' attribute" |
|
47
|
|
|
}, |
|
48
|
|
|
'private': { |
|
49
|
|
|
ignore: true, |
|
50
|
|
|
type: 'boolean' |
|
51
|
|
|
} |
|
52
|
|
|
}, |
|
53
|
|
|
|
|
54
|
|
|
legacyOptions: ["type_filter","private", "fields"], // Field restriction & private done server-side |
|
55
|
|
|
|
|
56
|
|
|
prefix: '#', |
|
57
|
|
|
|
|
58
|
|
|
DEFAULT_ID: "custom_fields", |
|
59
|
|
|
|
|
60
|
|
|
/** |
|
61
|
|
|
* Constructor |
|
62
|
|
|
* |
|
63
|
|
|
* @memberOf et2_customfields_list |
|
64
|
|
|
*/ |
|
65
|
|
|
init: function() { |
|
66
|
|
|
// Some apps (infolog edit) don't give ID, so assign one to get settings |
|
67
|
|
|
if(!arguments[1].id) arguments[1].id = this.DEFAULT_ID; |
|
68
|
|
|
|
|
69
|
|
|
this._super.apply(this, arguments); |
|
70
|
|
|
|
|
71
|
|
|
// Allows server side to override prefix - not an attribute though |
|
72
|
|
|
if(typeof this.options.prefix != 'undefined') this.prefix = this.options.prefix; |
|
73
|
|
|
|
|
74
|
|
|
// Create the table body and the table |
|
75
|
|
|
this.tbody = jQuery(document.createElement("tbody")); |
|
76
|
|
|
this.table = jQuery(document.createElement("table")) |
|
77
|
|
|
.addClass("et2_grid et2_customfield_list"); |
|
78
|
|
|
this.table.append(this.tbody); |
|
79
|
|
|
|
|
80
|
|
|
this.rows = {}; |
|
81
|
|
|
this.widgets = {}; |
|
82
|
|
|
this.detachedNodes = []; |
|
83
|
|
|
if(!this.options.fields) this.options.fields = {}; |
|
84
|
|
|
|
|
85
|
|
|
if(this.options.type_filter && typeof this.options.type_filter == "string") |
|
86
|
|
|
{ |
|
87
|
|
|
this.options.type_filter = this.options.type_filter.split(","); |
|
88
|
|
|
} |
|
89
|
|
|
if(this.options.type_filter) |
|
90
|
|
|
{ |
|
91
|
|
|
var already_filtered = !jQuery.isEmptyObject(this.options.fields); |
|
92
|
|
|
for(var field_name in this.options.customfields) |
|
93
|
|
|
{ |
|
94
|
|
|
// Already excluded? |
|
95
|
|
|
if(already_filtered && !this.options.fields[field_name]) continue; |
|
96
|
|
|
|
|
97
|
|
|
if(!this.options.customfields[field_name].type2 || this.options.customfields[field_name].type2.length == 0 || |
|
98
|
|
|
this.options.customfields[field_name].type2 == '0') |
|
99
|
|
|
{ |
|
100
|
|
|
// No restrictions |
|
101
|
|
|
this.options.fields[field_name] = true; |
|
102
|
|
|
continue; |
|
103
|
|
|
} |
|
104
|
|
|
var types = typeof this.options.customfields[field_name].type2 == 'string' ? this.options.customfields[field_name].type2.split(",") : this.options.customfields[field_name].type2; |
|
105
|
|
|
this.options.fields[field_name] = false; |
|
106
|
|
|
for(var i = 0; i < types.length; i++) |
|
107
|
|
|
{ |
|
108
|
|
|
if(jQuery.inArray(types[i],this.options.type_filter) > -1) |
|
109
|
|
|
{ |
|
110
|
|
|
this.options.fields[field_name] = true; |
|
111
|
|
|
continue; |
|
|
|
|
|
|
112
|
|
|
} |
|
113
|
|
|
} |
|
114
|
|
|
} |
|
115
|
|
|
} |
|
116
|
|
|
|
|
117
|
|
|
this.setDOMNode(this.table[0]); |
|
118
|
|
|
}, |
|
119
|
|
|
|
|
120
|
|
|
destroy: function() { |
|
121
|
|
|
this._super.apply(this, arguments); |
|
122
|
|
|
this.rows = {}; |
|
123
|
|
|
this.widgets = {}; |
|
124
|
|
|
this.detachedNodes = []; |
|
125
|
|
|
this.tbody = null; |
|
126
|
|
|
}, |
|
127
|
|
|
|
|
128
|
|
|
/** |
|
129
|
|
|
* What does this do? I don't know, but when everything is done the second |
|
130
|
|
|
* time, this makes it work. Otherwise, all custom fields are lost. |
|
131
|
|
|
*/ |
|
132
|
|
|
assign: function(_obj) { |
|
133
|
|
|
this.loadFields(); |
|
134
|
|
|
}, |
|
135
|
|
|
|
|
136
|
|
|
getDOMNode: function(_sender) { |
|
137
|
|
|
|
|
138
|
|
|
// Check whether the _sender object exists inside the management array |
|
139
|
|
|
if(this.rows && _sender.id && this.rows[_sender.id]) |
|
140
|
|
|
{ |
|
141
|
|
|
return this.rows[_sender.id]; |
|
142
|
|
|
} |
|
143
|
|
|
|
|
144
|
|
|
return this._super.apply(this, arguments); |
|
145
|
|
|
}, |
|
146
|
|
|
|
|
147
|
|
|
/** |
|
148
|
|
|
* Initialize widgets for custom fields |
|
149
|
|
|
*/ |
|
150
|
|
|
loadFields: function() { |
|
151
|
|
|
if(!this.options || !this.options.customfields) return; |
|
152
|
|
|
|
|
153
|
|
|
// Already set up - avoid duplicates in nextmatch |
|
154
|
|
|
if(this._type == 'customfields-list' && !this.isInTree()) return; |
|
155
|
|
|
if(!jQuery.isEmptyObject(this.widgets)) return; |
|
156
|
|
|
|
|
157
|
|
|
// Check for global setting changes (visibility) |
|
158
|
|
|
var global_data = this.getArrayMgr("modifications").getRoot().getEntry('~custom_fields~'); |
|
159
|
|
|
if(global_data && global_data.fields && !this.options.fields) this.options.fields = global_data.fields; |
|
160
|
|
|
|
|
161
|
|
|
// For checking app entries |
|
162
|
|
|
var apps = this.egw().link_app_list(); |
|
163
|
|
|
|
|
164
|
|
|
// Create the table rows |
|
165
|
|
|
for(var field_name in this.options.customfields) |
|
166
|
|
|
{ |
|
167
|
|
|
// Skip fields if we're filtering |
|
168
|
|
|
if(this._type != 'customfields-list' && !jQuery.isEmptyObject(this.options.fields) && !this.options.fields[field_name]) continue; |
|
169
|
|
|
|
|
170
|
|
|
var field = this.options.customfields[field_name]; |
|
171
|
|
|
|
|
172
|
|
|
var id = this.prefix+field_name; |
|
173
|
|
|
|
|
174
|
|
|
// Need curlies around ID for nm row expansion |
|
175
|
|
|
if(this.id == '$row') |
|
176
|
|
|
{ |
|
177
|
|
|
id = "{" + this.id + "}" + "["+this.prefix + field_name+"]"; |
|
178
|
|
|
} |
|
179
|
|
|
else if (this.id != this.DEFAULT_ID) |
|
180
|
|
|
{ |
|
181
|
|
|
// Prefix this ID to avoid potential ID collisions |
|
182
|
|
|
id = this.id + "["+id+"]"; |
|
183
|
|
|
} |
|
184
|
|
|
|
|
185
|
|
|
// Avoid creating field twice |
|
186
|
|
|
if(!this.rows[id]) |
|
187
|
|
|
{ |
|
188
|
|
|
|
|
189
|
|
|
var row = jQuery(document.createElement("tr")) |
|
190
|
|
|
.appendTo(this.tbody) |
|
191
|
|
|
.addClass(this.id+'_'+id); |
|
192
|
|
|
var cf = jQuery(document.createElement("td")) |
|
193
|
|
|
.appendTo(row); |
|
194
|
|
|
if(!field.type) field.type = 'text";' |
|
195
|
|
|
var setup_function = '_setup_'+(apps[field.type] ? 'link_entry' : field.type.replace("-","_")); |
|
196
|
|
|
|
|
197
|
|
|
var attrs = { |
|
198
|
|
|
'id': id, |
|
199
|
|
|
'statustext': field.help, |
|
200
|
|
|
'needed': field.needed, |
|
201
|
|
|
'readonly': this.getArrayMgr("readonlys").isReadOnly(id, null, this.options.readonly), |
|
202
|
|
|
'value': this.options.value[this.prefix+field_name] |
|
203
|
|
|
}; |
|
204
|
|
|
// Can't have a required readonly, it will warn & be removed later, so avoid the warning |
|
205
|
|
|
if(attrs.readonly === true) delete attrs.needed; |
|
206
|
|
|
|
|
207
|
|
|
if(this[setup_function]) { |
|
208
|
|
|
var no_skip = this[setup_function].call(this, field_name, field, attrs); |
|
209
|
|
|
if(!no_skip) continue; |
|
210
|
|
|
} |
|
211
|
|
|
|
|
212
|
|
|
if(this._type == 'customfields-list') { |
|
213
|
|
|
// No label, cust widget |
|
214
|
|
|
attrs.readonly = true; |
|
215
|
|
|
// Widget tooltips don't work in nextmatch because of the creation / binding separation |
|
216
|
|
|
// Set title to field label so browser will show something |
|
217
|
|
|
// Add field label & help as data attribute to row, so it can be stylied with CSS (title should be disabled) |
|
218
|
|
|
row.attr('title', field.label); |
|
219
|
|
|
row.attr('data-label', field.label); |
|
220
|
|
|
row.attr('data-field', field_name); |
|
221
|
|
|
row.attr('data-help', field.help); |
|
222
|
|
|
this.detachedNodes.push(row[0]); |
|
223
|
|
|
} |
|
224
|
|
|
else |
|
225
|
|
|
{ |
|
226
|
|
|
// Label in first column, widget in 2nd |
|
227
|
|
|
cf.text(field.label + ""); |
|
228
|
|
|
cf = jQuery(document.createElement("td")) |
|
229
|
|
|
.appendTo(row); |
|
230
|
|
|
} |
|
231
|
|
|
this.rows[id] = cf[0]; |
|
232
|
|
|
|
|
233
|
|
|
// Set any additional attributes set in options, but not for widgets that pass actual options |
|
234
|
|
|
if(['select','radio','radiogroup','checkbox','button'].indexOf(field.type) == -1 && !jQuery.isEmptyObject(field.values)) |
|
235
|
|
|
{ |
|
236
|
|
|
var w = et2_registry[attrs.type ? attrs.type : field.type]; |
|
237
|
|
|
for(var attr_name in field.values) |
|
238
|
|
|
{ |
|
239
|
|
|
if (typeof w.prototype.attributes[attr_name] != "undefined") |
|
240
|
|
|
attrs[attr_name] = field.values[attr_name]; |
|
241
|
|
|
} |
|
242
|
|
|
} |
|
243
|
|
|
// Create widget |
|
244
|
|
|
var widget = this.widgets[field_name] = et2_createWidget(attrs.type ? attrs.type : field.type, attrs, this); |
|
245
|
|
|
} |
|
246
|
|
|
|
|
247
|
|
|
// Field is not to be shown |
|
248
|
|
|
if(!this.options.fields || jQuery.isEmptyObject(this.options.fields) || this.options.fields[field_name] == true) |
|
249
|
|
|
{ |
|
250
|
|
|
jQuery(this.rows[field_name]).show(); |
|
251
|
|
|
} |
|
252
|
|
|
else |
|
253
|
|
|
{ |
|
254
|
|
|
jQuery(this.rows[field_name]).hide(); |
|
255
|
|
|
} |
|
256
|
|
|
|
|
257
|
|
|
} |
|
258
|
|
|
}, |
|
259
|
|
|
|
|
260
|
|
|
/** |
|
261
|
|
|
* Read needed info on available custom fields from various places it's stored. |
|
262
|
|
|
*/ |
|
263
|
|
|
transformAttributes: function(_attrs) { |
|
264
|
|
|
this._super.apply(this, arguments); |
|
265
|
|
|
|
|
266
|
|
|
// Add in settings that are objects |
|
267
|
|
|
|
|
268
|
|
|
// Customized settings for this widget (unlikely) |
|
269
|
|
|
var data = this.getArrayMgr("modifications").getEntry(this.id); |
|
270
|
|
|
// Check for global settings |
|
271
|
|
|
var global_data = this.getArrayMgr("modifications").getRoot().getEntry('~custom_fields~', true); |
|
272
|
|
|
if(global_data) |
|
273
|
|
|
{ |
|
274
|
|
|
for(var key in data) |
|
275
|
|
|
{ |
|
276
|
|
|
// Don't overwrite fields with global values |
|
277
|
|
|
if(global_data[key] && key !== 'fields') |
|
278
|
|
|
{ |
|
279
|
|
|
data[key] = jQuery.extend(true, {}, data[key], global_data[key]); |
|
280
|
|
|
} |
|
281
|
|
|
} |
|
282
|
|
|
} |
|
283
|
|
|
for(var key in data) |
|
284
|
|
|
{ |
|
285
|
|
|
_attrs[key] = data[key]; |
|
286
|
|
|
} |
|
287
|
|
|
for(var key in global_data) |
|
288
|
|
|
{ |
|
289
|
|
|
if(typeof global_data[key] != 'undefined' && ! _attrs[key]) _attrs[key] = global_data[key]; |
|
290
|
|
|
} |
|
291
|
|
|
|
|
292
|
|
|
if (this.id) |
|
293
|
|
|
{ |
|
294
|
|
|
// Set the value for this element |
|
295
|
|
|
var contentMgr = this.getArrayMgr("content"); |
|
296
|
|
|
if (contentMgr != null) { |
|
297
|
|
|
var val = contentMgr.getEntry(this.id); |
|
298
|
|
|
_attrs["value"] = {}; |
|
299
|
|
|
if (val !== null) |
|
300
|
|
|
{ |
|
301
|
|
|
if(this.id.indexOf(this.prefix) === 0 && typeof data.fields != 'undefined' && data.fields[this.id.replace(this.prefix,'')] === true) |
|
302
|
|
|
{ |
|
303
|
|
|
_attrs['value'][this.id] = val; |
|
304
|
|
|
} |
|
305
|
|
|
else |
|
306
|
|
|
{ |
|
307
|
|
|
// Only set the values that match desired custom fields |
|
308
|
|
|
for(var key in val) |
|
309
|
|
|
{ |
|
310
|
|
|
if(key.indexOf(this.prefix) == 0) { |
|
311
|
|
|
_attrs["value"][key] = val[key]; |
|
312
|
|
|
} |
|
313
|
|
|
} |
|
314
|
|
|
} |
|
315
|
|
|
//_attrs["value"] = val; |
|
316
|
|
|
} |
|
317
|
|
|
else |
|
318
|
|
|
{ |
|
319
|
|
|
// Check for custom fields directly in record |
|
320
|
|
|
for(var key in _attrs.customfields) |
|
321
|
|
|
{ |
|
322
|
|
|
_attrs["value"][this.prefix + key] = contentMgr.getEntry(this.prefix + key); |
|
323
|
|
|
} |
|
324
|
|
|
} |
|
325
|
|
|
} |
|
326
|
|
|
} |
|
327
|
|
|
}, |
|
328
|
|
|
|
|
329
|
|
|
loadFromXML: function(_node) { |
|
330
|
|
|
this.loadFields(); |
|
331
|
|
|
|
|
332
|
|
|
// Load the nodes as usual |
|
333
|
|
|
this._super.apply(this, arguments); |
|
334
|
|
|
}, |
|
335
|
|
|
|
|
336
|
|
|
set_value: function(_value) { |
|
337
|
|
|
if(!this.options.customfields) return; |
|
338
|
|
|
for(var field_name in this.options.customfields) |
|
339
|
|
|
{ |
|
340
|
|
|
// Skip fields if we're filtering |
|
341
|
|
|
if(!jQuery.isEmptyObject(this.options.fields) && !this.options.fields[field_name]) continue; |
|
342
|
|
|
|
|
343
|
|
|
// Make sure widget is created, and has the needed function |
|
344
|
|
|
if(!this.widgets[field_name] || !this.widgets[field_name].set_value) continue; |
|
345
|
|
|
var value = _value[this.prefix + field_name] ? _value[this.prefix + field_name] : null; |
|
346
|
|
|
|
|
347
|
|
|
// Check if ID was missing |
|
348
|
|
|
if(value == null && this.id == this.DEFAULT_ID && this.getArrayMgr("content").getEntry(this.prefix + field_name)) |
|
349
|
|
|
{ |
|
350
|
|
|
value = this.getArrayMgr("content").getEntry(this.prefix + field_name); |
|
351
|
|
|
} |
|
352
|
|
|
|
|
353
|
|
|
switch(this.options.customfields[field_name].type) |
|
|
|
|
|
|
354
|
|
|
{ |
|
355
|
|
|
case 'date': |
|
356
|
|
|
// Date custom fields are always in Y-m-d, which seldom matches user's preference |
|
357
|
|
|
// which fails when sent to date widget. This is only used for nm rows, when possible |
|
358
|
|
|
// this is fixed server side |
|
359
|
|
|
if(value && isNaN(value)) |
|
360
|
|
|
{ |
|
361
|
|
|
value = jQuery.datepicker.parseDate("yy-mm-dd",value); |
|
362
|
|
|
} |
|
363
|
|
|
break; |
|
364
|
|
|
} |
|
365
|
|
|
this.widgets[field_name].set_value(value); |
|
366
|
|
|
} |
|
367
|
|
|
}, |
|
368
|
|
|
|
|
369
|
|
|
/** |
|
370
|
|
|
* et2_IInput so the custom field can be it's own widget. |
|
371
|
|
|
*/ |
|
372
|
|
|
getValue: function() { |
|
373
|
|
|
// Not using an ID means we have to grab all the widget values, and put them where server knows to look |
|
374
|
|
|
if(this.id != this.DEFAULT_ID) |
|
375
|
|
|
{ |
|
376
|
|
|
return null; |
|
377
|
|
|
} |
|
378
|
|
|
var value = {}; |
|
379
|
|
|
for(var field_name in this.widgets) |
|
380
|
|
|
{ |
|
381
|
|
|
if(this.widgets[field_name].getValue && !this.widgets[field_name].options.readonly) |
|
382
|
|
|
{ |
|
383
|
|
|
value[this.prefix + field_name] = this.widgets[field_name].getValue(); |
|
384
|
|
|
} |
|
385
|
|
|
} |
|
386
|
|
|
return value; |
|
387
|
|
|
}, |
|
388
|
|
|
|
|
389
|
|
|
isDirty: function() { |
|
390
|
|
|
var dirty = true; |
|
391
|
|
|
for(var field_name in this.widgets) |
|
392
|
|
|
{ |
|
393
|
|
|
if(this.widgets[field_name].isDirty) |
|
394
|
|
|
{ |
|
395
|
|
|
dirty = dirty && this.widgets[field_name].isDirty(); |
|
396
|
|
|
} |
|
397
|
|
|
} |
|
398
|
|
|
return dirty; |
|
399
|
|
|
}, |
|
400
|
|
|
|
|
401
|
|
|
resetDirty: function() { |
|
402
|
|
|
for(var field_name in this.widgets) |
|
403
|
|
|
{ |
|
404
|
|
|
if(this.widgets[field_name].resetDirty) |
|
405
|
|
|
{ |
|
406
|
|
|
this.widgets[field_name].resetDirty(); |
|
407
|
|
|
} |
|
408
|
|
|
} |
|
409
|
|
|
}, |
|
410
|
|
|
|
|
411
|
|
|
isValid: function() { |
|
412
|
|
|
// Individual customfields will handle themselves |
|
413
|
|
|
return true; |
|
414
|
|
|
}, |
|
415
|
|
|
|
|
416
|
|
|
/** |
|
417
|
|
|
* Adapt provided attributes to match options for widget |
|
418
|
|
|
* |
|
419
|
|
|
* rows > 1 --> textarea, with rows=rows and cols=len |
|
420
|
|
|
* !rows --> input, with size=len |
|
421
|
|
|
* rows = 1 --> input, with size=len, maxlength=len |
|
422
|
|
|
*/ |
|
423
|
|
|
_setup_text: function(field_name, field, attrs) { |
|
424
|
|
|
// No label on the widget itself |
|
425
|
|
|
delete(attrs.label); |
|
426
|
|
|
|
|
427
|
|
|
field.type = 'textbox'; |
|
428
|
|
|
attrs.rows = field.rows > 1 ? field.rows : null; |
|
429
|
|
|
|
|
430
|
|
|
if(field.len) |
|
431
|
|
|
{ |
|
432
|
|
|
attrs.size = field.len; |
|
433
|
|
|
if (field.rows == 1) attrs.maxlength = field.len; |
|
434
|
|
|
} |
|
435
|
|
|
return true; |
|
436
|
|
|
}, |
|
437
|
|
|
_setup_ajax_select: function(field_name, field, attrs) { |
|
438
|
|
|
var attributes = ['get_rows','get_title','id_field','template']; |
|
439
|
|
|
if(field.values) |
|
440
|
|
|
{ |
|
441
|
|
|
for(var i = 0; i < attributes.length; i++) |
|
442
|
|
|
{ |
|
443
|
|
|
if(typeof field.values[attributes[i]] !== 'undefined') |
|
444
|
|
|
{ |
|
445
|
|
|
attrs[attributes[i]] = field.values[attributes[i]]; |
|
446
|
|
|
} |
|
447
|
|
|
} |
|
448
|
|
|
} |
|
449
|
|
|
return true; |
|
450
|
|
|
}, |
|
451
|
|
|
_setup_float: function(field_name, field, attrs) { |
|
452
|
|
|
// No label on the widget itself |
|
453
|
|
|
delete(attrs.label); |
|
454
|
|
|
|
|
455
|
|
|
field.type = 'float'; |
|
456
|
|
|
|
|
457
|
|
|
if(field.len) |
|
458
|
|
|
{ |
|
459
|
|
|
attrs.size = field.len; |
|
460
|
|
|
} |
|
461
|
|
|
return true; |
|
462
|
|
|
}, |
|
463
|
|
|
_setup_select: function(field_name, field, attrs) { |
|
464
|
|
|
// No label on the widget itself |
|
465
|
|
|
delete(attrs.label); |
|
466
|
|
|
|
|
467
|
|
|
attrs.rows = field.rows; |
|
468
|
|
|
// select_options are now send from server-side incl. ones defined via a file in EGroupware root |
|
469
|
|
|
attrs.tags = field.tags; |
|
470
|
|
|
|
|
471
|
|
|
return true; |
|
472
|
|
|
}, |
|
473
|
|
|
_setup_select_account: function(field_name, field, attrs) { |
|
474
|
|
|
attrs.empty_label = egw.lang('Select'); |
|
475
|
|
|
return this._setup_select(field_name, field, attrs); |
|
476
|
|
|
}, |
|
477
|
|
|
|
|
478
|
|
|
_setup_date: function(field_name, field, attrs) { |
|
479
|
|
|
attrs.data_format = 'Y-m-d'; |
|
480
|
|
|
return true; |
|
481
|
|
|
}, |
|
482
|
|
|
_setup_date_time: function(field_name, field, attrs) { |
|
483
|
|
|
attrs.data_format = 'Y-m-d H:i:s'; |
|
484
|
|
|
return true; |
|
485
|
|
|
}, |
|
486
|
|
|
_setup_htmlarea: function(field_name, field, attrs) { |
|
487
|
|
|
attrs.config = field.config ? field.config : {}; |
|
488
|
|
|
attrs.config.toolbarStartupExpanded = false; |
|
489
|
|
|
if(field.len) |
|
490
|
|
|
{ |
|
491
|
|
|
attrs.config.width = field.len+'px'; |
|
492
|
|
|
} |
|
493
|
|
|
attrs.config.height = (((field.rows > 0 && field.rows !='undefined') ? field.rows : 5) *16) +'px'; |
|
494
|
|
|
|
|
495
|
|
|
// We have to push the config modifications into the modifications array, or they'll |
|
496
|
|
|
// be overwritten by the site config from the server |
|
497
|
|
|
var data = this.getArrayMgr("modifications").getEntry(this.prefix+field_name); |
|
498
|
|
|
if(data) jQuery.extend(data.config, attrs.config); |
|
499
|
|
|
|
|
500
|
|
|
return true; |
|
501
|
|
|
}, |
|
502
|
|
|
_setup_radio: function(field_name, field, attrs) { |
|
503
|
|
|
// 'Empty' label will be first |
|
504
|
|
|
delete(attrs.label); |
|
505
|
|
|
|
|
506
|
|
|
if(field.values && field.values['']) |
|
507
|
|
|
{ |
|
508
|
|
|
attrs.label = field.values['']; |
|
509
|
|
|
delete field.values['']; |
|
510
|
|
|
} |
|
511
|
|
|
|
|
512
|
|
|
field.type = 'radiogroup'; |
|
513
|
|
|
attrs.options = field.values; |
|
514
|
|
|
return true; |
|
515
|
|
|
}, |
|
516
|
|
|
|
|
517
|
|
|
_setup_checkbox: function(field_name, field, attrs) { |
|
518
|
|
|
// Read-only checkbox is just text |
|
519
|
|
|
if(attrs.readonly) |
|
520
|
|
|
{ |
|
521
|
|
|
attrs.ro_true = field.label; |
|
522
|
|
|
} |
|
523
|
|
|
return true; |
|
524
|
|
|
}, |
|
525
|
|
|
|
|
526
|
|
|
/** |
|
527
|
|
|
* People set button attributes as |
|
528
|
|
|
* label: javascript |
|
529
|
|
|
*/ |
|
530
|
|
|
_setup_button: function(field_name, field, attrs) { |
|
531
|
|
|
// No label on the widget itself |
|
532
|
|
|
delete(attrs.label); |
|
533
|
|
|
|
|
534
|
|
|
attrs.label = field.label; |
|
535
|
|
|
|
|
536
|
|
|
if (this._type == 'customfields-list') |
|
537
|
|
|
{ |
|
538
|
|
|
// No buttons in a list, it causes problems with detached nodes |
|
539
|
|
|
return false; |
|
540
|
|
|
} |
|
541
|
|
|
// Simple case, one widget for a custom field |
|
542
|
|
|
if(!field.values || typeof field.values != 'object' || Object.keys(field.values).length == 1) |
|
543
|
|
|
{ |
|
544
|
|
|
for(var key in field.values) |
|
545
|
|
|
{ |
|
546
|
|
|
attrs.label = key; |
|
547
|
|
|
attrs.onclick = field.values[key]; |
|
548
|
|
|
} |
|
549
|
|
|
if (!attrs.label) |
|
550
|
|
|
{ |
|
551
|
|
|
attrs.label = 'No "label=onclick" in values!'; |
|
552
|
|
|
attrs.onclick = function(){ return false; }; |
|
553
|
|
|
} |
|
554
|
|
|
return !attrs.readonly; |
|
555
|
|
|
} |
|
556
|
|
|
else |
|
557
|
|
|
{ |
|
558
|
|
|
// Complicated case, a single custom field you get multiple widgets |
|
559
|
|
|
// Handle it all here, since this is the exception |
|
560
|
|
|
var row = jQuery('tr',this.tbody).last(); |
|
561
|
|
|
var cf = jQuery('td',row); |
|
562
|
|
|
// Label in first column, widget in 2nd |
|
563
|
|
|
cf.text(field.label + ""); |
|
564
|
|
|
cf = jQuery(document.createElement("td")) |
|
565
|
|
|
.appendTo(row); |
|
566
|
|
|
|
|
567
|
|
|
for(var key in field.values) |
|
568
|
|
|
{ |
|
569
|
|
|
var button_attrs = jQuery.extend({},attrs); |
|
570
|
|
|
button_attrs.label = key; |
|
571
|
|
|
button_attrs.onclick = field.values[key]; |
|
572
|
|
|
button_attrs.id = attrs.id + '_' + key; |
|
573
|
|
|
|
|
574
|
|
|
// This controls where the button is placed in the DOM |
|
575
|
|
|
this.rows[button_attrs.id] = cf[0]; |
|
576
|
|
|
|
|
577
|
|
|
// Do not store in the widgets list, one name for multiple widgets would cause problems |
|
578
|
|
|
/*this.widgets[field_name] = */ et2_createWidget(attrs.type ? attrs.type : field.type, button_attrs, this); |
|
579
|
|
|
} |
|
580
|
|
|
return false; |
|
581
|
|
|
} |
|
582
|
|
|
}, |
|
583
|
|
|
_setup_link_entry: function(field_name, field, attrs) { |
|
584
|
|
|
if(field.type === 'filemanager') |
|
585
|
|
|
{ |
|
586
|
|
|
return this._setup_filemanager(field_name, field, attrs); |
|
587
|
|
|
} |
|
588
|
|
|
// No label on the widget itself |
|
589
|
|
|
delete(attrs.label); |
|
590
|
|
|
|
|
591
|
|
|
attrs.type = "link-entry"; |
|
592
|
|
|
attrs.only_app = field.type; |
|
593
|
|
|
return true; |
|
594
|
|
|
}, |
|
595
|
|
|
|
|
596
|
|
|
_setup_filemanager: function(field_name, field, attrs) { |
|
597
|
|
|
attrs.type = 'vfs-upload'; |
|
598
|
|
|
delete(attrs.label); |
|
599
|
|
|
|
|
600
|
|
|
if (this._type == 'customfields-list') |
|
601
|
|
|
{ |
|
602
|
|
|
// No special UI needed? |
|
603
|
|
|
return true; |
|
604
|
|
|
} |
|
605
|
|
|
else |
|
606
|
|
|
{ |
|
607
|
|
|
// Complicated case, a single custom field you get multiple widgets |
|
608
|
|
|
// Handle it all here, since this is the exception |
|
609
|
|
|
var row = jQuery('tr',this.tbody).last(); |
|
610
|
|
|
var cf = jQuery('td',row); |
|
611
|
|
|
|
|
612
|
|
|
// Label in first column, widget in 2nd |
|
613
|
|
|
cf.text(field.label + ""); |
|
614
|
|
|
cf = jQuery(document.createElement("td")) |
|
615
|
|
|
.appendTo(row); |
|
616
|
|
|
|
|
617
|
|
|
// Create upload widget |
|
618
|
|
|
var widget = this.widgets[field_name] = et2_createWidget(attrs.type ? attrs.type : field.type, attrs, this); |
|
619
|
|
|
|
|
620
|
|
|
// This controls where the widget is placed in the DOM |
|
621
|
|
|
this.rows[attrs.id] = cf[0]; |
|
622
|
|
|
jQuery(widget.getDOMNode(widget)).css('vertical-align','top'); |
|
623
|
|
|
|
|
624
|
|
|
// Add a link to existing VFS file |
|
625
|
|
|
var select_attrs = jQuery.extend({}, |
|
626
|
|
|
attrs, |
|
627
|
|
|
// Filemanager select |
|
628
|
|
|
{ |
|
629
|
|
|
label: '', |
|
630
|
|
|
method: 'EGroupware\\Api\\Etemplate\\Widget\\Link::link_existing', |
|
631
|
|
|
method_id: attrs.path, |
|
632
|
|
|
button_label: egw.lang('Link') |
|
633
|
|
|
},{type: 'vfs-select'}); |
|
634
|
|
|
select_attrs.id = attrs.id + '_vfs_select'; |
|
635
|
|
|
|
|
636
|
|
|
// This controls where the button is placed in the DOM |
|
637
|
|
|
this.rows[select_attrs.id] = cf[0]; |
|
638
|
|
|
|
|
639
|
|
|
// Do not store in the widgets list, one name for multiple widgets would cause problems |
|
640
|
|
|
widget = et2_createWidget(select_attrs.type, select_attrs, this); |
|
641
|
|
|
jQuery(widget.getDOMNode(widget)).css('vertical-align','top').prependTo(cf); |
|
642
|
|
|
} |
|
643
|
|
|
return false; |
|
644
|
|
|
}, |
|
645
|
|
|
|
|
646
|
|
|
/** |
|
647
|
|
|
* Set which fields are visible, by name |
|
648
|
|
|
* |
|
649
|
|
|
* Note: no # prefix on the name |
|
650
|
|
|
* |
|
651
|
|
|
*/ |
|
652
|
|
|
set_visible: function(_fields) { |
|
653
|
|
|
for(var name in _fields) |
|
654
|
|
|
{ |
|
655
|
|
|
if(this.rows[this.prefix + name]) |
|
656
|
|
|
{ |
|
657
|
|
|
if(_fields[name]) |
|
658
|
|
|
{ |
|
659
|
|
|
jQuery(this.rows[this.prefix+name]).show(); |
|
660
|
|
|
} |
|
661
|
|
|
else |
|
662
|
|
|
{ |
|
663
|
|
|
jQuery(this.rows[this.prefix+name]).hide(); |
|
664
|
|
|
} |
|
665
|
|
|
} |
|
666
|
|
|
this.options.fields[name] = _fields[name]; |
|
667
|
|
|
} |
|
668
|
|
|
}, |
|
669
|
|
|
|
|
670
|
|
|
/** |
|
671
|
|
|
* Code for implementing et2_IDetachedDOM |
|
672
|
|
|
*/ |
|
673
|
|
|
getDetachedAttributes: function(_attrs) |
|
674
|
|
|
{ |
|
675
|
|
|
_attrs.push("value", "class"); |
|
676
|
|
|
}, |
|
677
|
|
|
|
|
678
|
|
|
getDetachedNodes: function() |
|
679
|
|
|
{ |
|
680
|
|
|
return this.detachedNodes ? this.detachedNodes : []; |
|
681
|
|
|
}, |
|
682
|
|
|
|
|
683
|
|
|
setDetachedAttributes: function(_nodes, _values) |
|
684
|
|
|
{ |
|
685
|
|
|
// Individual widgets are detected and handled by the grid, but the interface is needed for this to happen |
|
686
|
|
|
|
|
687
|
|
|
// Show the row if there's a value, hide it if there is no value |
|
688
|
|
|
for(var i = 0; i < _nodes.length; i++) |
|
689
|
|
|
{ |
|
690
|
|
|
// toggle() needs a boolean to do what we want |
|
691
|
|
|
var key = _nodes[i].getAttribute('data-field'); |
|
692
|
|
|
jQuery(_nodes[i]).toggle(_values.fields[key] && _values.value[this.prefix + key]?true:false); |
|
693
|
|
|
} |
|
694
|
|
|
} |
|
695
|
|
|
});}).call(this); |
|
696
|
|
|
|
|
697
|
|
|
et2_register_widget(et2_customfields_list, ["customfields", "customfields-list"]); |
|
698
|
|
|
|
|
699
|
|
|
|