Completed
Pull Request — master (#328)
by
unknown
02:18
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 2
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
			}
215
		},
216
217
		/**
218
		 * Sends an archive of the current folder to the browser
219
		 *
220
		 * @param event
221
		 */
222
		download: function (event) {
223
			event.preventDefault();
224
225
			var path = $('#content').data('albumname');
226
			var files = Gallery.currentAlbum;
227
			var downloadUrl = Gallery.utility.buildFilesUrl(path, files);
228
229
			OC.redirect(downloadUrl);
230
		},
231
232
		/**
233
		 * Returns the download URL of the given file(s)
234
		 * @param {string} files string or array of file names to download
235
		 * @param {string} [dir] optional directory in which the file name is, defaults to the current directory
236
		 *
237
		 * @see core/apps/files/js/filelist.js
238
		 */
239
		getSelectionDownloadUrl: function(files, dir) {
240
			return OCA.Files.Files.getDownloadUrl(files, dir || Gallery.currentAlbum || '/', true);
241
		},
242
243
		/**
244
		 * Shows an information box to the user
245
		 *
246
		 * @param event
247
		 */
248
		showInfo: function (event) {
249
			event.stopPropagation();
250
			Gallery.infoBox.showInfo();
251
		},
252
253
		/**
254
		 * Lets the user add the shared files to his cloud
255
		 */
256
		showSaveForm: function () {
257
			$(this).hide();
258
			$('.save-form').css('display', 'inline');
259
			$('#remote_address').focus();
260
		},
261
262
		/**
263
		 * Sends the shared files to the viewer's cloud
264
		 *
265
		 * @param event
266
		 */
267
		saveForm: function (event) {
268
			event.preventDefault();
269
270
			var saveElement = $('#save');
271
			var remote = $(this).find('input[type="text"]').val();
272
			var owner = saveElement.data('owner');
273
			var name = saveElement.data('name');
274
			var isProtected = saveElement.data('protected');
275
			Gallery._saveToServer(remote, Gallery.token, owner, name, isProtected);
276
		},
277
278
		/**
279
		 * Creates a new slideshow using the images found in the current folder
280
		 *
281
		 * @param {Array} images
282
		 * @param {string} startImage
283
		 * @param {boolean} autoPlay
284
		 *
285
		 * @returns {boolean}
286
		 */
287
		slideShow: function (images, startImage, autoPlay) {
288
			if (startImage === undefined) {
289
				OC.Notification.showTemporary(t('gallery',
290
					'Aborting preview. Could not find the file'));
291
				return false;
292
			}
293
			var start = images.indexOf(startImage);
294
			images = images.filter(function (image, index) {
295
				// If the slideshow is loaded before we get a thumbnail, we have to accept all
296
				// images
297
				if (!image.thumbnail) {
298
					return image;
299
				} 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...
300
					if (image.thumbnail.valid) {
301
						return image;
302
					} 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...
303
						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...
304
					}
305
				}
306
			}).map(function (image) {
307
				var name = OC.basename(image.path);
308
				var previewUrl = Gallery.utility.getPreviewUrl(image.fileId, image.etag);
309
				var params = {
310
					c: image.etag,
311
					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...
312
				};
313
				var downloadUrl = Gallery.utility.buildGalleryUrl('files',
314
					'/download/' + image.fileId,
315
					params);
316
317
				return {
318
					name: name,
319
					path: image.path,
320
					file: image.fileId,
321
					mimeType: image.mimeType,
322
					permissions: image.permissions,
323
					url: previewUrl,
324
					downloadUrl: downloadUrl
325
				};
326
			});
327
			Gallery.activeSlideShow.setImages(images, autoPlay);
328
			Gallery.activeSlideShow.onStop = function () {
329
				$('#content').show();
330
				Gallery.view.removeLoading();
331
				if (Gallery.currentAlbum !== '') {
332
					// Only modern browsers can manipulate history
333
					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...
334
						history.replaceState('', '',
335
							'#' + encodeURIComponent(Gallery.currentAlbum));
336
					} else {
337
						location.hash = '#' + encodeURIComponent(Gallery.currentAlbum);
338
					}
339
				} else {
340
					// Only modern browsers can manipulate history
341
					if (history && history.replaceState) {
342
						history.replaceState('', '', '#');
343
					} else {
344
						location.hash = '#';
345
					}
346
				}
347
			};
348
			Gallery.activeSlideShow.show(start);
349
			if(!_.isUndefined(Gallery.Share)){
350
				Gallery.Share.hideDropDown();
351
			}
352
			$('.album-info-container').slideUp();
353
			// Resets the last focused element
354
			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...
355
		},
356
357
		/**
358
		 * Moves files and albums to a new location
359
		 *
360
		 * @param {jQuery} $item
361
		 * @param {string} fileName
362
		 * @param {string} filePath
363
		 * @param {jQuery} $target
364
		 * @param {string} targetPath
365
		 */
366
		move: function ($item, fileName, filePath, $target, targetPath) {
367
			var self = this;
368
			var dir = Gallery.currentAlbum;
369
370
			if (targetPath.charAt(targetPath.length - 1) !== '/') {
371
				// make sure we move the files into the target dir,
372
				// not overwrite it
373
				targetPath = targetPath + '/';
374
			}
375
			self.filesClient.move(dir + '/' + fileName, targetPath + fileName)
376
				.done(function () {
377
					self._removeElement(dir, filePath, $item);
378
				})
379
				.fail(function (status) {
380
					if (status === 412) {
381
						// TODO: some day here we should invoke the conflict dialog
382
						OC.Notification.showTemporary(
383
							t('gallery', 'Could not move "{file}", target exists', {file: fileName})
384
						);
385
					} else {
386
						OC.Notification.showTemporary(
387
							t('gallery', 'Could not move "{file}"', {file: fileName})
388
						);
389
					}
390
					$item.fadeTo("normal", 1);
391
					$target.children('.album-loader').hide();
392
				})
393
				.always(function () {
394
					// Nothing?
395
				});
396
		},
397
398
		/**
399
		 * Builds the album's model
400
		 *
401
		 * @param {{
402
		 * 	files:Array,
403
		 * 	albums:Array,
404
		 * 	albumconfig:Object,
405
		 * 	albumpath:String,
406
		 *	updated:Boolean
407
		 * 	}} data
408
		 * @private
409
		 */
410
		_mapFiles: function (data) {
411
			Gallery.imageMap = {};
412
			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...
413
			var path = null;
414
			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...
415
			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...
416
			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...
417
			var etag = null;
418
			var size = null;
419
			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...
420
			var owner = null;
421
			var permissions = 0;
422
			var currentLocation = data.albumpath;
423
			// This adds a new node to the map for each parent album
424
			Gallery._mapStructure(currentLocation);
425
			var files = data.files;
426
			if (files.length > 0) {
427
				var subAlbumCache = {};
428
				var albumCache = Gallery.albumMap[currentLocation]
429
					= new Album(
430
					currentLocation,
431
					[],
432
					[],
433
					OC.basename(currentLocation),
434
					data.albums[currentLocation].nodeid,
435
					data.albums[currentLocation].mtime,
436
					data.albums[currentLocation].etag,
437
					data.albums[currentLocation].size,
438
					data.albums[currentLocation].sharedwithuser,
439
					data.albums[currentLocation].owner,
440
					data.albums[currentLocation].freespace,
441
					data.albums[currentLocation].permissions
442
				);
443
				for (var i = 0; i < files.length; i++) {
444
					path = files[i].path;
445
					fileId = files[i].nodeid;
446
					mimeType = files[i].mimetype;
447
					mTime = files[i].mtime;
448
					etag = files[i].etag;
449
					size = files[i].size;
450
					sharedWithUser = files[i].sharedwithuser;
451
					owner = files[i].owner;
452
					permissions = files[i].permissions;
453
454
					image =
455
						new GalleryImage(
456
							path, path, fileId, mimeType, mTime, etag, size, sharedWithUser, owner, permissions
457
						);
458
459
					// Determines the folder name for the image
460
					var dir = OC.dirname(path);
461
					if (dir === path) {
462
						dir = '';
463
					}
464
					if (dir === currentLocation) {
465
						// The image belongs to the current album, so we can add it directly
466
						albumCache.images.push(image);
467
					} else {
468
						// The image belongs to a sub-album, so we create a sub-album cache if it
469
						// doesn't exist and add images to it
470
						if (!subAlbumCache[dir]) {
471
							subAlbumCache[dir] = new Album(
472
								dir,
473
								[],
474
								[],
475
								OC.basename(dir),
476
								data.albums[dir].nodeid,
477
								data.albums[dir].mtime,
478
								data.albums[dir].etag,
479
								data.albums[dir].size,
480
								data.albums[dir].sharedwithuser,
481
								data.albums[currentLocation].owner,
482
								data.albums[currentLocation].freespace,
483
								data.albums[dir].permissions);
484
						}
485
						subAlbumCache[dir].images.push(image);
486
						// The sub-album also has to be added to the global map
487
						if (!Gallery.albumMap[dir]) {
488
							Gallery.albumMap[dir] = {};
489
						}
490
					}
491
					Gallery.imageMap[image.path] = image;
492
				}
493
				// Adds the sub-albums to the current album
494
				Gallery._mapAlbums(albumCache, subAlbumCache);
495
496
				// Caches the information which is not already cached
497
				albumCache.etag = data.albums[currentLocation].etag;
498
				albumCache.imageMap = Gallery.imageMap;
499
			}
500
		},
501
502
		/**
503
		 * Adds every album leading to the current folder to a global album map
504
		 *
505
		 * Per example, if you have Root/Folder1/Folder2/CurrentFolder then the map will contain:
506
		 *    * Root
507
		 *    * Folder1
508
		 *    * Folder2
509
		 *    * CurrentFolder
510
		 *
511
		 *  Every time a new location is loaded, the map is completed
512
		 *
513
		 *
514
		 * @param {string} path
515
		 *
516
		 * @returns {Album}
517
		 * @private
518
		 */
519
		_mapStructure: function (path) {
520
			if (!Gallery.albumMap[path]) {
521
				Gallery.albumMap[path] = {};
522
				// Builds relationships between albums
523
				if (path !== '') {
524
					var parent = OC.dirname(path);
525
					if (parent === path) {
526
						parent = '';
527
					}
528
					Gallery._mapStructure(parent);
529
				}
530
			}
531
			return Gallery.albumMap[path];
532
		},
533
534
		/**
535
		 * Adds the sub-albums to the current album
536
		 *
537
		 * @param {Album} albumCache
538
		 * @param {{Album}} subAlbumCache
539
		 * @private
540
		 */
541
		_mapAlbums: function (albumCache, subAlbumCache) {
542
			for (var j = 0, keys = Object.keys(subAlbumCache); j <
543
			keys.length; j++) {
544
				albumCache.subAlbums.push(subAlbumCache[keys[j]]);
545
			}
546
		},
547
548
		/**
549
		 * Saves the folder to a remote server
550
		 *
551
		 * Our location is the remote for the other server
552
		 *
553
		 * @param {string} remote
554
		 * @param {string}token
555
		 * @param {string}owner
556
		 * @param {string}name
557
		 * @param {boolean} isProtected
558
		 * @private
559
		 */
560
		_saveToServer: function (remote, token, owner, name, isProtected) {
561
			var location = window.location.protocol + '//' + window.location.host + OC.webroot;
562
			var isProtectedInt = (isProtected) ? 1 : 0;
563
			var url = remote + '/index.php/apps/files#' + 'remote=' + encodeURIComponent(location)
564
				+ "&token=" + encodeURIComponent(token) + "&owner=" + encodeURIComponent(owner) +
565
				"&name=" +
566
				encodeURIComponent(name) + "&protected=" + isProtectedInt;
567
568
			if (remote.indexOf('://') > 0) {
569
				OC.redirect(url);
570
			} else {
571
				// if no protocol is specified, we automatically detect it by testing https and
572
				// http
573
				// this check needs to happen on the server due to the Content Security Policy
574
				// directive
575
				$.get(OC.generateUrl('apps/files_sharing/testremote'),
576
					{remote: remote}).then(function (protocol) {
577
					if (protocol !== 'http' && protocol !== 'https') {
578
						OC.dialogs.alert(t('files_sharing',
579
							'No compatible server found at {remote}',
580
							{remote: remote}),
581
							t('files_sharing', 'Invalid server url'));
582
					} else {
583
						OC.redirect(protocol + '://' + url);
584
					}
585
				});
586
			}
587
		},
588
589
		/**
590
		 * Removes the moved element from the UI and refreshes the view
591
		 *
592
		 * @param {string} dir
593
		 * @param {string}filePath
594
		 * @param {jQuery} $item
595
		 * @private
596
		 */
597
		_removeElement: function (dir, filePath, $item) {
598
			var images = Gallery.albumMap[Gallery.currentAlbum].images;
599
			var albums = Gallery.albumMap[Gallery.currentAlbum].subAlbums;
600
			// if still viewing the same directory
601
			if (Gallery.currentAlbum === dir) {
602
				var removed = false;
603
				// We try to see if an image was removed
604
				var movedImage = _(images).findIndex({path: filePath});
605
				if (movedImage >= 0) {
606
					images.splice(movedImage, 1);
607
					removed = true;
608
				} else {
609
					// It wasn't an image, so try to remove an album
610
					var movedAlbum = _(albums).findIndex({path: filePath});
611
					if (movedAlbum >= 0) {
612
						albums.splice(movedAlbum, 1);
613
						removed = true;
614
					}
615
				}
616
617
				if (removed) {
618
					$item.remove();
619
					// Refresh the photowall without checking if new files have arrived in the
620
					// current album
621
					// TODO On the next visit this album is going to be reloaded, unless we can get
622
					// an etag back from the move endpoint
623
					Gallery.view.init(Gallery.currentAlbum);
624
				}
625
			}
626
		}
627
	};
628
	window.Gallery = Gallery;
629
})(jQuery, OC, t);
630