Completed
Pull Request — master (#328)
by
unknown
04:58
created

Gallery.getSelectionDownloadUrl   A

Complexity

Conditions 1
Paths 3

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 3
nop 3
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
/**
2
 * Nextcloud - Gallery
3
 *
4
 *
5
 * This file is licensed under the Affero General Public License version 3 or
6
 * later. See the COPYING file.
7
 *
8
 * @author Olivier Paroz <[email protected]>
9
 *
10
 * @copyright Olivier Paroz 2017
11
 */
12
/* global Album, GalleryImage */
13
(function ($, OC, t) {
14
	"use strict";
15
	var Gallery = {
16
		currentAlbum: null,
17
		currentEtag: null,
18
		config: {},
19
		/** Map of the whole gallery, built as we navigate through folders */
20
		albumMap: {},
21
		/** Used to pick an image based on the URL */
22
		imageMap: {},
23
		appName: 'gallery',
24
		token: undefined,
25
		activeSlideShow: null,
26
		buttonsWidth: 600,
27
		browserToolbarHeight: 150,
28
		filesClient: null,
29
30
		/**
31
		 * Refreshes the view and starts the slideshow if required
32
		 *
33
		 * @param {string} path
34
		 * @param {string} albumPath
35
		 */
36
		refresh: function (path, albumPath) {
37
			if (Gallery.currentAlbum !== albumPath) {
38
				Gallery.view.init(albumPath, null);
39
			}
40
41
			// If the path is mapped, that means that it's an albumPath
42
			if (Gallery.albumMap[path]) {
43
				if (Gallery.activeSlideShow) {
44
					Gallery.activeSlideShow.stop();
45
				}
46
			} else if (Gallery.imageMap[path] && Gallery.activeSlideShow.active === false) {
47
				Gallery.view.startSlideshow(path, albumPath);
48
			}
49
		},
50
51
		/**
52
		 * Retrieves information about all the images and albums located in the current folder
53
		 *
54
		 * @param {string} currentLocation
55
		 *
56
		 * @returns {*}
57
		 */
58
		getFiles: function (currentLocation) {
59
			// Cache the sorting order of the current album before loading new files
60
			if (!$.isEmptyObject(Gallery.albumMap)) {
61
				Gallery.albumMap[Gallery.currentAlbum].sorting = Gallery.config.albumSorting;
62
			}
63
			// Checks if we've visited this location before ands saves the etag to use for
64
			// comparison later
65
			var albumEtag;
66
			var albumCache = Gallery.albumMap[decodeURIComponent(currentLocation)];
67
			if (!$.isEmptyObject(albumCache)) {
68
				albumEtag = albumCache.etag;
69
			}
70
71
			// Sends the request to the server
72
			var params = {
73
				location: currentLocation,
74
				mediatypes: Gallery.config.getMediaTypes(),
75
				features: Gallery.config.getFeatures(),
76
				etag: albumEtag
0 ignored issues
show
Bug introduced by
The variable albumEtag does not seem to be initialized in case !$.isEmptyObject(albumCache) on line 67 is false. Are you sure this can never be the case?
Loading history...
77
			};
78
			// Only use the folder as a GET parameter and not as part of the URL
79
			var url = Gallery.utility.buildGalleryUrl('files', '/list', params);
80
			return $.getJSON(url).then(
81
				function (/**@type{{
82
					* files:Array,
83
					* albums:Array,
84
					* albumconfig:Object,
85
					* albumpath:String,
86
					* updated:Boolean}}*/
87
						  data) {
88
					var albumpath = data.albumpath;
89
					var updated = data.updated;
90
					// FIXME albumConfig should be cached as well
91
					/**@type {{design,information,sorting,error: string}}*/
92
					var albumConfig = data.albumconfig;
93
					//Gallery.config.setAlbumPermissions(currentAlbum);
94
					Gallery.config.setAlbumConfig(albumConfig, albumpath);
95
					// Both the folder and the etag have to match
96
					if ((decodeURIComponent(currentLocation) === albumpath) &&
97
						(updated === false)) {
98
						Gallery.imageMap = albumCache.imageMap;
99
					} else {
100
						Gallery._mapFiles(data);
101
					}
102
103
					// Restore the previous sorting order for this album
104
					if (!$.isEmptyObject(Gallery.albumMap[albumpath].sorting)) {
105
						Gallery.config.updateAlbumSorting(
106
							Gallery.albumMap[albumpath].sorting);
107
					}
108
109
				}, function (xhr) {
110
					var result = xhr.responseJSON;
111
					var albumPath = decodeURIComponent(currentLocation);
112
					var message;
113
					if (result === null) {
114
						message = t('gallery', 'There was a problem reading files from this album');
115
					} else {
116
						message = result.message;
117
					}
118
					Gallery.view.init(albumPath, message);
119
					Gallery._mapStructure(albumPath);
120
				});
121
		},
122
123
		/**
124
		 * Sorts albums and images based on user preferences
125
		 */
126
		sorter: function () {
127
			var sortType = 'name';
128
			var sortOrder = 'asc';
129
			var albumSortType = 'name';
130
			var albumSortOrder = 'asc';
131
			if (this.id === 'sort-date-button') {
132
				sortType = 'date';
133
134
			}
135
			var currentSort = Gallery.config.albumSorting;
136
			if (currentSort.type === sortType && currentSort.order === sortOrder) {
137
				sortOrder = 'des';
138
			}
139
140
			// Update the controls
141
			Gallery.view.sortControlsSetup(sortType, sortOrder);
142
143
			// We can't currently sort by album creation time
144
			if (sortType === 'name') {
145
				albumSortOrder = sortOrder;
146
			}
147
148
			// FIXME Rendering is still happening while we're sorting...
149
150
			// Clear before sorting
151
			Gallery.view.clear();
152
153
			// Sort the images
154
			Gallery.albumMap[Gallery.currentAlbum].images.sort(Gallery.utility.sortBy(sortType,
155
				sortOrder));
156
			Gallery.albumMap[Gallery.currentAlbum].subAlbums.sort(
157
				Gallery.utility.sortBy(albumSortType,
158
					albumSortOrder));
159
160
			// Save the new settings
161
			var sortConfig = {
162
				type: sortType,
163
				order: sortOrder,
164
				albumOrder: albumSortOrder
165
			};
166
			Gallery.config.updateAlbumSorting(sortConfig);
167
168
			// Refresh the view
169
			Gallery.view.viewAlbum(Gallery.currentAlbum);
170
		},
171
172
		/**
173
		 * Switches to the Files view
174
		 *
175
		 * @param event
176
		 */
177
		switchToFilesView: function (event) {
178
			event.stopPropagation();
179
180
			var subUrl = '';
181
			var params = {path: '/' + Gallery.currentAlbum};
182
			if (Gallery.token) {
183
				params.token = Gallery.token;
184
				subUrl = 's/{token}?path={path}';
185
			} else {
186
				subUrl = 'apps/files?dir={path}';
187
			}
188
189
			var button = $('#filelist-button');
190
			button.children('img').addClass('hidden');
191
			button.children('#button-loading').removeClass('hidden').addClass('icon-loading-small');
192
			OC.redirect(OC.generateUrl(subUrl, params));
193
		},
194
195
		/**
196
		 * Populates the share dialog with the needed information
197
		 *
198
		 * @param event
199
		 */
200
		share: function (event) {
201
			// Clicking on share button does not trigger automatic slide-up
202
			$('.album-info-container').slideUp();
203
204
			if (!Gallery.Share.droppedDown) {
205
				event.preventDefault();
206
				event.stopPropagation();
207
208
				var currentAlbum = Gallery.albumMap[Gallery.currentAlbum];
209
				$('#controls a.share').data('path', currentAlbum.path)
210
					.data('link', true)
211
					.data('item-source', currentAlbum.fileId)
212
					.data('possible-permissions', currentAlbum.permissions)
213
					.click();
214
				if (!$('#linkCheckbox').is(':checked')) {
215
					$('#linkText').hide();
216
				}
217
			}
218
		},
219
220
		/**
221
		 * Sends an archive of the current folder to the browser
222
		 *
223
		 * @param event
224
		 */
225
		download: function (event) {
226
			event.preventDefault();
227
228
			var path = $('#content').data('albumname');
229
			var files = Gallery.currentAlbum;
230
			var downloadUrl = Gallery.utility.buildFilesUrl(path, files);
231
232
			OC.redirect(downloadUrl);
233
		},
234
235
		/**
236
		 * Returns the download URL of the given file(s)
237
		 * @param {string} files string or array of file names to download
238
		 * @param {string} [dir] optional directory in which the file name is, defaults to the current directory
239
		 * @param {bool} [isDir] whether the given filename is a directory and might need a special URL. False by default.
0 ignored issues
show
Coding Style introduced by
Line is too long.
Loading history...
240
		 *
241
		 * @see core/apps/files/js/filelist.js
242
		 */
243
		getSelectionDownloadUrl: function(files, dir, isDir) {
244
			return OCA.Files.Files.getDownloadUrl(files, dir || Gallery.currentAlbum || '/', isDir);
245
		},
246
247
		/**
248
		 * Shows an information box to the user
249
		 *
250
		 * @param event
251
		 */
252
		showInfo: function (event) {
253
			event.stopPropagation();
254
			Gallery.infoBox.showInfo();
255
		},
256
257
		/**
258
		 * Lets the user add the shared files to his cloud
259
		 */
260
		showSaveForm: function () {
261
			$(this).hide();
262
			$('.save-form').css('display', 'inline');
263
			$('#remote_address').focus();
264
		},
265
266
		/**
267
		 * Sends the shared files to the viewer's cloud
268
		 *
269
		 * @param event
270
		 */
271
		saveForm: function (event) {
272
			event.preventDefault();
273
274
			var saveElement = $('#save');
275
			var remote = $(this).find('input[type="text"]').val();
276
			var owner = saveElement.data('owner');
277
			var name = saveElement.data('name');
278
			var isProtected = saveElement.data('protected');
279
			Gallery._saveToServer(remote, Gallery.token, owner, name, isProtected);
280
		},
281
282
		/**
283
		 * Creates a new slideshow using the images found in the current folder
284
		 *
285
		 * @param {Array} images
286
		 * @param {string} startImage
287
		 * @param {boolean} autoPlay
288
		 *
289
		 * @returns {boolean}
290
		 */
291
		slideShow: function (images, startImage, autoPlay) {
292
			if (startImage === undefined) {
293
				OC.Notification.showTemporary(t('gallery',
294
					'Aborting preview. Could not find the file'));
295
				return false;
296
			}
297
			var start = images.indexOf(startImage);
298
			images = images.filter(function (image, index) {
299
				// If the slideshow is loaded before we get a thumbnail, we have to accept all
300
				// images
301
				if (!image.thumbnail) {
302
					return image;
303
				} else {
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
304
					if (image.thumbnail.valid) {
305
						return image;
306
					} else if (index < images.indexOf(startImage)) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if index < images.indexOf(startImage) is false. Are you sure this is correct? If so, consider adding return; explicitly.

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

Loading history...
307
						start--;
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
308
					}
309
				}
310
			}).map(function (image) {
311
				var name = OC.basename(image.path);
312
				var previewUrl = Gallery.utility.getPreviewUrl(image.fileId, image.etag);
313
				var params = {
314
					c: image.etag,
315
					requesttoken: oc_requesttoken
0 ignored issues
show
Bug introduced by
The variable oc_requesttoken seems to be never declared. If this is a global, consider adding a /** global: oc_requesttoken */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
Coding Style introduced by
Identifier 'oc_requesttoken' is not in camel case.
Loading history...
Bug introduced by
oc_requesttoken does not seem to be defined.
Loading history...
316
				};
317
				var downloadUrl = Gallery.utility.buildGalleryUrl('files',
318
					'/download/' + image.fileId,
319
					params);
320
321
				return {
322
					name: name,
323
					path: image.path,
324
					file: image.fileId,
325
					mimeType: image.mimeType,
326
					permissions: image.permissions,
327
					url: previewUrl,
328
					downloadUrl: downloadUrl
329
				};
330
			});
331
			Gallery.activeSlideShow.setImages(images, autoPlay);
332
			Gallery.activeSlideShow.onStop = function () {
333
				$('#content').show();
334
				Gallery.view.removeLoading();
335
				if (Gallery.currentAlbum !== '') {
336
					// Only modern browsers can manipulate history
337
					if (history && history.replaceState) {
0 ignored issues
show
Best Practice introduced by
If you intend to check if the variable history is declared in the current environment, consider using typeof history === "undefined" instead. This is safe if the variable is not actually declared.
Loading history...
338
						history.replaceState('', '',
339
							'#' + encodeURIComponent(Gallery.currentAlbum));
340
					} else {
341
						location.hash = '#' + encodeURIComponent(Gallery.currentAlbum);
342
					}
343
				} else {
344
					// Only modern browsers can manipulate history
345
					if (history && history.replaceState) {
346
						history.replaceState('', '', '#');
347
					} else {
348
						location.hash = '#';
349
					}
350
				}
351
			};
352
			Gallery.activeSlideShow.show(start);
353
			if(!_.isUndefined(Gallery.Share)){
354
				Gallery.Share.hideDropDown();
355
			}
356
			$('.album-info-container').slideUp();
357
			// Resets the last focused element
358
			document.activeElement.blur();
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
359
		},
360
361
		/**
362
		 * Moves files and albums to a new location
363
		 *
364
		 * @param {jQuery} $item
365
		 * @param {string} fileName
366
		 * @param {string} filePath
367
		 * @param {jQuery} $target
368
		 * @param {string} targetPath
369
		 */
370
		move: function ($item, fileName, filePath, $target, targetPath) {
371
			var self = this;
372
			var dir = Gallery.currentAlbum;
373
374
			if (targetPath.charAt(targetPath.length - 1) !== '/') {
375
				// make sure we move the files into the target dir,
376
				// not overwrite it
377
				targetPath = targetPath + '/';
378
			}
379
			self.filesClient.move(dir + '/' + fileName, targetPath + fileName)
380
				.done(function () {
381
					self._removeElement(dir, filePath, $item);
382
				})
383
				.fail(function (status) {
384
					if (status === 412) {
385
						// TODO: some day here we should invoke the conflict dialog
386
						OC.Notification.showTemporary(
387
							t('gallery', 'Could not move "{file}", target exists', {file: fileName})
388
						);
389
					} else {
390
						OC.Notification.showTemporary(
391
							t('gallery', 'Could not move "{file}"', {file: fileName})
392
						);
393
					}
394
					$item.fadeTo("normal", 1);
395
					$target.children('.album-loader').hide();
396
				})
397
				.always(function () {
398
					// Nothing?
399
				});
400
		},
401
402
		/**
403
		 * Builds the album's model
404
		 *
405
		 * @param {{
406
		 * 	files:Array,
407
		 * 	albums:Array,
408
		 * 	albumconfig:Object,
409
		 * 	albumpath:String,
410
		 *	updated:Boolean
411
		 * 	}} data
412
		 * @private
413
		 */
414
		_mapFiles: function (data) {
415
			Gallery.imageMap = {};
416
			var image = null;
0 ignored issues
show
Unused Code introduced by
The assignment to image seems to be never used. If you intend to free memory here, this is not necessary since the variable leaves the scope anyway.
Loading history...
417
			var path = null;
418
			var fileId = null;
0 ignored issues
show
Unused Code introduced by
The assignment to fileId seems to be never used. If you intend to free memory here, this is not necessary since the variable leaves the scope anyway.
Loading history...
419
			var mimeType = null;
0 ignored issues
show
Unused Code introduced by
The assignment to mimeType seems to be never used. If you intend to free memory here, this is not necessary since the variable leaves the scope anyway.
Loading history...
420
			var mTime = null;
0 ignored issues
show
Unused Code introduced by
The assignment to mTime seems to be never used. If you intend to free memory here, this is not necessary since the variable leaves the scope anyway.
Loading history...
421
			var etag = null;
422
			var size = null;
423
			var sharedWithUser = null;
0 ignored issues
show
Unused Code introduced by
The assignment to sharedWithUser seems to be never used. If you intend to free memory here, this is not necessary since the variable leaves the scope anyway.
Loading history...
424
			var owner = null;
425
			var permissions = 0;
426
			var currentLocation = data.albumpath;
427
			// This adds a new node to the map for each parent album
428
			Gallery._mapStructure(currentLocation);
429
			var files = data.files;
430
			if (files.length > 0) {
431
				var subAlbumCache = {};
432
				var albumCache = Gallery.albumMap[currentLocation]
433
					= new Album(
434
					currentLocation,
435
					[],
436
					[],
437
					OC.basename(currentLocation),
438
					data.albums[currentLocation].nodeid,
439
					data.albums[currentLocation].mtime,
440
					data.albums[currentLocation].etag,
441
					data.albums[currentLocation].size,
442
					data.albums[currentLocation].sharedwithuser,
443
					data.albums[currentLocation].owner,
444
					data.albums[currentLocation].freespace,
445
					data.albums[currentLocation].permissions
446
				);
447
				for (var i = 0; i < files.length; i++) {
448
					path = files[i].path;
449
					fileId = files[i].nodeid;
450
					mimeType = files[i].mimetype;
451
					mTime = files[i].mtime;
452
					etag = files[i].etag;
453
					size = files[i].size;
454
					sharedWithUser = files[i].sharedwithuser;
455
					owner = files[i].owner;
456
					permissions = files[i].permissions;
457
458
					image =
459
						new GalleryImage(
460
							path, path, fileId, mimeType, mTime, etag, size, sharedWithUser, owner, permissions
461
						);
462
463
					// Determines the folder name for the image
464
					var dir = OC.dirname(path);
465
					if (dir === path) {
466
						dir = '';
467
					}
468
					if (dir === currentLocation) {
469
						// The image belongs to the current album, so we can add it directly
470
						albumCache.images.push(image);
471
					} else {
472
						// The image belongs to a sub-album, so we create a sub-album cache if it
473
						// doesn't exist and add images to it
474
						if (!subAlbumCache[dir]) {
475
							subAlbumCache[dir] = new Album(
476
								dir,
477
								[],
478
								[],
479
								OC.basename(dir),
480
								data.albums[dir].nodeid,
481
								data.albums[dir].mtime,
482
								data.albums[dir].etag,
483
								data.albums[dir].size,
484
								data.albums[dir].sharedwithuser,
485
								data.albums[currentLocation].owner,
486
								data.albums[currentLocation].freespace,
487
								data.albums[dir].permissions);
488
						}
489
						subAlbumCache[dir].images.push(image);
490
						// The sub-album also has to be added to the global map
491
						if (!Gallery.albumMap[dir]) {
492
							Gallery.albumMap[dir] = {};
493
						}
494
					}
495
					Gallery.imageMap[image.path] = image;
496
				}
497
				// Adds the sub-albums to the current album
498
				Gallery._mapAlbums(albumCache, subAlbumCache);
499
500
				// Caches the information which is not already cached
501
				albumCache.etag = data.albums[currentLocation].etag;
502
				albumCache.imageMap = Gallery.imageMap;
503
			}
504
		},
505
506
		/**
507
		 * Adds every album leading to the current folder to a global album map
508
		 *
509
		 * Per example, if you have Root/Folder1/Folder2/CurrentFolder then the map will contain:
510
		 *    * Root
511
		 *    * Folder1
512
		 *    * Folder2
513
		 *    * CurrentFolder
514
		 *
515
		 *  Every time a new location is loaded, the map is completed
516
		 *
517
		 *
518
		 * @param {string} path
519
		 *
520
		 * @returns {Album}
521
		 * @private
522
		 */
523
		_mapStructure: function (path) {
524
			if (!Gallery.albumMap[path]) {
525
				Gallery.albumMap[path] = {};
526
				// Builds relationships between albums
527
				if (path !== '') {
528
					var parent = OC.dirname(path);
529
					if (parent === path) {
530
						parent = '';
531
					}
532
					Gallery._mapStructure(parent);
533
				}
534
			}
535
			return Gallery.albumMap[path];
536
		},
537
538
		/**
539
		 * Adds the sub-albums to the current album
540
		 *
541
		 * @param {Album} albumCache
542
		 * @param {{Album}} subAlbumCache
543
		 * @private
544
		 */
545
		_mapAlbums: function (albumCache, subAlbumCache) {
546
			for (var j = 0, keys = Object.keys(subAlbumCache); j <
547
			keys.length; j++) {
548
				albumCache.subAlbums.push(subAlbumCache[keys[j]]);
549
			}
550
		},
551
552
		/**
553
		 * Saves the folder to a remote server
554
		 *
555
		 * Our location is the remote for the other server
556
		 *
557
		 * @param {string} remote
558
		 * @param {string}token
559
		 * @param {string}owner
560
		 * @param {string}name
561
		 * @param {boolean} isProtected
562
		 * @private
563
		 */
564
		_saveToServer: function (remote, token, owner, name, isProtected) {
565
			var location = window.location.protocol + '//' + window.location.host + OC.webroot;
566
			var isProtectedInt = (isProtected) ? 1 : 0;
567
			var url = remote + '/index.php/apps/files#' + 'remote=' + encodeURIComponent(location)
568
				+ "&token=" + encodeURIComponent(token) + "&owner=" + encodeURIComponent(owner) +
569
				"&name=" +
570
				encodeURIComponent(name) + "&protected=" + isProtectedInt;
571
572
			if (remote.indexOf('://') > 0) {
573
				OC.redirect(url);
574
			} else {
575
				// if no protocol is specified, we automatically detect it by testing https and
576
				// http
577
				// this check needs to happen on the server due to the Content Security Policy
578
				// directive
579
				$.get(OC.generateUrl('apps/files_sharing/testremote'),
580
					{remote: remote}).then(function (protocol) {
581
					if (protocol !== 'http' && protocol !== 'https') {
582
						OC.dialogs.alert(t('files_sharing',
583
							'No compatible server found at {remote}',
584
							{remote: remote}),
585
							t('files_sharing', 'Invalid server url'));
586
					} else {
587
						OC.redirect(protocol + '://' + url);
588
					}
589
				});
590
			}
591
		},
592
593
		/**
594
		 * Removes the moved element from the UI and refreshes the view
595
		 *
596
		 * @param {string} dir
597
		 * @param {string}filePath
598
		 * @param {jQuery} $item
599
		 * @private
600
		 */
601
		_removeElement: function (dir, filePath, $item) {
602
			var images = Gallery.albumMap[Gallery.currentAlbum].images;
603
			var albums = Gallery.albumMap[Gallery.currentAlbum].subAlbums;
604
			// if still viewing the same directory
605
			if (Gallery.currentAlbum === dir) {
606
				var removed = false;
607
				// We try to see if an image was removed
608
				var movedImage = _(images).findIndex({path: filePath});
609
				if (movedImage >= 0) {
610
					images.splice(movedImage, 1);
611
					removed = true;
612
				} else {
613
					// It wasn't an image, so try to remove an album
614
					var movedAlbum = _(albums).findIndex({path: filePath});
615
					if (movedAlbum >= 0) {
616
						albums.splice(movedAlbum, 1);
617
						removed = true;
618
					}
619
				}
620
621
				if (removed) {
622
					$item.remove();
623
					// Refresh the photowall without checking if new files have arrived in the
624
					// current album
625
					// TODO On the next visit this album is going to be reloaded, unless we can get
626
					// an etag back from the move endpoint
627
					Gallery.view.init(Gallery.currentAlbum);
628
				}
629
			}
630
		}
631
	};
632
	window.Gallery = Gallery;
633
})(jQuery, OC, t);
634