Completed
Push — 14.2 ( afd98e...d409f8 )
by Hadi
41:27 queued 11:53
created

AppJS.extend.copy_link   C

Complexity

Conditions 10
Paths 64

Size

Total Lines 51

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 10
nc 64
nop 2
dl 0
loc 51
rs 6
c 1
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like AppJS.extend.copy_link often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
/**
2
 * EGroupware - Filemanager - Javascript UI
3
 *
4
 * @link http://www.egroupware.org
5
 * @package filemanager
6
 * @author Ralf Becker <RalfBecker-AT-outdoor-training.de>
7
 * @copyright (c) 2008-14 by Ralf Becker <RalfBecker-AT-outdoor-training.de>
8
 * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
9
 * @version $Id$
10
 */
11
12
/**
13
 * UI for filemanager
14
 *
15
 * @augments AppJS
16
 */
17
app.classes.filemanager = AppJS.extend(
18
{
19
	appname: 'filemanager',
20
	/**
21
	 * path widget, by template
22
	 */
23
	path_widget: {},
24
	/**
25
	 * Are files cut into clipboard - need to be deleted at source on paste
26
	 */
27
	clipboard_is_cut: false,
28
29
	/**
30
	 * Constructor
31
	 *
32
	 * @memberOf app.filemanager
33
	 */
34
	init: function()
35
	{
36
		// call parent
37
		this._super.apply(this, arguments);
38
39
		// Loading filemanager in its tab and home causes us problems with
40
		// unwanted destruction, so we check for already existing path widgets
41
		var lists = etemplate2.getByApplication('home');
42
		for(var i = 0; i < lists.length; i++)
43
		{
44
			if(lists[i].app == 'filemanager' && lists[i].widgetContainer.getWidgetById('path'))
45
			{
46
				this.path_widget[lists[i].uniqueId] = lists[i].widgetContainer.getWidgetById('path');
47
			}
48
		}
49
	},
50
51
	/**
52
	 * Destructor
53
	 */
54
	destroy: function()
55
	{
56
		delete this.et2;
57
		// call parent
58
		this._super.apply(this, arguments);
59
	},
60
61
	/**
62
	 * This function is called when the etemplate2 object is loaded
63
	 * and ready.  If you must store a reference to the et2 object,
64
	 * make sure to clean it up in destroy().
65
	 *
66
	 * @param et2 etemplate2 Newly ready object
67
	 * @param {string} name template name
68
	 */
69
	et2_ready: function(et2,name)
70
	{
71
		// call parent
72
		this._super.apply(this, arguments);
73
74
		var path_widget = this.et2.getWidgetById('path');
75
		if(path_widget)	// do NOT set not found path-widgets, as uploads works on first one only!
76
		{
77
			this.path_widget[et2.DOMContainer.id] = path_widget;
78
			// Bind to removal to remove from list
79
			$j(et2.DOMContainer).on('clear', function(e) {
80
				if (app.filemanager && app.filemanager.path_widget) delete app.filemanager.path_widget[e.target.id];
81
			});
82
		}
83
84
		if(this.et2.getWidgetById('nm'))
85
		{
86
			// Legacy JS only supports 2 arguments (event and widget), so set
87
			// to the actual function here
88
			this.et2.getWidgetById('nm').set_onfiledrop(jQuery.proxy(this.filedrop, this));
89
		}
90
91
		// get clipboard from browser localstore and update button tooltips
92
		this.clipboard_tooltips();
93
94
		// calling set_readonly for initial path
95
		if (this.et2.getArrayMgr('content').getEntry('initial_path_readonly'))
96
		{
97
			this.readonly = [this.et2.getArrayMgr('content').getEntry('nm[path]'), true];
98
		}
99
		if (typeof this.readonly != 'undefined')
100
		{
101
			this.set_readonly.apply(this, this.readonly);
102
			delete this.readonly;
103
		}
104
	},
105
106
	/**
107
	 * Set the application's state to the given state.
108
	 *
109
	 * Extended from parent to also handle view
110
	 *
111
	 *
112
	 * @param {{name: string, state: object}|string} state Object (or JSON string) for a state.
113
	 *	Only state is required, and its contents are application specific.
114
	 *
115
	 * @return {boolean} false - Returns false to stop event propagation
116
	 */
117
	setState: function(state)
118
	{
119
		// State should be an object, not a string, but we'll parse
120
		if(typeof state == "string")
121
		{
122
			if(state.indexOf('{') != -1 || state =='null')
123
			{
124
				state = JSON.parse(state);
125
			}
126
		}
127
		var result = this._super.call(this, state, 'filemanager.index');
128
129
		// This has to happen after the parent, changing to tile recreates
130
		// nm controller
131
		if(typeof state == "object" && state.state && state.state.view)
132
		{
133
			var et2 = etemplate2.getById('filemanager-index');
134
			if(et2)
135
			{
136
				this.et2 = et2.widgetContainer;
137
				this.change_view(state.state.view);
138
			}
139
		}
140
		return result;
141
	},
142
143
	/**
144
	 * Retrieve the current state of the application for future restoration
145
	 *
146
	 * Extended from parent to also set view
147
	 *
148
	 * @return {object} Application specific map representing the current state
149
	 */
150
	getState: function()
151
	{
152
		var state = this._super.apply(this);
153
154
		var et2 = etemplate2.getById('filemanager-index');
155
		if(et2)
156
		{
157
			var nm = et2.widgetContainer.getWidgetById('nm');
158
			state.view = nm.view;
159
		}
160
161
		return state;
162
	},
163
164
	/**
165
	 * Regexp to convert id to a path, use this.id2path(_id)
166
	 */
167
	remove_prefix: /^filemanager::/,
168
	/**
169
	 * Convert id to path (remove "filemanager::" prefix)
170
	 *
171
	 * @param {string} _id
172
	 * @returns string
173
	 */
174
	id2path: function(_id)
175
	{
176
		return _id.replace(this.remove_prefix, '');
177
	},
178
179
	/**
180
	 * Convert array of elems to array of paths
181
	 *
182
	 * @param {egwActionObject[]} _elems selected items from actions
183
	 * @return array
184
	 */
185
	_elems2paths: function(_elems)
186
	{
187
		var paths = [];
188
		for (var i = 0; i < _elems.length; i++)
189
		{
190
			// If selected has no id, try parent.  This happens for the placeholder row
191
			// in empty directories.
192
			paths.push(_elems[i].id? this.id2path(_elems[i].id) : _elems[i]._context._parentId);
193
		}
194
		return paths;
195
	},
196
197
	/**
198
	 * Get directory of a path
199
	 *
200
	 * @param {string} _path
201
	 * @returns string
202
	 */
203
	dirname: function(_path)
204
	{
205
		var parts = _path.split('/');
206
		parts.pop();
207
		return parts.join('/') || '/';
208
	},
209
210
	/**
211
	 * Get name of a path
212
	 *
213
	 * @param {string} _path
214
	 * @returns string
215
	 */
216
	basename: function(_path)
217
	{
218
		return _path.split('/').pop();
219
	},
220
221
	/**
222
	 * Get current working directory
223
	 *
224
	 * @param {string} etemplate_name
225
	 * @return string
226
	 */
227
	get_path: function(etemplate_name)
228
	{
229
		if(!etemplate_name || typeof this.path_widget[etemplate_name] == 'undefined')
230
		{
231
			for(etemplate_name in this.path_widget) break;
232
		}
233
		return this.path_widget[etemplate_name] ? this.path_widget[etemplate_name].get_value() : null;
234
	},
235
236
	/**
237
	 * Open compose with already attached files
238
	 *
239
	 * @param {(string|string[])} attachments path(s)
240
	 * @param {object} params
241
	 */
242
	open_mail: function(attachments, params)
243
	{
244
		if (typeof attachments == 'undefined') attachments = this.get_clipboard_files();
245
		if (!params || typeof params != 'object') params = {};
246
		if (!(attachments instanceof Array)) attachments = [ attachments ];
247
		for(var i=0; i < attachments.length; i++)
248
		{
249
		   params['preset[file]['+i+']'] = 'vfs://default'+attachments[i];
250
		}
251
		egw.open('', 'mail', 'add', params);
252
	},
253
254
	/**
255
	 * Mail files action: open compose with already attached files
256
	 *
257
	 * @param _action
258
	 * @param _elems
259
	 */
260
	mail: function(_action, _elems)
261
	{
262
		this.open_mail(this._elems2paths(_elems), {
263
			'preset[filemode]': _action.id.substr(5)
264
		});
265
	},
266
267
	/**
268
	 * Trigger Upload after each file is uploaded
269
	 * @param {type} _event
270
	 */
271
	uploadOnOne: function (_event)
272
	{
273
		this.upload(_event,1);
274
	},
275
276
	/**
277
	 * Send names of uploaded files (again) to server, to process them: either copy to vfs or ask overwrite/rename
278
	 *
279
	 * @param {event} _event
280
	 * @param {number} _file_count
281
	 * @param {string=} _path where the file is uploaded to, default current directory
282
	 */
283
	upload: function(_event, _file_count, _path)
284
	{
285
		if(typeof _path == 'undefined')
286
		{
287
			_path = this.get_path();
288
		}
289
		if (_file_count && !jQuery.isEmptyObject(_event.data.getValue()))
290
		{
291
			var widget = _event.data;
292
			var request = egw.json('filemanager_ui::ajax_action', ['upload', widget.getValue(), _path],
293
				this._upload_callback, this, true, this
294
			).sendRequest();
295
			widget.set_value('');
296
		}
297
	},
298
299
	/**
300
	 * Finish callback for file a file dialog, to get the overwrite / rename prompt
301
	 *
302
	 * @param {event} _event
303
	 * @param {number} _file_count
304
	 */
305
	file_a_file_upload: function(_event, _file_count)
306
	{
307
		var widget = _event.data;
308
		var value = widget.getValue();
309
		var path = widget.getRoot().getWidgetById("path").getValue();
310
		var action = widget.getRoot().getWidgetById("action").getValue();
311
		var link = widget.getRoot().getWidgetById("entry").getValue();
312
		if(action == 'save_as' && link.app && link.id)
313
		{
314
			path = "/apps/"+link.app+"/"+link.id;
315
		}
316
317
		var props = widget.getInstanceManager().getValues(widget.getRoot());
318
		egw.json('filemanager_ui::ajax_action', [action == 'save_as' ? 'upload' : 'link', widget.getValue(), path, props],
319
				function(_data)
320
				{
321
					app.filemanager._upload_callback(_data);
322
323
					// Remove successful after a delay
324
					for(var file in _data.uploaded)
325
					{
326
						if(!_data.uploaded[file].confirm || _data.uploaded[file].confirmed)
327
						{
328
							// Remove that file from file widget...
329
							widget.remove_file(_data.uploaded[file].name);
330
						}
331
					}
332
					opener.egw_refresh('','filemanager',null,null,'filemanager');
333
				}, app.filemanager, true, this
334
		).sendRequest(true);
335
		return true;
336
	},
337
338
339
	/**
340
	 * Callback for server response to upload request:
341
	 * - display message and refresh list
342
	 * - ask use to confirm overwritting existing files or rename upload
343
	 *
344
	 * @param {object} _data values for attributes msg, files, ...
345
	 */
346
	_upload_callback: function(_data)
347
	{
348
		if (_data.msg || _data.uploaded) window.egw_refresh(_data.msg, this.appname);
349
350
		var that = this;
351
		for(var file in _data.uploaded)
352
		{
353
			if (_data.uploaded[file].confirm && !_data.uploaded[file].confirmed)
354
			{
355
				var buttons = [
356
					{text: this.egw.lang("Yes"), id: "overwrite", class: "ui-priority-primary", "default": true},
357
					{text: this.egw.lang("Rename"), id:"rename"},
358
					{text: this.egw.lang("Cancel"), id:"cancel"}
359
				];
360
				if (_data.uploaded[file].confirm === "is_dir")
361
					buttons.shift();
362
				var dialog = et2_dialog.show_prompt(function(_button_id, _value) {
363
					var uploaded = {};
364
					uploaded[this.my_data.file] = this.my_data.data;
365
					switch (_button_id)
0 ignored issues
show
Coding Style introduced by
As per coding-style, switch statements should have a default case.
Loading history...
366
					{
367
						case "overwrite":
368
							uploaded[this.my_data.file].confirmed = true;
369
							// fall through
370
						case "rename":
371
							uploaded[this.my_data.file].name = _value;
372
							delete uploaded[this.my_data.file].confirm;
373
							// send overwrite-confirmation and/or rename request to server
374
							egw.json('filemanager_ui::ajax_action', [this.my_data.action, uploaded, this.my_data.path, this.my_data.props],
375
								that._upload_callback, that, true, that
376
							).sendRequest();
377
							return;
378
						case "cancel":
379
							// Remove that file from every file widget...
380
							that.et2.iterateOver(function(_widget) {
381
								_widget.remove_file(this.my_data.data.name);
382
							}, this, et2_file);
383
					}
384
				},
385
				_data.uploaded[file].confirm === "is_dir" ?
386
					this.egw.lang("There's already a directory with that name!") :
387
					this.egw.lang('Do you want to overwrite existing file %1 in directory %2?', _data.uploaded[file].name, _data.path),
388
				this.egw.lang('File %1 already exists', _data.uploaded[file].name),
389
				_data.uploaded[file].name, buttons, file);
390
				// setting required data for callback in as my_data
391
				dialog.my_data = {
392
					action: _data.action,
393
					file: file,
394
					path: _data.path,
395
					data: _data.uploaded[file],
396
					props: _data.props
397
				};
398
			}
399
		}
400
	},
401
402
	/**
403
	 * Get any files that are in the system clipboard
404
	 *
405
	 * @return {string[]} Paths
406
	 */
407
	get_clipboard_files: function()
408
	{
409
		var clipboard_files = [];
410
		if (typeof window.localStorage != 'undefined' && typeof egw.getSessionItem('phpgwapi', 'egw_clipboard') != 'undefined')
411
		{
412
			var clipboard = JSON.parse(egw.getSessionItem('phpgwapi', 'egw_clipboard')) || {
413
				type:[],
414
				selected:[]
415
			};
416
			if(clipboard.type.indexOf('file') >= 0)
417
			{
418
				for(var i = 0; i < clipboard.selected.length; i++)
419
				{
420
					var split = clipboard.selected[i].id.split('::');
421
					if(split[0] == 'filemanager')
422
					{
423
						clipboard_files.push(this.id2path(clipboard.selected[i].id));
424
					}
425
				}
426
			}
427
		}
428
		return clipboard_files;
429
	},
430
431
	/**
432
	 * Update clickboard tooltips in buttons
433
	 */
434
	clipboard_tooltips: function()
435
	{
436
		var paste_buttons = ['button[paste]', 'button[linkpaste]', 'button[mailpaste]'];
437
		for(var i=0; i < paste_buttons.length; ++i)
438
		{
439
			var button = this.et2.getWidgetById(paste_buttons[i]);
440
			if (button) button.set_statustext(this.get_clipboard_files().join(",\n"));
441
		}
442
	},
443
444
	/**
445
	 * Clip files into clipboard
446
	 *
447
	 * @param _action
448
	 * @param _elems
449
	 */
450
	clipboard: function(_action, _elems)
451
	{
452
		this.clipboard_is_cut = _action.id == "cut";
453
		var clipboard = JSON.parse(egw.getSessionItem('phpgwapi', 'egw_clipboard')) || {
454
			type:[],
455
			selected:[]
456
		};
457
		if(_action.id != "add")
458
		{
459
			clipboard = {
460
				type:[],
461
				selected:[]
462
			};
463
		};
464
465
		// When pasting we need to know the type of data - pull from actions
466
		var drag = _elems[0].getSelectedLinks('drag').links;
467
		for(var k in drag)
468
		{
469
			if(drag[k].enabled && drag[k].actionObj.dragType.length > 0)
470
			{
471
				clipboard.type = clipboard.type.concat(drag[k].actionObj.dragType);
472
			}
473
		}
474
		clipboard.type = jQuery.unique(clipboard.type);
475
		// egwAction is a circular structure and can't be stringified so just take what we want
476
		// Hopefully that's enough for the action handlers
477
		for(var k in _elems)
478
		{
479
			if(_elems[k].id) clipboard.selected.push({id:_elems[k].id, data:_elems[k].data});
480
		}
481
482
		// Save it in session
483
		egw.setSessionItem('phpgwapi', 'egw_clipboard', JSON.stringify(clipboard));
484
485
		this.clipboard_tooltips();
486
	},
487
488
	/**
489
	 * Paste files into current directory or mail them
490
	 *
491
	 * @param _type 'paste', 'linkpaste', 'mailpaste'
492
	 */
493
	paste: function(_type)
494
	{
495
		var clipboard_files = this.get_clipboard_files();
496
		if (clipboard_files.length == 0)
497
		{
498
			alert(this.egw.lang('Clipboard is empty!'));
499
			return;
500
		}
501
		switch(_type)
0 ignored issues
show
Coding Style introduced by
As per coding-style, switch statements should have a default case.
Loading history...
502
		{
503
			case 'mailpaste':
504
				this.open_mail(clipboard_files);
505
				break;
506
507
			case 'paste':
508
				this._do_action(this.clipboard_is_cut ? 'move' : 'copy', clipboard_files);
509
510
				if (this.clipboard_is_cut)
511
				{
512
					this.clipboard_is_cut = false;
513
					clipboard_files = [];
514
					this.clipboard_tooltips();
515
				}
516
				break;
517
518
			case 'linkpaste':
519
				this._do_action('symlink', clipboard_files);
520
				break;
521
		}
522
	},
523
524
	/**
525
	 * Pass action to server
526
	 *
527
	 * @param _action
528
	 * @param _elems
529
	 */
530
	action: function(_action, _elems)
531
	{
532
		var paths = this._elems2paths(_elems);
533
		var path = this.get_path(_action && _action.parent.data.nextmatch.getInstanceManager().uniqueId || false);
534
		this._do_action(_action.id, paths,true, path);
535
	},
536
537
	/**
538
	 * Prompt user for directory to create
539
	 *
540
	 * @param {egwAction|undefined|jQuery.Event} action Action, event or undefined if called directly
541
	 * @param {egwActionObject[] | undefined} selected Selected row, or undefined if called directly
542
	 */
543
	createdir: function(action, selected)
544
	{
545
		var self = this;
546
		et2_dialog.show_prompt(function(button, dir){
547
			if (button && dir)
548
			{
549
				var path = self.get_path(action && action.parent ? action.parent.data.nextmatch.getInstanceManager().uniqueId : false);
550
				if(action && action instanceof egwAction)
551
				{
552
					var paths = self._elems2paths(selected);
553
					if(paths[0]) path = paths[0];
554
					// check if target is a file --> use it's directory instead
555
					if(selected[0].id || path)
556
					{
557
						var data = egw.dataGetUIDdata(selected[0].id || 'filemanager::'+path );
558
						if (data && data.data.mime != 'httpd/unix-directory')
559
						{
560
							path = self.dirname(path);
561
						}
562
					}
563
				}
564
				self._do_action('createdir', dir, true, path);	// true=synchronous request
565
				self.change_dir((path == '/' ? '' : path)+'/'+ dir);
566
			}
567
		},this.egw.lang('New directory'),this.egw.lang('Create directroy'));
568
	},
569
570
	/**
571
	 * Prompt user for directory to create
572
	 */
573
	symlink: function()
574
	{
575
		var self = this;
576
		et2_dialog.show_prompt(function (button, target) {
577
			if (button && target)
578
			{
579
				self._do_action('symlink', target);
580
			}
581
		},this.egw.lang('Link target'), this.egw.lang('Create link'));
582
	},
583
584
	/**
585
	 * Run a serverside action via an ajax call
586
	 *
587
	 * @param _type 'move_file', 'copy_file', ...
588
	 * @param _selected selected paths
589
	 * @param _sync send a synchronous ajax request
590
	 * @param _path defaults to current path
591
	 */
592
	_do_action: function(_type, _selected, _sync, _path)
593
	{
594
		if (typeof _path == 'undefined') _path = this.get_path();
595
		var request = egw.json('filemanager_ui::ajax_action', [_type, _selected, _path],
596
			this._do_action_callback, this, !_sync, this
597
		).sendRequest();
598
	},
599
600
	/**
601
	 * Callback for _do_action ajax call
602
	 *
603
	 * @param _data
604
	 */
605
	_do_action_callback: function(_data)
606
	{
607
		window.egw_refresh(_data.msg, this.appname);
608
	},
609
610
	/**
611
	 * Force download of a file by appending '?download' to it's download url
612
	 *
613
	 * @param _action
614
	 * @param _senders
615
	 */
616
	force_download: function(_action, _senders)
617
	{
618
		for(var i = 0; i < _senders.length; i++)
619
		{
620
			var data = egw.dataGetUIDdata(_senders[i].id);
621
			var url = data ? data.data.download_url : '/webdav.php'+this.id2path(_senders[i].id);
622
			if (url[0] == '/') url = egw.link(url);
623
624
			var a = document.createElement('a');
625
			if(typeof a.download == "undefined")
626
			{
627
				window.location = url+"?download";
628
				return false;
629
			}
630
631
			// Multiple file download for those that support it
632
			a = $j(a)
633
				.prop('href', url)
634
				.prop('download', data ? data.data.name : "")
635
				.appendTo(this.et2.getDOMNode());
636
637
			var evt = document.createEvent('MouseEvent');
638
			evt.initMouseEvent('click', true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 0, null);
639
			a[0].dispatchEvent(evt);
640
			a.remove();
641
		}
642
		return false;
643
	},
644
645
	/**
646
	 * Check to see if the browser supports downloading multiple files
647
	 * (using a tag download attribute) to enable/disable the context menu
648
	 *
649
	 * @param {egwAction} action
650
	 * @param {egwActionObject[]} selected
651
	 */
652
	is_multiple_allowed: function(action, selected)
653
	{
654
		var allowed = typeof document.createElement('a').download != "undefined";
655
656
		if(typeof action == "undefined") return allowed;
657
658
		return (allowed || selected.length <= 1) && action.not_disableClass.apply(action, arguments);
659
	},
660
661
662
	/**
663
	 * Change directory
664
	 *
665
	 * @param {string} _dir directory to change to incl. '..' for one up
666
	 * @param {et2_widget} widget
667
	 */
668
	change_dir: function(_dir, widget)
669
	{
670
		for(var etemplate_name in this.path_widget) break;
671
		if (widget) etemplate_name = widget.getInstanceManager().uniqueId;
672
673
		switch (_dir)
0 ignored issues
show
Coding Style introduced by
As per coding-style, switch statements should have a default case.
Loading history...
674
		{
675
			case '..':
676
				_dir = this.dirname(this.get_path(etemplate_name));
1 ignored issue
show
Bug introduced by
The variable etemplate_name seems to not be initialized for all possible execution paths. Are you sure get_path handles undefined variables?
Loading history...
677
				break;
678
			case '~':
679
				_dir = this.et2.getWidgetById('nm').options.settings.home_dir;
680
				break;
681
		}
682
683
		this.path_widget[etemplate_name].set_value(_dir);
684
	},
685
686
	/**
687
	 * Toggle view between tiles and rows
688
	 *
689
	 * @param {string|Event} [view] - Specify what to change the view to.  Either 'tile' or 'row'.
0 ignored issues
show
Documentation introduced by
The parameter [view] does not exist. Did you maybe forget to remove this comment?
Loading history...
690
	 *	Or, if this is used as a callback view is actually the event, and we need to find the view.
691
	 * @param {et2_widget} [button_widget] - The widget that's calling
0 ignored issues
show
Documentation Bug introduced by
The parameter [button_widget] does not exist. Did you maybe mean button_widget instead?
Loading history...
692
	 */
693
	change_view: function(view, button_widget)
694
	{
695
		var et2 = etemplate2.getById('filemanager-index');
696
		var nm = false;
697
		if(et2 && et2.widgetContainer.getWidgetById('nm'))
698
		{
699
			nm = et2.widgetContainer.getWidgetById('nm');
700
		}
701
		if(!nm)
702
		{
703
			egw.debug('warn', 'Could not find nextmatch to change view');
704
705
			return;
706
		}
707
708
		if(!button_widget)
709
		{
710
			button_widget = nm.getWidgetById('button[change_view]');
711
		}
712
		if(button_widget && button_widget.instanceOf(et2_button))
713
		{
714
			// Switch view based on button icon, since controller can get re-created
715
			if(typeof view != 'string')
716
			{
717
				view = button_widget.options.image.replace('list_','');
718
			}
719
720
			// Toggle button icon to the other view
721
			button_widget.set_image("list_"+(view == nm.controller.VIEW_ROW ? nm.controller.VIEW_TILE : nm.controller.VIEW_ROW));
722
723
			button_widget.set_label(view == nm.controller.VIEW_ROW ? this.egw.lang("Tile view") : this.egw.lang('List view'));
724
		}
725
726
		nm.set_view(view);
727
		// Put it into active filters (but don't refresh)
728
		nm.activeFilters.view = view;
729
730
		// Change template to match
731
		var template = view == nm.controller.VIEW_ROW ? 'filemanager.index.rows' : 'filemanager.tile';
732
		nm.set_template(template);
733
734
		// Wait for template to load, then refresh
735
		template = nm.getWidgetById(template);
736
		if(template && template.loading)
737
		{
738
			template.loading.done(function() {
739
				nm.applyFilters({view: view});
740
			})
741
		}
742
	},
743
744
	/**
745
	 * Open/active an item
746
	 *
747
	 * @param _action
748
	 * @param _senders
749
	 */
750
	open: function(_action, _senders)
751
	{
752
		var data = egw.dataGetUIDdata(_senders[0].id);
753
		var path = this.id2path(_senders[0].id);
754
		var mime = this.et2._inst.widgetContainer.getWidgetById('$row');
755
		// try to get mime widget DOM node out of the row DOM
756
		var mime_dom = jQuery(_senders[0].iface.getDOMNode()).find("span#filemanager-index_\\$row");
757
758
		// symlinks dont have mime 'http/unix-directory', but server marks all directories with class 'isDir'
759
		if (data.data.mime == 'httpd/unix-directory' || data.data['class'] && data.data['class'].split(/ +/).indexOf('isDir') != -1)
760
		{
761
			this.change_dir(path,_action.parent.data.nextmatch || this.et2);
762
		}
763
		else if(mime && data.data.mime.match(mime.mime_regexp) && mime_dom.length>0)
764
		{
765
			mime_dom.click();
766
		}
767
		else
768
		{
769
			egw.open({path: path, type: data.data.mime, download_url: data.data.download_url}, 'file','view',null,'_browser');
770
		}
771
		return false;
772
	},
773
774
	/**
775
	 * Edit prefs of current directory
776
	 *
777
	 * @param _action
778
	 * @param _senders
779
	 */
780
	editprefs: function(_action, _senders)
781
	{
782
		var path =  typeof _senders != 'undefined' ? this.id2path(_senders[0].id) : this.get_path(_action && _action.parent.data.nextmatch.getInstanceManager().uniqueId || false);
783
784
		egw().open_link(egw.link('/index.php', {
785
			menuaction: 'filemanager.filemanager_ui.file',
786
			path: path
787
		}), 'fileprefs', '510x425');
788
	},
789
790
	/**
791
	 * File(s) droped
792
	 *
793
	 * @param _action
794
	 * @param _elems
795
	 * @param _target
796
	 * @returns
797
	 */
798
	drop: function(_action, _elems, _target)
799
	{
800
		var src = this._elems2paths(_elems);
801
802
803
		// Target will be missing ID if directory is empty
804
		// so start with the current directory
805
		var nm_dst = this.get_path(_action.parent.data.nextmatch.getInstanceManager().uniqueId || false);
806
807
		// File(s) were dropped on a row, they want them inside
808
		if(_target)
809
		{
810
			var dst = '';
811
			var paths = this._elems2paths([_target]);
812
			if(paths[0]) dst = paths[0];
813
814
			// check if target is a file --> use it's directory instead
815
			if(_target.id)
816
			{
817
				var data = egw.dataGetUIDdata(_target.id);
818
				if (!data || data.data.mime != 'httpd/unix-directory')
819
				{
820
					dst = this.dirname(dst);
821
				}
822
			}
823
		}
824
825
		this._do_action(_action.id.replace("file_drop_",''), src, false, dst || nm_dst);
826
	},
827
828
	/**
829
	 * Handle a native / HTML5 file drop from system
830
	 *
831
	 * This is a callback from nextmatch to prevent the default link action, and just upload instead.
832
	 *
833
	 * @param {string} row_uid UID of the row the files were dropped on
834
	 * @param {Files[]} files
835
	 */
836
	filedrop: function(row_uid, files)
837
	{
838
		var self = this;
839
		var data = egw.dataGetUIDdata(row_uid);
840
		files = files || window.event.dataTransfer.files;
841
842
		var path = typeof data != 'undefined' && data.data.mime == "httpd/unix-directory" ? data.data.path : this.get_path();
843
		var widget = this.et2.getWidgetById('upload');
844
845
		// Override finish to specify a potentially different path
846
		var old_onfinishone = widget.options.onFinishOne;
847
		var old_onfinish = widget.options.onFinish;
848
849
		widget.options.onFinishOne = function(_event, _file_count) {
850
			self.upload(_event, _file_count, path);
851
		};
852
853
		widget.options.onFinish = function() {
854
			widget.options.onFinish = old_onfinish;
855
			widget.options.onFinishOne = old_onfinishone;
856
		}
857
		// This triggers the upload
858
		widget.set_value(files);
859
860
		// Return false to prevent the link
861
		return false;
862
	},
863
864
	/**
865
	 * Change readonly state for given directory
866
	 *
867
	 * Get call/transported with each get_rows call, but should only by applied to UI if matching curent dir
868
	 *
869
	 * @param {string} _path
870
	 * @param {boolean} _ro
871
	 */
872
	set_readonly: function(_path, _ro)
873
	{
874
		//alert('set_readonly("'+_path+'", '+_ro+')');
875
		if (!this.path_widget)	// widget not yet ready, try later
876
		{
877
			this.readonly = [_path, _ro];
878
			return;
879
		}
880
		for(var id in this.path_widget)
881
		{
882
			var path = this.get_path(id);
883
884
			if (_path == path)
885
			{
886
				var ids = ['button[linkpaste]', 'button[paste]', 'button[createdir]', 'button[symlink]', 'upload'];
887
				for(var i=0; i < ids.length; ++i)
888
				{
889
					var widget = etemplate2.getById(id).widgetContainer.getWidgetById(ids[i]);
890
					if (widget)
891
					{
892
						if (widget._type == 'button' || widget._type == 'buttononly')
893
						{
894
							widget.set_readonly(_ro);
895
						}
896
						else
897
						{
898
							widget.set_disabled(_ro);
899
						}
900
					}
901
				}
902
			}
903
		}
904
	},
905
906
	/**
907
	 * Row or filename in select-file dialog clicked
908
	 *
909
	 * @param {jQuery.event} event
910
	 * @param {et2_widget} widget
911
	 */
912
	select_clicked: function(event, widget)
913
	{
914
		if (!widget || typeof widget.value != 'object')
915
		{
0 ignored issues
show
Comprehensibility Documentation Best Practice introduced by
This code block is empty. Consider removing it or adding a comment to explain.
Loading history...
916
917
		}
918
		else if (widget.value.is_dir)	// true for "httpd/unix-directory" and "egw/*"
919
		{
920
			var path = null;
921
			// Cannot do this, there are multiple widgets named path
922
			// widget.getRoot().getWidgetById("path");
923
			widget.getRoot().iterateOver(function(widget) {
924
				if(widget.id == "path") path = widget;
925
			},null, et2_textbox);
926
			if(path)
927
			{
928
				path.set_value(widget.value.path);
929
			}
930
		}
931
		else if (this.et2 && this.et2.getArrayMgr('content').getEntry('mode') != 'open-multiple')
932
		{
933
			var editfield = this.et2.getWidgetById('name');
934
			if(editfield)
935
			{
936
				editfield.set_value(widget.value.name);
937
			}
938
		}
939
		else
940
		{
941
			var file = widget.value.name;
942
			widget.getParent().iterateOver(function(widget)
943
			{
944
				if(widget.options.selected_value == file)
945
				{
946
					widget.set_value(widget.get_value() == file ? widget.options.unselected_value : file);
947
				}
948
			}, null, et2_checkbox);
949
950
		}
951
		// Stop event or it will toggle back off
952
		event.preventDefault();
953
		event.stopPropagation();
954
		return false;
955
	},
956
957
	/**
958
	 * Set Sudo button's label and change its onclick handler according to its action
959
	 *
960
	 * @param {widget object} _widget sudo buttononly
961
	 * @param {string} _action string of action type {login|logout}
962
	 */
963
	set_sudoButton: function (_widget, _action)
964
	{
965
		var widget = _widget || this.et2.getWidgetById('sudouser');
966
		if (widget)
967
		{
968
			switch (_action)
969
			{
970
				case 'login':
971
					widget.set_label('Logout');
972
					this.et2._inst.submit(widget);
973
					break;
974
975
				default:
976
					widget.set_label('Superuser');
977
					widget.onclick = function(){
978
						jQuery('.superuser').css('display','inline');
979
					};
980
			}
981
		}
982
	},
983
984
	/**
985
	 * This function copies the selected file/folder entry as webdav link into clipboard
986
	 *
987
	 * @param {object} _action egw actions
988
	 * @param {object} _senders selected nm row
989
	 * @returns {Boolean} returns false if not successful
990
	 */
991
	copy_link: function (_action, _senders)
992
	{
993
		var data = egw.dataGetUIDdata(_senders[0].id);
994
		var url = data ? data.data.download_url : '/webdav.php'+this.id2path(_senders[0].id);
995
		if (url[0] == '/') url = egw.link(url);
996
		if (url.substr(0,4) == 'http'  && url.indexOf('://') <= 5) {
997
			// it's already a full url
998
		}
999
		else
1000
		{
1001
			var hostUrl = new URL(window.location.href);
1002
			url = hostUrl.origin + url;
1003
		}
1004
1005
		if (url)
1006
		{
1007
			var elem = jQuery(document.createElement('div'));
1008
			var range = [];
1009
			elem.text(url);
1010
			elem.appendTo('body');
1011
			if (document.selection)
1012
			{
1013
				range = document.body.createTextRange();
1014
				range.moveToElementText(elem);
1015
				range.select();
1016
			}
1017
			else if (window.getSelection)
1018
			{
1019
				var range = document.createRange();
1020
				range.selectNode(elem[0]);
1021
				window.getSelection().removeAllRanges();
1022
				window.getSelection().addRange(range);
1023
			}
1024
1025
			var successful = false;
1026
			try {
1027
				successful = document.execCommand('copy');
1028
				if (successful)
1029
				{
1030
					egw.message('WebDav link copied into clipboard');
1031
					window.getSelection().removeAllRanges();
1032
1033
					return true;
1034
				}
1035
			}
1036
			catch (e) {}
0 ignored issues
show
Coding Style Comprehensibility Best Practice introduced by
Empty catch clauses should be used with caution; consider adding a comment why this is needed.
Loading history...
1037
			egw.message('Failed to copy the link!');
1038
			elem.remove();
1039
			return false;
1040
		}
1041
	}
1042
});
1043