Completed
Push — 16.1 ( 1e4888...9df24e )
by Ralf
12:06
created

et2_widget_vfs.js ➔ ... ➔ et2_file.extend.getValue   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
/**
2
 * EGroupware eTemplate2 - JS VFS widgets
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 2012
10
 * @version $Id$
11
*/
12
13
/*egw:uses
14
	/vendor/bower-asset/jquery/dist/jquery.js;
15
	et2_core_inputWidget;
16
	et2_core_valueWidget;
17
	et2_widget_description;
18
	et2_widget_file;
19
	expose;
20
*/
21
22
/**
23
 * Class which implements the "vfs" XET-Tag
24
 *
25
 * @augments et2_valueWidget
26
 */
27
var et2_vfs = (function(){ "use strict"; return et2_valueWidget.extend([et2_IDetachedDOM],
28
{
29
	attributes: {
30
		"value": {
31
			"type": "any", // Object
32
			"description": "Array of (stat) information about the file"
33
		}
34
	},
35
36
	/**
37
	 * Mime type of directories
38
	 */
39
	DIR_MIME_TYPE: 'httpd/unix-directory',
40
41
	/**
42
	 * Constructor
43
	 *
44
	 * @memberOf et2_vfs
45
	 */
46
	init: function() {
47
		this._super.apply(this, arguments);
48
49
		this.value = "";
50
		this.span = jQuery(document.createElement("ul"))
51
			.addClass('et2_vfs');
52
53
		this.setDOMNode(this.span[0]);
54
	},
55
56
	getValue: function() {
57
		return this.value;
58
	},
59
60
	set_value: function(_value) {
61
		if (typeof _value !== 'object')
62
		{
63
			// Only warn if it's an actual value, just blank for falsy values
64
			if(_value)
65
			{
66
				this.egw().debug("warn", "%s only has path, needs full array", this.id, _value);
67
			}
68
			this.span.empty().text(_value);
69
			return;
70
		}
71
		this.span.empty();
72
		this.value = _value;
73
		var path = _value.path ? _value.path : '/';
74
		// calculate path as parent of name, which can contain slashes
75
		// eg. _value.path=/home/ralf/sub/file, _value.name=sub/file --> path=/home/ralf
76
		// --> generate clickable fields for sub/ + file
77
		if(_value.path.indexOf(_value.name) >= 0)
78
		{
79
			path = path.substr(0, _value.path.length-_value.name.length-1);
80
			var path_offset = path.split('/').length;
81
			var path_parts = _value.path.split('/');
82
		}
83
		else
84
		{
85
			var path_offset = 0;
86
			var path_parts = [_value.name];
87
		}
88
89
		var text;
90
		for(var i = path_offset; i < path_parts.length; i++)
91
		{
92
			path += (path=='/'?'':'/')+path_parts[i];
93
			text = egw.decodePath(path_parts[i]);
94
95
			// Nice human-readable stuff for apps
96
			if(path_parts[1] == 'apps')
97
			{
98
				switch(path_parts.length)
0 ignored issues
show
Coding Style introduced by
As per coding-style, switch statements should have a default case.
Loading history...
99
				{
100
					case 2:
101
						if(i == 1)
102
						{
103
							text = this.egw().lang('applications');
104
						}
105
						break;
106
					case 3:
107
						if( i == 2)
108
						{
109
							text = this.egw().lang(path_parts[2]);
110
						}
111
						break;
112
					case 4:
113
						if(!isNaN(text))
114
						{
115
							var link_title = this.egw().link_title(path_parts[2],path_parts[3],
116
								function(title) {
117
									if(!title || this.value.name == title) return;
118
									jQuery('li',this.span).last().text(title);
119
								}, this
120
							);
121
							if(link_title && typeof link_title !== 'undefined') text = link_title;
122
						}
123
						break;
124
				}
125
			}
126
			var self = this;
127
			var data = {path: path, type: i < path_parts.length-1 ? this.DIR_MIME_TYPE : _value.mime };
128
			jQuery(document.createElement("li"))
129
				.addClass("vfsFilename")
130
				.text(text + (i < path_parts.length-1 ? '/' : ''))
131
				//.attr('title', egw.decodePath(path))
132
				.addClass("et2_clickable et2_link")
133
				.click({data:data, egw: this.egw()}, function(e) {
134
					if(!self.onclick) {
0 ignored issues
show
Bug introduced by
The variable self is changed as part of the for loop for example by this on line 126. Only the value of the last iteration will be visible in this function if it is called after the loop.
Loading history...
135
						e.data.egw.open(e.data.data, "file");
136
					}
137
					else if (self.click(e))
138
					{
139
						e.data.egw.open(e.data.data, "file");
140
					}
141
				})
142
				.appendTo(this.span);
143
		}
144
	},
145
146
	/**
147
	 * Code for implementing et2_IDetachedDOM (data grid)
148
	 *
149
	 * @param {array} _attrs array of attribute-names to push further names onto
150
	 */
151
	getDetachedAttributes: function(_attrs)
152
	{
153
		_attrs.push("value");
154
	},
155
156
	getDetachedNodes: function()
157
	{
158
		return [this.span[0]];
159
	},
160
161
	setDetachedAttributes: function(_nodes, _values)
162
	{
163
		this.span = jQuery(_nodes[0]);
164
		if(typeof _values["value"] != 'undefined')
165
		{
166
			this.set_value(_values["value"]);
167
		}
168
	}
169
170
171
});}).call(this);
172
et2_register_widget(et2_vfs, ["vfs"]);
173
174
/**
175
 * vfs-name
176
 * filename automatically urlencoded on return (urldecoded on display to user)
177
 *
178
 * @augments et2_textbox
179
 */
180
var et2_vfsName = (function(){ "use strict"; return et2_textbox.extend(
181
{
182
	/**
183
	 * Constructor
184
	 *
185
	 * @memberOf et2_vfsName
186
	 */
187
	init: function() {
188
		this._super.apply(this, arguments);
189
		this.input.addClass("et2_vfs");
190
	},
191
	set_value: function(_value) {
192
		if(_value.path)
193
		{
194
			_value = _value.path;
195
		}
196
		_value = egw.decodePath(_value);
197
		this._super.apply(this,[_value]);
198
	},
199
	getValue: function() {
200
		return egw.encodePath(this._super.apply(this)||'');
201
	}
202
});}).call(this);
203
et2_register_widget(et2_vfsName, ["vfs-name"]);
204
205
/**
206
 * vfs-name
207
 * filename automatically urlencoded on return (urldecoded on display to user)
208
 *
209
 * @augments et2_textbox_ro
210
 */
211
var et2_vfsName_ro = (function(){ "use strict"; return et2_textbox_ro.extend(
212
{
213
	/**
214
	 * Constructor
215
	 *
216
	 * @memberOf et2_vfsName_ro
217
	 */
218
	init: function() {
219
		this._super.apply(this, arguments);
220
	},
221
	set_value: function(_value) {
222
		if(_value.path)
223
		{
224
			_value = _value.path;
225
		}
226
		_value = egw.decodePath(_value);
227
		this._super.apply(this,[_value]);
228
	},
229
	getValue: function() {
230
		return egw.encodePath(this._super.apply(this));
231
	}
232
});}).call(this);
233
et2_register_widget(et2_vfsName_ro, ["vfs-name_ro"]);
234
235
/**
236
 * vfs-mime: icon for mimetype of file, or thumbnail
237
 * incl. optional link overlay icon, if file is a symlink
238
 *
239
 * Creates following structure
240
 * <span class="iconOverlayContainer">
241
 *   <img class="et2_vfs vfsMimeIcon" src="..."/>
242
 *   <span class="overlayContainer">
243
 *      <img class="overlay" src="etemplate/templates/default/images/link.png"/>
244
 *   </span>
245
 * </span>
246
 *
247
 * span.overlayContainer is optional and only generated for symlinks
248
 *
249
 * @augments et2_valueWidget
250
 */
251
var et2_vfsMime = (function(){ "use strict"; return expose(et2_valueWidget.extend([et2_IDetachedDOM],
252
{
253
	attributes: {
254
		"value": {
255
			"type": "any", // Object
256
			"description": "Array of (stat) information about the file"
257
		},
258
		"size": {
259
			"name": "Icon size",
260
			"type": "integer",
261
			"description": "Size of icon / thumbnail, in pixels",
262
			"default": et2_no_init
263
		},
264
		"expose_callback":{
265
			"name": "expose_callback",
266
			"type": "js",
267
			"default": et2_no_init,
268
			"description": "JS code which is executed when expose slides."
269
		},
270
		expose_view: {
271
			name: "Expose view",
272
			type: "boolean",
273
			default: true,
274
			description: "Clicking on an image would popup an expose view"
275
		},
276
		thumb_mime_size:{
277
			name: "Image thumbnail size",
278
			type: "string",
279
			default:"",
280
			description:" Size of thumbnail in pixel for specified mime type with syntax of: mime_type(s),size (eg. image,video,128)"
281
		}
282
283
	},
284
285
	legacyOptions:["size"],
286
287
	/**
288
	 * Constructor
289
	 *
290
	 * @memberOf et2_vfsMime
291
	 */
292
	init: function() {
293
		this._super.apply(this, arguments);
294
		this.iconOverlayContainer = jQuery(document.createElement('span')).addClass('iconOverlayContainer');
295
		this.image = jQuery(document.createElement("img"));
296
		this.image.addClass("et2_vfs vfsMimeIcon");
297
		this.iconOverlayContainer.append(this.image);
298
		this.setDOMNode(this.iconOverlayContainer[0]);
299
	},
300
301
	/**
302
	 * Handler for expose slide action, from expose
303
	 * Returns data needed for the given index, or false to let expose handle it
304
	 *
305
	 * @param {Gallery} gallery
306
	 * @param {integer} index
307
	 * @param {DOMNode} slide
308
	 * @return {Array} array of objects consist of media contnet
309
	 */
310
	expose_onslide: function(gallery, index, slide)
311
	{
312
		var content = false;
313
		if (this.options.expose_callback && typeof this.options.expose_callback == 'function')
314
		{
315
			//Call the callback to load more items
316
			content = this.options.expose_callback.call(this,[gallery, index]);
317
			if (content) this.add(content);
318
		}
319
		return content;
320
	},
321
322
	/**
323
	 * Function to get media content to feed the expose
324
	 *
325
	 * @param {type} _value
326
	 * @returns {Array} return an array of object consists of media content
327
	 */
328
	getMedia: function (_value)
329
	{
330
		var base_url = egw.webserverUrl.match(/^\//,'ig')?egw(window).window.location.origin + egw.webserverUrl:egw.webserverUrl;
331
		var mediaContent = [{
332
			title: _value.name,
333
			type: _value.mime,
334
			href: _value.download_url
335
		}];
336
		// check if download_url is not already an url (some stream-wrappers allow to specify that!)
337
		if (_value.download_url && (_value.download_url[0] == '/' || _value.download_url.substr(0, 4) != 'http'))
338
		{
339
			mediaContent[0].href = base_url + _value.download_url;
340
341
			if (mediaContent[0].href && mediaContent[0].href.match(/\/webdav.php/,'ig'))
342
			{
343
				mediaContent[0].download_href = mediaContent[0].href + '?download';
344
			}
345
		}
346
		if (_value && _value.mime && _value.mime.match(/video\//,'ig'))
347
		{
348
			mediaContent[0].thumbnail = this.egw().mime_icon(_value.mime, _value.path, undefined, _value.mtime);
349
		}
350
		else
351
		{
352
			mediaContent[0].thumbnail = _value.path && _value.mime ?
353
				this.egw().mime_icon(_value.mime, _value.path, undefined, _value.mtime) :
354
				this.image.attr('src')+ '&thheight=128';
355
		}
356
		return mediaContent;
357
	},
358
359
	set_value: function(_value) {
360
		if (typeof _value !== 'object')
361
		{
362
			this.egw().debug("warn", "%s only has path, needs array with path & mime", this.id, _value);
363
			// Keep going, will be 'unknown type'
364
		}
365
		var src = this.egw().mime_icon(_value.mime, _value.path, undefined, _value.mtime);
366
		if(src)
367
		{
368
			// Set size of thumbnail
369
			if(src.indexOf("thumbnail.php") > -1)
370
			{
371
				if(this.options.size)
372
				{
373
					src += "&thsize="+this.options.size;
374
				}
375
				else if (this.options.thumb_mime_size)
376
				{
377
					var mime_size = this.options.thumb_mime_size.split(',');
378
					var mime_regex = RegExp(_value.mime.split('/')[0]);
379
					if (typeof mime_size != 'undefined' && jQuery.isArray(mime_size)
380
							&& !isNaN(mime_size[mime_size.length-1]) && isNaN(mime_size[0]) && this.options.thumb_mime_size.match(mime_regex[0], 'ig'))
381
					{
382
						src += "&thsize=" + mime_size[mime_size.length-1];
383
					}
384
				}
385
				this.image.css("max-width", "100%");
386
			}
387
			this.image.attr("src", src);
388
		}
389
		// add/remove link icon, if file is (not) a symlink
390
		if ((_value.mode & et2_vfsMode.prototype.types.l) == et2_vfsMode.prototype.types.l)
391
		{
392
			if (typeof this.overlayContainer == 'undefined')
393
			{
394
395
				this.overlayContainer = jQuery(document.createElement('span')).addClass('overlayContainer');
396
				this.overlayContainer.append(jQuery(document.createElement('img'))
397
					.addClass('overlay').attr('src', this.egw().image('link', 'etemplate')));
398
				this.iconOverlayContainer.append(this.overlayContainer);
399
			}
400
		}
401
		else if (typeof this.overlayContainer != 'undefined')
402
		{
403
			this.overlayContainer.remove();
404
			delete this.overlayContainer;
405
		}
406
	},
407
	/**
408
	 * Implementation of "et2_IDetachedDOM" for fast viewing in gridview
409
	 * Override to add needed attributes
410
	 *
411
	 * @param {array} _attrs array of attribute-names to push further names onto
412
	 */
413
	getDetachedAttributes: function(_attrs) {
414
		_attrs.push("value", "class");
415
	},
416
	getDetachedNodes: function() {
417
		return [this.node, this.iconOverlayContainer[0], this.image[0]];
418
	},
419
420
	setDetachedAttributes: function(_nodes, _values) {
421
		this.iconOverlayContainer = jQuery(_nodes[1]);
422
		this.image = jQuery(_nodes[2]);
423
		this.node = _nodes[0];
424
		this.overlayContainer = _nodes[0].children[1];
425
		if(typeof _values['class'] != "undefined") {
426
			this.image.addClass(_values['class']);
427
		}
428
		if(typeof _values['value'] != "undefined") {
429
			this.set_value(_values['value']);
430
		}
431
	}
432
}));}).call(this);
433
et2_register_widget(et2_vfsMime, ["vfs-mime"]);
434
435
/**
436
 * vfs-size
437
 * Human readable file sizes
438
 *
439
 * @augments et2_description
440
 */
441
var et2_vfsSize = (function(){ "use strict"; return et2_description.extend({
442
	attributes: {
443
		"value": {
444
			"type": "integer"
445
		}
446
	},
447
	/**
448
	 * Constructor
449
	 *
450
	 * @memberOf et2_vfsSize
451
	 */
452
	init: function() {
453
		this._super.apply(this, arguments);
454
		this.span.addClass("et2_vfs");
455
	},
456
	human_size: function(size) {
457
		if(typeof size !== "number")
458
		{
459
			size = parseInt(size);
460
		}
461
		if(!size)
462
		{
463
			size = 0;
464
		}
465
		var units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
466
		var i = 0;
467
		while(size >= 1024)
468
		{
469
			size /= 1024;
470
			++i;
471
		}
472
		return size.toFixed(i == 0 ? 0 : 1) + ' ' + units[i];
473
	},
474
	set_value: function(_value) {
475
		if(_value.size)
476
		{
477
			_value = _value.size;
478
		}
479
		jQuery(this.node).text(this.human_size(_value));
480
	},
481
	setDetachedAttributes: function(_nodes, _values)
482
	{
483
		if(typeof _values["value"] !== "undefined") {
484
			this.node = _nodes[0];
485
			this.set_value(_values["value"]);
486
			delete _values["value"];
487
		}
488
		this._super.apply(this, arguments);
489
	}
490
});}).call(this);
491
et2_register_widget(et2_vfsSize, ["vfs-size"]);
492
493
494
/**
495
 * vfs-mode: textual representation of permissions + extra bits
496
 *
497
 * @augments et2_description
498
 */
499
var et2_vfsMode = (function(){ "use strict"; return et2_description.extend({
500
	// Masks for file types
501
	types: {
502
		'l': 0xA000, // link
503
		's': 0xC000, // Socket
504
		'p': 0x1000, // FIFO pipe
505
		'c': 0x2000, // Character special
506
		'd': 0x4000, // Directory
507
		'b': 0x6000, // Block special
508
		'-': 0x8000 // Regular
509
	},
510
	// Sticky / UID / GID
511
	sticky: [
512
		{ mask: 0x200, "char": "T", position: 9 }, // Sticky
513
		{ mask: 0x400, "char": "S", position: 6 }, // sGID
514
		{ mask: 0x800, "char": "S", position: 3 } // SUID
515
	],
516
	perms: {
517
		'x': 0x1, // Execute
518
		'w': 0x2, // Write
519
		'r': 0x4  // Read
520
	},
521
522
	/**
523
	 * Constructor
524
	 *
525
	 * @memberOf et2_vfsMode
526
	 */
527
	init: function() {
528
		this._super.apply(this, arguments);
529
		this.span.addClass("et2_vfs");
530
	},
531
532
	/**
533
	 * Get text for file stuff
534
	 * Result will be like -rwxr--r--.  First char is type, then read, write, execute (or other bits) for
535
	 * user, group, world
536
	 *
537
	 * @param {number} _value vfs mode
538
	 */
539
	text_mode: function(_value) {
540
		var text = [];
541
		if(typeof _value != "number")
542
		{
543
			_value = parseInt(_value);
544
		}
545
		if(!_value) return "----------";
546
547
		// Figure out type
548
		var type = 'u'; // unknown
549
		for(var flag in this.types)
550
		{
551
			if((_value & this.types[flag]) == this.types[flag])
552
			{
553
				type = flag;
554
				break;
555
			}
556
		}
557
558
		// World, group, user - build string backwards
559
		for(var i = 0; i < 3; i++)
560
		{
561
			for(var perm in this.perms)
562
			{
563
				if(_value & this.perms[perm])
0 ignored issues
show
introduced by
You have used a bitwise operator & in a condition. Did you maybe want to use the logical operator &&
Loading history...
564
				{
565
					text.unshift(perm);
566
				}
567
				else
568
				{
569
					text.unshift("-");
570
				}
571
			}
572
			_value = _value >> 3;
573
		}
574
		// Sticky / UID / GID
575
		for(var i = 0; i < this.sticky.length; i++)
576
		{
577
			if(this.sticky[i].mask & _value)
0 ignored issues
show
introduced by
You have used a bitwise operator & in a condition. Did you maybe want to use the logical operator &&
Loading history...
578
			{
579
				current = text[this.sticky[i].position];
580
				text[this.sticky[i].position] = this.sticky[i]["char"];
581
				if(current == 'x') text[this.sticky[i].position].toLowerCase();
582
			}
583
		}
584
		return type + text.join('');
585
	},
586
	set_value: function(_value) {
587
		if(_value.size)
588
		{
589
			_value = _value.size;
590
		}
591
		var text = this.text_mode(_value);
592
		jQuery(this.node).text(text);
593
	},
594
595
	setDetachedAttributes: function(_nodes, _values)
596
	{
597
		if(typeof _values["value"] !== "undefined") {
598
			this.node = _nodes[0];
599
			this.set_value(_values["value"]);
600
			delete _values["value"];
601
		}
602
		this._super.apply(this, arguments);
603
	}
604
});}).call(this);
605
et2_register_widget(et2_vfsMode, ["vfs-mode"]);
606
607
608
/**
609
 * vfs-uid / vfs-gid: Displays the name for an ID.
610
 * Same as read-only selectAccount, except if there's no user it shows "root"
611
 *
612
 * @augments et2_selectAccount_ro
613
 */
614
var et2_vfsUid = (function(){ "use strict"; return et2_selectAccount_ro.extend(
615
{
616
	/**
617
	 * @memberOf et2_vfsUid
618
	 * @param _node
619
	 * @param _value
620
	 */
621
	set_title: function(_node, _value) {
622
		if(_value == "")
623
		{
624
			arguments[1] = "root";
625
		}
626
		this._super.apply(this, arguments);
627
	}
628
});}).call(this);
629
et2_register_widget(et2_vfsUid, ["vfs-uid","vfs-gid"]);
630
631
632
/* vfs-upload aka VFS file:       displays either download and delete (x) links or a file upload
633
 *   + value is either a vfs path or colon separated $app:$id:$relative_path, eg: infolog:123:special/offer
634
 *   + if empty($id) / new entry, file is created in a hidden temporary directory in users home directory
635
 *     and calling app is responsible to move content of that dir to entry directory, after entry is saved
636
 *   + option: required mimetype or regular expression for mimetype to match, eg. '/^text\//i' for all text files
637
 *   + if path ends in a slash, multiple files can be uploaded, their original filename is kept then
638
 *
639
 * @augments et2_file
640
 */
641
var et2_vfsUpload = (function(){ "use strict"; return et2_file.extend(
642
{
643
	attributes: {
644
		"value": {
645
			"type": "any"	// Either nothing, or an object with file info
646
		},
647
		"path": {
648
			"name": "Path",
649
			"description": "Upload files to the specified VFS path",
650
			"type": "string",
651
			"default": ''
652
		}
653
	},
654
	legacyOptions: ["mime"],
655
656
	asyncOptions: {
657
		target: egw.ajaxUrl("EGroupware\\Api\\Etemplate\\Widget\\Vfs::ajax_upload")
658
	},
659
660
	/**
661
	 * Constructor
662
	 *
663
	 * @param _parent
664
	 * @param attrs
665
	 * @memberof et2_vfsUpload
666
	 */
667
	init: function(_parent, attrs) {
668
		this._super.apply(this, arguments);
669
		jQuery(this.node).addClass("et2_vfs");
670
671
		if(!this.options.path)
672
		{
673
			this.options.path = this.options.id;
674
		}
675
		// If the path is a directory, allow multiple uploads
676
		if(this.options.path.substr(-1) == '/')
677
		{
678
			this.set_multiple(true);
679
		}
680
		this.list = jQuery(document.createElement('table')).appendTo(this.node);
681
	},
682
683
	/**
684
	 * If there is a file / files in the specified location, display them
685
	 * Value is the information for the file[s] in the specified location.
686
	 *
687
	 * @param {Object[]} _value
688
	 */
689
	set_value: function(_value) {
690
		// Remove previous
691
		while(this._children.length > 0)
692
		{
693
			var node = this._children[this._children.length-1];
694
			this.removeChild(node);
695
			node.free();
696
		}
697
		this.progress.empty();
698
		this.list.empty();
699
700
		// Set new
701
		if(typeof _value == 'object' && _value && _value.length)
702
		{
703
			for(var i = 0; i < _value.length; i++)
704
			{
705
				this._addFile(_value[i]);
706
			}
707
		}
708
	},
709
710
	/**
711
	 * Value is determined by what's at the location specified by path
712
	 *
713
	 * @returns {null}
714
	 */
715
	getValue: function() {
716
		return null;
717
	},
718
719
	getDOMNode: function(sender) {
720
		if(sender !== this && sender._type.indexOf('vfs') >= 0 )
721
		{
722
			var value = sender.getValue && sender.getValue() || sender.options.value || {};
723
			var row =  jQuery('[data-path="'+(value.path)+'"]',this.list);
724
			if(sender._type === 'vfs-mime')
725
			{
726
				return jQuery('.icon',row).get(0) || null;
727
			}
728
			else
729
			{
730
				return jQuery('.title',row).get(0) || null;
731
			}
732
		}
733
		else
734
		{
735
			return this._super.apply(this, arguments);
736
		}
737
	},
738
739
	/**
740
	 * Add in the request id
741
	 *
742
	 * @param {type} form
743
	 */
744
	beforeSend: function(form)
745
	{
746
		var instance = this.getInstanceManager();
747
748
		var extra = this._super.apply(this, arguments);
749
		extra.path = this.options.path;
750
		return extra;
751
	},
752
753
	_addFile: function(file_data) {
754
		var row = jQuery(document.createElement("tr"))
755
			.attr("data-path", file_data.path)
756
			.attr("draggable", "true")
757
			.appendTo(this.list);
758
		var mime = jQuery(document.createElement("td"))
759
			.addClass('icon')
760
			.appendTo(row);
761
762
		var title = jQuery(document.createElement("td"))
763
			.addClass('title')
764
			.appendTo(row);
765
		var mime = et2_createWidget('vfs-mime',{value: file_data},this);
766
		var vfs = et2_createWidget('vfs', {value: file_data}, this);
767
768
		// Add in delete button
769
		if (!this.options.readonly)
770
		{
771
			var self = this;
772
			var delete_button = jQuery(document.createElement("td"))
773
				.appendTo(row);
774
			jQuery("<div />")
775
				.appendTo(delete_button)
776
				// We don't use ui-icon because it assigns a bg image
777
				.addClass("delete icon")
778
				.bind( 'click', function() {
779
					et2_dialog.show_dialog(
780
						function(button) {
781
							if(button == et2_dialog.YES_BUTTON)
782
							{
783
								egw.json("filemanager_ui::ajax_action", [
784
										'delete',
785
										[row.attr('data-path')],
786
										''
787
									],
788
									function(data) {
789
										if(data && data.errs == 0) {row.slideUp(row.remove);}
790
										if(data && data.msg) {
791
											self.egw().message(data.msg, data.errs == 0 ? 'success' : 'error');
792
										}
793
									}
794
								).sendRequest();
795
							}
796
						},
797
						egw.lang('Delete file?')
798
					);
799
				});
800
		}
801
	}
802
});}).call(this);
803
et2_register_widget(et2_vfsUpload, ["vfs-upload"]);
804
805
806
var et2_vfsSelect = (function(){ "use strict"; return et2_inputWidget.extend(
807
{
808
	// Allowed mode options
809
	modes: ['open','open-multiple','saveas','select-dir'],
810
811
	attributes: {
812
		"mode": {
813
			name: "Dialog mode",
814
			type: "string",
815
			description: "One of {open|open-multiple|saveas|select-dir}",
816
			default: "open-multiple"
817
		},
818
		"method": {
819
			name: "Server side callback",
820
			type: "string",
821
			description: "Server side callback to process selected value(s) in app.class.method or class::method format.  The first parameter will be Method ID, the second the file list."
822
		},
823
		"method_id": {
824
			name: "Method ID",
825
			type: "any",
826
			description: "optional parameter passed to server side callback.  Can be a string or a function.",
827
			default: ""
828
		},
829
		"path": {
830
			name: "Path",
831
			type: "string",
832
			description:"Start path in VFS.  Leave unset to use the last used path."
833
		},
834
		"mime": {
835
			name: "Mime type",
836
			type: "string",
837
			description: "Limit display to the given mime-type"
838
		},
839
		"button_label": {
840
			name: "Button label",
841
			description: "Set the label on the dialog's OK button.",
842
			default: "open"
843
		},
844
		"value": {
845
			"type": "any", // Object
846
			"description": "Array of paths (strings)"
847
		},
848
		"button_caption":{
849
			name: "button caption",
850
			type: "string",
851
			default: "Select files from Filemanager ...",
852
			description: "Caption for vfs-select button.",
853
			translate:true
854
		}
855
	},
856
857
	/**
858
	 * Constructor
859
	 *
860
	 * @param _parent
861
	 * @param _attrs
862
	 * @memberOf et2_vfsSelect
863
	 */
864
	init: function(_parent, _attrs) {
865
		// _super.apply is responsible for the actual setting of the params (some magic)
866
		this._super.apply(this, arguments);
867
868
		// Allow no child widgets
869
		this.supportedWidgetClasses = [];
870
871
		this.button = jQuery(document.createElement("button"))
872
			.attr("title", this.egw().lang("Select file(s) from VFS"))
873
			.addClass("et2_button et2_vfs_btn")
874
			.css("background-image","url("+this.egw().image("filemanager/navbar")+")");
875
876
		if (this.options.button_caption != "")
877
		{
878
			this.button.text(this.options.button_caption);
879
		}
880
		this.setDOMNode(egw.app('filemanager') ? this.button[0]:document.createElement('span'));
881
	},
882
883
	click: function(e) {
884
885
	    // No permission
886
	    if(!egw.app('filemanager')) return;
887
888
		var self = this;
889
890
		var attrs = {
891
			menuaction: 'filemanager.filemanager_select.select',
892
			mode: this.options.mode,
893
			method: this.options.method,
894
			label: this.options.button_label,
895
			id: typeof this.options.method_id == "function" ? this.options.method_id.call(): this.options.method_id
896
		};
897
		if(this.options.path)
898
		{
899
			attrs.path = this.options.path;
900
		}
901
		if(this.options.mime)
902
		{
903
			attrs.mime = this.options.mime;
904
		};
905
906
		// Open the filemanager select in a popup
907
		var popup = this.egw(window).open_link(
908
			this.egw().link('/index.php', attrs),
909
			'link_existing',
910
			'680x400'
911
		);
912
		if(popup)
913
		{
914
			// Safari and IE lose reference to global variables after window close
915
			// Try to get updated data before window is closed then later we trigger
916
			// change event on widget
917
			self.egw().window.setTimeout(function(){
918
				jQuery(popup).bind('unload',function(){
919
					// Set selected files to widget
920
					self.value = this.selected_files;
921
922
					// Update path to where the user wound up]
923
					if (typeof this.etemplate2 !='undefined') self.options.path = this.etemplate2.getByApplication('filemanager')[0].widgetContainer.getArrayMgr("content").getEntry('path');
924
				});
925
			},1000);
926
927
			// Update on close doesn't always (ever, in chrome) work, so poll
928
			var poll = self.egw().window.setInterval(
929
				function() {
930
					if(popup.closed) {
931
						self.egw().window.clearInterval(poll);
932
						// Fire a change event so any handlers run
933
						jQuery(self.node).change();
934
					}
935
				},1000
936
			);
937
		}
938
	},
939
940
	/**
941
	 * Set the dialog's mode.
942
	 * Valid options are in et2_vfsSelect.modes
943
	 *
944
	 * @param {string} mode 'open', 'open-multiple', 'saveas' or 'select-dir'
945
	 */
946
	set_mode: function(mode) {
947
		// Check mode
948
		if(jQuery.inArray(mode, this.modes) < 0)
949
		{
950
			this.egw().debug("warn", "Invalid mode for '%s': %s Valid options:", this.id,mode, this.modes);
951
			return;
952
		}
953
		this.options.mode = mode;
954
	},
955
956
	/**
957
	 * Set the label on the dialog's OK button.
958
	 *
959
	 * @param {string} label
960
	 */
961
	set_button_label: function(label)
962
	{
963
		this.options.button_label = label;
964
	},
965
966
	/**
967
	 * Set the caption for vfs-select button
968
	 *
969
	 * @param {string} caption string value as a caption
970
	 */
971
	set_button_caption: function (caption)
972
	{
973
		this.options.button_caption = caption;
974
	},
975
976
	/**
977
	 * Set the ID passed to the server side callback
978
	 *
979
	 * @param {string} id
980
	 */
981
	set_method_id: function(id) {
982
		this.options.method_id = id;
983
	},
984
985
	getValue: function() {
986
		return this.value;
987
	}
988
});}).call(this);
989
et2_register_widget(et2_vfsSelect, ["vfs-select"]);
990