Completed
Push — 16.1 ( fbc1d1...f15ff7 )
by Nathan
30:28 queued 16:35
created

tWidget.extend.set_readonly   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
nc 2
nop 1
dl 0
loc 12
rs 9.4285
c 1
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.replace(/'/g, '&quot'))+"']",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
	/**
754
	 * A file upload is finished, update the UI
755
	 */
756
	finishUpload: function(file, response) {
757
		var result = this._super.apply(this, arguments);
758
759
		if(typeof response == 'string') response = jQuery.parseJSON(response);
760
		if(response.response[0] && typeof response.response[0].data.length == 'undefined') {
761
			for(var key in response.response[0].data) {
762
				var value = response.response[0].data[key];
763
				if(value && value.path)
764
				{
765
					this._addFile(value);
766
					jQuery("[data-file='"+file.fileName.replace(/'/g, '&quot')+"']",this.progress).hide();
767
				}
768
			}
769
		}
770
		return result;
771
	},
772
773
	_addFile: function(file_data) {
774
		if(jQuery("[data-path='"+file_data.path.replace(/'/g, '&quot')+"']").remove().length)
775
		{
776
			for(var child_index = this._children.length-1; child_index >= 0; child_index--)
777
			{
778
				var child = this._children[child_index];
779
				if(child.options.value.path === file_data.path)
780
				{
781
					this.removeChild(child);
782
					child.free();
783
				}
784
			}
785
		}
786
		var row = jQuery(document.createElement("tr"))
787
			.attr("data-path", file_data.path.replace(/'/g, '&quot'))
788
			.attr("draggable", "true")
789
			.appendTo(this.list);
790
		var mime = jQuery(document.createElement("td"))
791
			.addClass('icon')
792
			.appendTo(row);
793
794
		var title = jQuery(document.createElement("td"))
795
			.addClass('title')
796
			.appendTo(row);
797
		var mime = et2_createWidget('vfs-mime',{value: file_data},this);
798
		var vfs = et2_createWidget('vfs', {value: file_data}, this);
799
800
		// Add in delete button
801
		if (!this.options.readonly)
802
		{
803
			var self = this;
804
			var delete_button = jQuery(document.createElement("td"))
805
				.appendTo(row);
806
			jQuery("<div />")
807
				.appendTo(delete_button)
808
				// We don't use ui-icon because it assigns a bg image
809
				.addClass("delete icon")
810
				.bind( 'click', function() {
811
					et2_dialog.show_dialog(
812
						function(button) {
813
							if(button == et2_dialog.YES_BUTTON)
814
							{
815
								egw.json("filemanager_ui::ajax_action", [
816
										'delete',
817
										[row.attr('data-path').replace(/&quot/g, "'")],
818
										''
819
									],
820
									function(data) {
821
										if(data && data.errs == 0) {row.slideUp(row.remove);}
822
										if(data && data.msg) {
823
											self.egw().message(data.msg, data.errs == 0 ? 'success' : 'error');
824
										}
825
									}
826
								).sendRequest();
827
							}
828
						},
829
						egw.lang('Delete file?')
830
					);
831
				});
832
		}
833
	}
834
});}).call(this);
835
et2_register_widget(et2_vfsUpload, ["vfs-upload"]);
836
837
838
var et2_vfsSelect = (function(){ "use strict"; return et2_inputWidget.extend(
839
{
840
	// Allowed mode options
841
	modes: ['open','open-multiple','saveas','select-dir'],
842
843
	attributes: {
844
		"mode": {
845
			name: "Dialog mode",
846
			type: "string",
847
			description: "One of {open|open-multiple|saveas|select-dir}",
848
			default: "open-multiple"
849
		},
850
		"method": {
851
			name: "Server side callback",
852
			type: "string",
853
			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."
854
		},
855
		"method_id": {
856
			name: "Method ID",
857
			type: "any",
858
			description: "optional parameter passed to server side callback.  Can be a string or a function.",
859
			default: ""
860
		},
861
		"path": {
862
			name: "Path",
863
			type: "string",
864
			description:"Start path in VFS.  Leave unset to use the last used path."
865
		},
866
		"mime": {
867
			name: "Mime type",
868
			type: "string",
869
			description: "Limit display to the given mime-type"
870
		},
871
		"button_label": {
872
			name: "Button label",
873
			description: "Set the label on the dialog's OK button.",
874
			default: "open"
875
		},
876
		"value": {
877
			"type": "any", // Object
878
			"description": "Array of paths (strings)"
879
		},
880
		"button_caption":{
881
			name: "button caption",
882
			type: "string",
883
			default: "Select files from Filemanager ...",
884
			description: "Caption for vfs-select button.",
885
			translate:true
886
		}
887
	},
888
889
	/**
890
	 * Constructor
891
	 *
892
	 * @param _parent
893
	 * @param _attrs
894
	 * @memberOf et2_vfsSelect
895
	 */
896
	init: function(_parent, _attrs) {
897
		// _super.apply is responsible for the actual setting of the params (some magic)
898
		this._super.apply(this, arguments);
899
900
		// Allow no child widgets
901
		this.supportedWidgetClasses = [];
902
903
		this.button = jQuery(document.createElement("button"))
904
			.attr("title", this.egw().lang("Select file(s) from VFS"))
905
			.addClass("et2_button et2_vfs_btn")
906
			.css("background-image","url("+this.egw().image("filemanager/navbar")+")");
907
908
		if(this.options.readonly)
909
		{
910
			this.button.hide();
911
		}
912
		
913
		if (this.options.button_caption != "")
914
		{
915
			this.button.text(this.options.button_caption);
916
		}
917
		this.setDOMNode(egw.app('filemanager') ? this.button[0]:document.createElement('span'));
918
	},
919
920
	click: function(e) {
921
922
	    // No permission
923
	    if(!egw.app('filemanager')) return;
924
925
		var self = this;
926
927
		var attrs = {
928
			menuaction: 'filemanager.filemanager_select.select',
929
			mode: this.options.mode,
930
			method: this.options.method,
931
			label: this.options.button_label,
932
			id: typeof this.options.method_id == "function" ? this.options.method_id.call(): this.options.method_id
933
		};
934
		if(this.options.path)
935
		{
936
			attrs.path = this.options.path;
937
		}
938
		if(this.options.mime)
939
		{
940
			attrs.mime = this.options.mime;
941
		};
942
943
		// Open the filemanager select in a popup
944
		var popup = this.egw(window).open_link(
945
			this.egw().link('/index.php', attrs),
946
			'link_existing',
947
			'680x400'
948
		);
949
		if(popup)
950
		{
951
			// Safari and IE lose reference to global variables after window close
952
			// Try to get updated data before window is closed then later we trigger
953
			// change event on widget
954
			self.egw().window.setTimeout(function(){
955
				jQuery(popup).bind('unload',function(){
956
					// Set selected files to widget
957
					self.value = this.selected_files;
958
959
					// Update path to where the user wound up]
960
					if (typeof this.etemplate2 !='undefined') self.options.path = this.etemplate2.getByApplication('filemanager')[0].widgetContainer.getArrayMgr("content").getEntry('path');
961
				});
962
			},1000);
963
964
			// Update on close doesn't always (ever, in chrome) work, so poll
965
			var poll = self.egw().window.setInterval(
966
				function() {
967
					if(popup.closed) {
968
						self.egw().window.clearInterval(poll);
969
						// Fire a change event so any handlers run
970
						jQuery(self.node).change();
971
					}
972
				},1000
973
			);
974
		}
975
	},
976
977
	/**
978
	 * Set the dialog's mode.
979
	 * Valid options are in et2_vfsSelect.modes
980
	 *
981
	 * @param {string} mode 'open', 'open-multiple', 'saveas' or 'select-dir'
982
	 */
983
	set_mode: function(mode) {
984
		// Check mode
985
		if(jQuery.inArray(mode, this.modes) < 0)
986
		{
987
			this.egw().debug("warn", "Invalid mode for '%s': %s Valid options:", this.id,mode, this.modes);
988
			return;
989
		}
990
		this.options.mode = mode;
991
	},
992
993
	/**
994
	 * Set the label on the dialog's OK button.
995
	 *
996
	 * @param {string} label
997
	 */
998
	set_button_label: function(label)
999
	{
1000
		this.options.button_label = label;
1001
	},
1002
1003
	/**
1004
	 * Set the caption for vfs-select button
1005
	 *
1006
	 * @param {string} caption string value as a caption
1007
	 */
1008
	set_button_caption: function (caption)
1009
	{
1010
		this.options.button_caption = caption;
1011
	},
1012
1013
	/**
1014
	 * Set the ID passed to the server side callback
1015
	 *
1016
	 * @param {string} id
1017
	 */
1018
	set_method_id: function(id) {
1019
		this.options.method_id = id;
1020
	},
1021
1022
	set_readonly: function(readonly) {
1023
		this.options.readonly = Boolean(readonly);
1024
		debugger;
0 ignored issues
show
Debugging Code introduced by
debugger looks like debug code. Are you sure you do not want to remove it?
Loading history...
1025
		if(this.options.readonly)
1026
		{
1027
			this.button.hide();
1028
		}
1029
		else
1030
		{
1031
			this.button.show();
1032
		}
1033
	},
1034
1035
	getValue: function() {
1036
		return this.value;
1037
	}
1038
});}).call(this);
1039
et2_register_widget(et2_vfsSelect, ["vfs-select"]);
1040