Completed
Push — stable9 ( 485cb1...e094cf )
by Lukas
26:41 queued 26:23
created

core/js/sharedialogview.js (8 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
/*
2
 * Copyright (c) 2015
3
 *
4
 * This file is licensed under the Affero General Public License version 3
5
 * or later.
6
 *
7
 * See the COPYING-README file.
8
 *
9
 */
10
11
(function() {
12
	if(!OC.Share) {
13
		OC.Share = {};
14
	}
15
16
	var TEMPLATE_BASE =
17
		'<div class="resharerInfoView subView"></div>' +
18
		'{{#if isSharingAllowed}}' +
19
		'<label for="shareWith-{{cid}}" class="hidden-visually">{{shareLabel}}</label>' +
20
		'<div class="oneline">' +
21
		'    <input id="shareWith-{{cid}}" class="shareWithField" type="text" placeholder="{{sharePlaceholder}}" />' +
22
		'    <span class="shareWithLoading icon-loading-small hidden"></span>'+
23
		'{{{remoteShareInfo}}}' +
24
		'</div>' +
25
		'{{/if}}' +
26
		'<div class="shareeListView subView"></div>' +
27
		'<div class="linkShareView subView"></div>' +
28
		'<div class="expirationView subView"></div>' +
29
		'<div class="loading hidden" style="height: 50px"></div>';
30
31
	var TEMPLATE_REMOTE_SHARE_INFO =
32
		'<a target="_blank" class="icon-info svg shareWithRemoteInfo hasTooltip" href="{{docLink}}" ' +
33
		'title="{{tooltip}}"></a>';
34
35
	/**
36
	 * @class OCA.Share.ShareDialogView
37
	 * @member {OC.Share.ShareItemModel} model
38
	 * @member {jQuery} $el
39
	 * @memberof OCA.Sharing
40
	 * @classdesc
41
	 *
42
	 * Represents the GUI of the share dialogue
43
	 *
44
	 */
45
	var ShareDialogView = OC.Backbone.View.extend({
46
		/** @type {Object} **/
47
		_templates: {},
48
49
		/** @type {boolean} **/
50
		_showLink: true,
51
52
		/** @type {string} **/
53
		tagName: 'div',
54
55
		/** @type {OC.Share.ShareConfigModel} **/
56
		configModel: undefined,
57
58
		/** @type {object} **/
59
		resharerInfoView: undefined,
60
61
		/** @type {object} **/
62
		linkShareView: undefined,
63
64
		/** @type {object} **/
65
		expirationView: undefined,
66
67
		/** @type {object} **/
68
		shareeListView: undefined,
69
70
		events: {
71
			'input .shareWithField': 'onShareWithFieldChanged'
72
		},
73
74
		initialize: function(options) {
75
			var view = this;
76
77
			this.model.on('fetchError', function() {
78
				OC.Notification.showTemporary(t('core', 'Share details could not be loaded for this item.'));
79
			});
80
81
			if(!_.isUndefined(options.configModel)) {
82
				this.configModel = options.configModel;
83
			} else {
84
				throw 'missing OC.Share.ShareConfigModel';
85
			}
86
87
			this.configModel.on('change:isRemoteShareAllowed', function() {
88
				view.render();
89
			});
90
			this.model.on('change:permissions', function() {
91
				view.render();
92
			});
93
94
			this.model.on('request', this._onRequest, this);
95
			this.model.on('sync', this._onEndRequest, this);
96
97
			var subViewOptions = {
98
				model: this.model,
99
				configModel: this.configModel
100
			};
101
102
			var subViews = {
103
				resharerInfoView: 'ShareDialogResharerInfoView',
104
				linkShareView: 'ShareDialogLinkShareView',
105
				expirationView: 'ShareDialogExpirationView',
106
				shareeListView: 'ShareDialogShareeListView'
107
			};
108
109
			for(var name in subViews) {
110
				var className = subViews[name];
111
				this[name] = _.isUndefined(options[name])
112
					? new OC.Share[className](subViewOptions)
113
					: options[name];
114
			}
115
116
			_.bindAll(this,
117
				'autocompleteHandler',
118
				'_onSelectRecipient',
119
				'onShareWithFieldChanged'
120
			);
121
		},
122
123
		onShareWithFieldChanged: function() {
124
			var $el = this.$el.find('.shareWithField');
125
			if ($el.val().length < 2) {
126
				$el.removeClass('error').tooltip('hide');
127
			}
128
		},
129
130
		autocompleteHandler: function (search, response) {
131
			var view = this;
132
			var $loading = this.$el.find('.shareWithLoading');
133
			$loading.removeClass('hidden');
134
			$loading.addClass('inlineblock');
135
			$.get(
136
				OC.linkToOCS('apps/files_sharing/api/v1') + 'sharees',
137
				{
138
					format: 'json',
139
					search: search.term.trim(),
140
					perPage: 200,
141
					itemType: view.model.get('itemType')
142
				},
143
				function (result) {
144
					$loading.addClass('hidden');
145
					$loading.removeClass('inlineblock');
146
					if (result.ocs.meta.statuscode == 100) {
147
						var users   = result.ocs.data.exact.users.concat(result.ocs.data.users);
148
						var groups  = result.ocs.data.exact.groups.concat(result.ocs.data.groups);
149
						var remotes = result.ocs.data.exact.remotes.concat(result.ocs.data.remotes);
150
151
						var usersLength;
152
						var groupsLength;
153
						var remotesLength;
154
155
						var i, j;
156
157
						//Filter out the current user
158
						usersLength = users.length;
159
						for (i = 0 ; i < usersLength; i++) {
160
							if (users[i].value.shareWith === OC.currentUser) {
161
								users.splice(i, 1);
162
								break;
163
							}
164
						}
165
166
						// Filter out the owner of the share
167
						if (view.model.hasReshare()) {
168
							usersLength = users.length;
169
							for (i = 0 ; i < usersLength; i++) {
170
								if (users[i].value.shareWith === view.model.getReshareOwner()) {
171
									users.splice(i, 1);
172
									break;
173
								}
174
							}
175
						}
176
177
						var shares = view.model.get('shares');
178
						var sharesLength = shares.length;
179
180
						// Now filter out all sharees that are already shared with
181
						for (i = 0; i < sharesLength; i++) {
182
							var share = shares[i];
183
184
							if (share.share_type === OC.Share.SHARE_TYPE_USER) {
0 ignored issues
show
Identifier 'share_type' is not in camel case.
Loading history...
185
								usersLength = users.length;
186
								for (j = 0; j < usersLength; j++) {
187
									if (users[j].value.shareWith === share.share_with) {
0 ignored issues
show
Identifier 'share_with' is not in camel case.
Loading history...
188
										users.splice(j, 1);
189
										break;
190
									}
191
								}
192
							} else if (share.share_type === OC.Share.SHARE_TYPE_GROUP) {
0 ignored issues
show
Identifier 'share_type' is not in camel case.
Loading history...
193
								groupsLength = groups.length;
194
								for (j = 0; j < groupsLength; j++) {
195
									if (groups[j].value.shareWith === share.share_with) {
0 ignored issues
show
Identifier 'share_with' is not in camel case.
Loading history...
196
										groups.splice(j, 1);
197
										break;
198
									}
199
								}
200
							} else if (share.share_type === OC.Share.SHARE_TYPE_REMOTE) {
0 ignored issues
show
Identifier 'share_type' is not in camel case.
Loading history...
201
								remotesLength = remotes.length;
202
								for (j = 0; j < remotesLength; j++) {
203
									if (remotes[j].value.shareWith === share.share_with) {
0 ignored issues
show
Identifier 'share_with' is not in camel case.
Loading history...
204
										remotes.splice(j, 1);
205
										break;
206
									}
207
								}
208
							}
209
						}
210
211
						var suggestions = users.concat(groups).concat(remotes);
212
213
						if (suggestions.length > 0) {
214
							$('.shareWithField').removeClass('error')
215
								.tooltip('hide')
216
								.autocomplete("option", "autoFocus", true);
217
							response(suggestions);
218
						} else {
219
							var title = t('core', 'No users or groups found for {search}', {search: $('.shareWithField').val()});
220
							if (!view.configModel.get('allowGroupSharing')) {
221
								title = t('core', 'No users found for {search}', {search: $('.shareWithField').val()});
222
							}
223
							$('.shareWithField').addClass('error')
224
								.attr('data-original-title', title)
225
								.tooltip('hide')
226
								.tooltip({
227
									placement: 'bottom',
228
									trigger: 'manual'
229
								})
230
								.tooltip('fixTitle')
231
								.tooltip('show');
232
							response();
233
						}
234
					} else {
235
						response();
236
					}
237
				}
238
			).fail(function() {
239
				$loading.addClass('hidden');
240
				$loading.removeClass('inlineblock');
241
				OC.Notification.show(t('core', 'An error occured. Please try again'));
242
				window.setTimeout(OC.Notification.hide, 5000);
243
			});
244
		},
245
246
		autocompleteRenderItem: function(ul, item) {
247
			var insert = $("<a>");
248
			var text = item.label;
249
			if (item.value.shareType === OC.Share.SHARE_TYPE_GROUP) {
250
				text = t('core', '{sharee} (group)', {
251
					sharee: text
252
				});
253
			} else if (item.value.shareType === OC.Share.SHARE_TYPE_REMOTE) {
254
				if (item.value.server) {
255
					text = t('core', '{sharee} (at {server})', {
256
						sharee: text,
257
						server: item.value.server
258
					});
259
				} else {
260
					text = t('core', '{sharee} (remote)', {
261
						sharee: text
262
					});
263
				}
264
			}
265
			insert.text(text);
266
			insert.attr('title', item.value.shareWith);
267
			if(item.value.shareType === OC.Share.SHARE_TYPE_GROUP) {
268
				insert = insert.wrapInner('<strong></strong>');
269
			}
270
			insert.tooltip({
271
				placement: 'bottom',
272
				container: 'body'
273
			});
274
			return $("<li>")
275
				.addClass((item.value.shareType === OC.Share.SHARE_TYPE_GROUP) ? 'group' : 'user')
276
				.append(insert)
277
				.appendTo(ul);
278
		},
279
280
		_onSelectRecipient: function(e, s) {
281
			e.preventDefault();
282
			$(e.target).attr('disabled', true)
283
				.val(s.item.label);
284
			var $loading = this.$el.find('.shareWithLoading');
285
			$loading.removeClass('hidden')
286
				.addClass('inlineblock');
287
288
			this.model.addShare(s.item.value, {success: function() {
289
				$(e.target).val('')
290
					.attr('disabled', false);
291
				$loading.addClass('hidden')
292
					.removeClass('inlineblock');
293
			}, error: function(obj, msg) {
294
				OC.Notification.showTemporary(msg);
295
				$(e.target).attr('disabled', false)
296
					.autocomplete('search', $(e.target).val());
297
				$loading.addClass('hidden')
298
					.removeClass('inlineblock');
299
			}});
300
		},
301
302
		_toggleLoading: function(state) {
303
			this._loading = state;
304
			this.$el.find('.subView').toggleClass('hidden', state);
305
			this.$el.find('.loading').toggleClass('hidden', !state);
306
		},
307
308
		_onRequest: function() {
309
			// only show the loading spinner for the first request (for now)
310
			if (!this._loadingOnce) {
311
				this._toggleLoading(true);
312
			}
313
		},
314
315
		_onEndRequest: function() {
316
			var self = this;
317
			this._toggleLoading(false);
318
			if (!this._loadingOnce) {
319
				this._loadingOnce = true;
320
				// the first time, focus on the share field after the spinner disappeared
321
				_.defer(function() {
322
					self.$('.shareWithField').focus();
323
				});
324
			}
325
		},
326
327
		render: function() {
328
			var baseTemplate = this._getTemplate('base', TEMPLATE_BASE);
329
330
			this.$el.html(baseTemplate({
331
				cid: this.cid,
332
				shareLabel: t('core', 'Share'),
333
				sharePlaceholder: this._renderSharePlaceholderPart(),
334
				remoteShareInfo: this._renderRemoteShareInfoPart(),
335
				isSharingAllowed: this.model.sharePermissionPossible()
336
			}));
337
338
			var $shareField = this.$el.find('.shareWithField');
339
			if ($shareField.length) {
340
				$shareField.autocomplete({
341
					minLength: 1,
342
					delay: 750,
343
					focus: function(event) {
344
						event.preventDefault();
345
					},
346
					source: this.autocompleteHandler,
347
					select: this._onSelectRecipient
348
				}).data('ui-autocomplete')._renderItem = this.autocompleteRenderItem;
349
			}
350
351
			this.resharerInfoView.$el = this.$el.find('.resharerInfoView');
352
			this.resharerInfoView.render();
353
354
			this.linkShareView.$el = this.$el.find('.linkShareView');
355
			this.linkShareView.render();
356
357
			this.expirationView.$el = this.$el.find('.expirationView');
358
			this.expirationView.render();
359
360
			this.shareeListView.$el = this.$el.find('.shareeListView');
361
			this.shareeListView.render();
362
363
			this.$el.find('.hasTooltip').tooltip();
364
365
			return this;
366
		},
367
368
		/**
369
		 * sets whether share by link should be displayed or not. Default is
370
		 * true.
371
		 *
372
		 * @param {bool} showLink
373
		 */
374
		setShowLink: function(showLink) {
375
			this._showLink = (typeof showLink === 'boolean') ? showLink : true;
376
			this.linkShareView.showLink = this._showLink;
377
		},
378
379
		_renderRemoteShareInfoPart: function() {
380
			var remoteShareInfo = '';
381
			if(this.configModel.get('isRemoteShareAllowed')) {
382
				var infoTemplate = this._getRemoteShareInfoTemplate();
383
				remoteShareInfo = infoTemplate({
384
					docLink: this.configModel.getFederatedShareDocLink(),
385
					tooltip: t('core', 'Share with people on other ownClouds using the syntax [email protected]/owncloud')
0 ignored issues
show
Line is too long.
Loading history...
386
				});
387
			}
388
389
			return remoteShareInfo;
390
		},
391
392
		_renderSharePlaceholderPart: function () {
393
			var sharePlaceholder = t('core', 'Share with users…');
394
395
			if (this.configModel.get('allowGroupSharing')) {
396
				if (this.configModel.get('isRemoteShareAllowed')) {
397
					sharePlaceholder = t('core', 'Share with users, groups or remote users…');
398
				} else {
399
					sharePlaceholder = t('core', 'Share with users or groups…')
0 ignored issues
show
There should be a semicolon.

Requirement of semicolons purely is a coding style issue since JavaScript has specific rules about semicolons which are followed by all browsers.

Further Readings:

Loading history...
400
				}
401
			} else if (this.configModel.get('isRemoteShareAllowed')) {
402
					sharePlaceholder = t('core', 'Share with users or remote users…');
403
			}
404
405
			return sharePlaceholder;
406
		},
407
408
		/**
409
		 *
410
		 * @param {string} key - an identifier for the template
411
		 * @param {string} template - the HTML to be compiled by Handlebars
412
		 * @returns {Function} from Handlebars
413
		 * @private
414
		 */
415
		_getTemplate: function (key, template) {
416
			if (!this._templates[key]) {
417
				this._templates[key] = Handlebars.compile(template);
418
			}
419
			return this._templates[key];
420
		},
421
422
		/**
423
		 * returns the info template for remote sharing
424
		 *
425
		 * @returns {Function}
426
		 * @private
427
		 */
428
		_getRemoteShareInfoTemplate: function() {
429
			return this._getTemplate('remoteShareInfo', TEMPLATE_REMOTE_SHARE_INFO);
430
		}
431
	});
432
433
	OC.Share.ShareDialogView = ShareDialogView;
434
435
})();
436