PageSourceSearch

https://informe.mariacorredentora.org/core/js/sharedialogview.js?v=90e8d4483fc0da6fe813d4cc1c297189

js mariacorredentora.org collected 2026-10-06 19:20:14 UTC 20,016 bytes, 675 lines download raw bytes

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		'{{#if isLinkSharingAllowed}}' +
20		'<ul class="subTabHeaders">' +
21		'    <li class="subTabHeader selected subtab-localshare">{{localSharesLabel}}</li>' +
22		'    <li class="subTabHeader subtab-publicshare">{{publicSharesLabel}}</li>' +
23		'</ul>' +
24		'{{/if}}' +
25		'<div class="tabsContainer">' +
26		// TODO: this really should be a separate view class
27		'    <div class="localShareView tab" style="padding-left:0;padding-right:0;">' +
28		'        <label for="shareWith-{{cid}}" class="hidden-visually">{{shareLabel}}</label>' +
29		'        <div class="oneline">' +
30		'            <input id="shareWith-{{cid}}" class="shareWithField" type="text" placeholder="{{sharePlaceholder}}" />' +
31		'            <span class="shareWithLoading icon-loading-small hidden"></span>'+
32		'            {{{remoteShareInfo}}}' +
33		'        </div>' +
34		'        <div class="shareeListView subView"></div>' +
35		'        <div class="shareeTreeUserGroupListView subView"></div>' +
36		'    </div>' +
37		'    <div class="linkShareView tab hidden" style="padding-left:0;padding-right:0;">' +
38		'        <div class="linkListView"></div>' +
39		'        <div class="shareeTreeLinkListView subView"></div>' +
40		'    </div>' +
41		'</div>' +
42		'{{else}}' +
43		'<div class="noSharingPlaceholder">{{noSharingPlaceholder}}</div>' +
44		'{{/if}}' +
45		'<div class="loading hidden" style="height: 50px"></div>';
46
47	var TEMPLATE_REMOTE_SHARE_INFO =
48		'<a target="_blank" class="icon icon-info shareWithRemoteInfo hasTooltip" href="{{docLink}
48}" ' +
49		'title="{{tooltip}}"></a>';
50
51	var TEMPLATE_AUTOCOMPLETE_ITEM =
52		'<li class="{{shareTypeClass}}">' +
53			'<a>' +
54				'<div class="share-autocomplete-item">' +
55					'{{#if showAvatar}}' +
56					'<div class="avatardiv"></div>' +
57					'{{/if}}' +
58					'<div class="autocomplete-item-text">' +
59						'<span class="autocomplete-item-displayname">{{displayName}}</span>' +
60						'{{#if additionalInfo}}' +
61						'<br/><span class="autocomplete-item-additional-info">({{additionalInfo}})</span>' +
62						'{{/if}}' +
63						'<br/><span class="autocomplete-item-typeInfo">{{typeInfo}}</span>' +
64					'</div>' +
65				'</div>' +
66			'</a>' +
67		'</li>';
68
69	/**
70	 * @class OCA.Share.ShareDialogView
71	 * @member {OC.Share.ShareItemModel} model
72	 * @member {jQuery} $el
73	 * @memberof OCA.Sharing
74	 * @classdesc
75	 *
76	 * Represents the GUI of the share dialogue
77	 *
78	 */
79	var ShareDialogView = OC.Backbone.View.extend({
80		/** @type {Object} **/
81		_templates: {},
82
83		/** @type {string} **/
84		tagName: 'div',
85
86		/** @type {OC.Share.ShareConfigModel} **/
87		configModel: undefined,
88
89		/** @type {object} **/
90		resharerInfoView: undefined,
91
92		/** @type {object} **/
93		linkShareView: undefined,
94
95		/** @type {object} **/
96		shareeListView: undefined,
97
98		/** @type {string} **/
99		batchActionSeparator: ',',
100
101		events: {
102			'input .shareWithField': 'onShareWithFieldChanged',
103			'click .subTabHeader': '_onClickTabHeader'
104		},
105
106		initialize: function(options) {
107			var view = this;
108
109			this.model.on('fetchError', function() {
110				OC.Notification.showTemporary(t('core', 'Share details could not be loaded for this item.'));
111			});
112
113			if(!_.isUndefined(options.configModel)) {
114				this.configModel = options.configModel;
115			} else {
116				throw 'missing OC.Share.ShareConfigModel';
117			}
118
119			this.configModel.on('change:isRemoteShareAllowed', function() {
120				view.render();
121			});
122			this.model.on('change:permissions', function() {
123				view.render();
124			});
125
126			this.model.on('request', this._onRequest, this);
127			this.model.on('sync', this._onEndRequest, this);
128
129			var subViewOptions = {
130				model: this.model,
131				configModel: this.configModel
132			};
133
134			var subViews = {
135				resharerInfoView: 'ShareDialogResharerInfoView',
136				shareeListView: 'ShareDialogShareeListView'
137			};
138
139			for(var name in subViews) {
140				var className = subViews[name];
141				this[name] = _.isUndefined(options[name])
142					? new OC.Share[className](subViewOptions)
143					: options[name];
144			}
145
146			this.linkShareView = null;
147
148			_.bindAll(this,
149				'autocompleteHandler',
150				'_onSelectRecipient',
151				'onShareWithFieldChanged'
152			);
153
154			OC.Plugins.attach('OC.Share.ShareDialogView', this);
155		},
156
157		_onClickTabHeader: function(ev) {
158			var $target = $(ev.target);
159			this.$('.subTabHeaders .subTabHeader.selected').removeClass('selected');
160
161			$target.addClass('selected');
162
163			this.$('.localShareView').toggleClass('hidden', !$target.hasClass('subtab-localshare'));
164
165			var $linkShareView = this.$('.linkShareView');
166			var $linkListView = this.$('.linkListView');
167			if ($linkShareView.length) {
168				$linkShareView.toggleClass('hidden', !$target.hasClass('subtab-publicshare'));
169
170				if (!this.linkShareView) {
171					this.linkShareView = new OC.Share.ShareDialogLinkListView({
172						collection: this.model.getLinkSharesCollection(),
173						// pass in the legacy stuff...
174						itemModel: this.model
175					});
176					this.linkShareView.render();
177					$linkListView.html(this.linkShareView.$el);
178				}
179			}
180		},
181
182		onShareWithFieldChanged: function() {
183			var $el = this.$el.find('.shareWithField');
184			if ($el.val().length < 2) {
185				$el.removeClass('error').tooltip('hide');
186			}
187		},
188
189		autocompleteHandler: function (search, response) {
190			var view = this;
191			var $loading = this.$el.find('.shareWithLoading');
192			var trimmedSearch = search.term.trim();
193			$loading.removeClass('hidden');
194			$loading.addClass('inlineblock');
195			$.get(
196				OC.linkToOCS('apps/files_sharing/api/v1') + 'sharees',
197				{
198					format: 'json',
199					search: trimmedSearch,
200					perPage: 200,
201					itemType: view.model.get('itemType')
202				},
203				function (result) {
204					$loading.addClass('hidden');
205					$loading.removeClass('inlineblock');
206					if (result.ocs.meta.statuscode == 100) {
207						var users   = result.ocs.data.exact.users.concat(result.ocs.data.users);
208						var groups  = result.ocs.data.exact.groups.concat(result.ocs.data.groups);
209						var remotes = result.ocs.data.exact.remotes.concat(result.ocs.data.remotes);
210
211						var usersLength;
212						var groupsLength;
213						var remotesLength;
214
215						var i, j;
216
217						//Filter out the current user
218						usersLength = users.length;
219						for (i = 0 ; i < usersLength; i++) {
220							if (users[i].value.shareWith === OC.currentUser) {
221								users.splice(i, 1);
222								break;
223							}
224						}
225
226						// Filter out the owner of the share
227						if (view.model.hasReshare()) {
228							usersLength = users.length;
229							for (i = 0 ; i < usersLength; i++) {
230								if (users[i].value.shareWith === view.model.getReshareOwner()) {
231									users.splice(i, 1);
232									break;
233								}
234							}
235						}
236
237						var shares = view.model.get('shares');
238						var sharesLength = shares.length;
239
240						// Now filter out all sharees that are already shared with
241						for (i = 0; i < sharesLength; i++) {
242							var share = shares[i];
243
244							if (share.share_type === OC.Share.SHARE_TYPE_USER) {
245								usersLength = users.length;
246								for (j = 0; j < usersLength; j++) {
247									if (users[j].value.shareWith === share.share_with) {
248										users.splice(j, 1);
249										break;
250									}
251								}
252							} else if (share.share_type === OC.Share.SHARE_TYPE_GROUP) {
253								groupsLength = groups.length;
254								for (j = 0; j < groupsLength; j++) {
255									if (groups[j].value.shareWith === share.share_with) {
256										groups.splice(j, 1);
257										break;
258									}
259								}
260							} else if (share.share_type === OC.Share.SHARE_TYPE_REMOTE) {
261								remotesLength = remotes.length;
262								for (j = 0; j < remotesLength; j++) {
263									if (remotes[j].value.shareWith === share.share_with) {
264										remotes.splice(j, 1);
265										break;
266									}
267								}
268							}
269						}
270
271						var isBatch = trimmedSearch.indexOf(view.batchActionSeparator) !== -1;
272						var suggestions = users.concat(groups);
273						if (suggestions.length < 1 && !isBatch) {
274							suggestions = suggestions.concat(remotes);
275						}
276
277						if (isBatch) {
278							return view._getUsersForBatchAction(trimmedSearch).then(function (res) {
279								if (res.found.length) {
280									//Filter out the current user
281									for (i = 0; i < res.found.length; i++) {
282										if (res.found[i].shareWith === OC.currentUser) {
283											res.found.splice(i, 1);
284											continue;
285										}
286									}
287								}
288								if (res.found.length) {
289									// Build the label for the list of users to be batch-added
290									var labelArray = [];
291									for (i = 0; i < res.found.length; i++) {
292										labelArray.push(res.found[i].shareWith)
293									}
294
295									suggestions.push({
296										batch: res.found,
297										failedBatch: res.notFound,
298										label: labelArray.join(', '),
299										value: {}
300									});
301								}
302
303								$loading.addClass('hidden');
304								$loading.removeClass('inlineblock');
305								if (suggestions.length) {
306									$('.shareWithField').removeClass('error')
307										.tooltip('hide')
308										.autocomplete("option", "autoFocus", true);
309									return response(suggestions, result);
310								}
311
312								view._displayError(t('core', 'No users or groups found'));
313								return response(undefined, result);
314							})
315						}
316
317						if (suggestions.length > 0) {
318							suggestions.sort(function (a, b) {
319								return OC.Util.naturalSortCompare(a.label, b.label);
320							});
321							$('.shareWithField').removeClass('error')
322								.tooltip('hide')
323								.autocomplete("option", "autoFocus", true);
324							response(suggestions, result);
325						} else {
326							var title = t('core', 'No users or groups found for {search}', {search: $('.shareWithField').val()});
327							if (!view.configModel.get('allowGroupSharing')) {
328								title = t('core', 'No users found for {search}', {search: $('.shareWithField').val()});
329							}
330							var suggestStarts = OC.getCapabilities().files_sharing.search_min_length;
331							if (suggestStarts > $('.shareWithField').val().length) {
332								title = title + '. ' + n(
333									'core',
334									'Please enter at least {chars} character for suggestions',
335									'Please enter at least {chars} characters for suggestions',
336									suggestStarts,
337									{chars: suggestStarts}
338								);
339							}
340							view._displayError(title);
341							response(undefined, result);
342						}
343					} else {
344						response(undefined, result);
345					}
346				}
347			).fail(function() {
348				$loading.addClass('hidden');
349				$loading.removeClass('inlineblock');
350				OC.Notification.show(t('core', 'An error occurred. Please try again'));
351				window.setTimeout(OC.Notification.hide, 5000);
352			});
353		},
354
355		autocompleteRenderItem: function(ul, item) {
356
357			var text = item.label;
358			var typeInfo = t('core', 'User');
359
360			if (item.batch) {
361				typeInfo = this._getBatchActionLabel();
362			}
363			if (item.value.shareType === OC.Share.SHARE_TYPE_GROUP) {
364				typeInfo = t('core', 'Group');
365			}
366			if (item.value.shareType === OC.Share.SHARE_TYPE_GUEST) {
367				typeInfo = t('core', 'Guest');
368			}
369			if (item.value.shareType === OC.Share.SHARE_TYPE_REMOTE) {
370				if (item.value.server) {
371					typeInfo = t('core', 'At {server}', {
372						server: item.value.server
373					});
374				} else {
375					typeInfo = t('core', 'Federated');
376				}
377			}
378			if(item.value.userType === OC.User.USER_TYPE_GUEST){
379				typeInfo = t('core', 'Guest');
380			}
381
382			var template = this._getAutocompleteItemTemplate();
383			var $el = $(template({
384				showAvatar: this.configModel.areAvatarsEnabled(),
385				displayName: text,
386				typeInfo: typeInfo,
387				additionalInfo: item.value.shareWithAdditionalInfo,
388				shareTypeClass: (item.value.shareType === OC.Share.SHARE_TYPE_GROUP) ? 'group' : 'user'
389			}));
390
391			if(this.configModel.areAvatarsEnabled()) {
392				var $avatar = $el.find('.avatardiv');
393				if (item.value.shareType === OC.Share.SHARE_TYPE_USER) {
394					$avatar.avatar(item.value.shareWith, 32, undefined, undefined, undefined, item.label);
395				} else {
396					$avatar.imageplaceholder(text, undefined, 32);
397				}
398			}
399
400			return $el.appendTo(ul);
401		},
402
403		_onSelectRecipient: function(e, s) {
404			e.preventDefault();
405			$(e.target).attr('disabled', true)
406				.val(s.item.label);
407			var $loading = this.$el.find('.shareWithLoading');
408			$loading.removeClass('hidden')
409				.addClass('inlineblock');
410
411			if (s.item.failedBatch && s.item.failedBatch.length) {
412				this._showFailedBatchSharees(s.item.failedBatch);
413			}
414
415			var shares = s.item.batch || [s.item.value];
416			for (var i = 0; i < shares.length; i++) {
417				this.model.addShare(shares[i], {
418					success: function () {
419						$(e.target).val('').attr('disabled', false);
420						$loading.addClass('hidden').removeClass('inlineblock');
421					}, error: function (obj, msg) {
422						OC.Notification.showTemporary(msg);
423						$(e.target).attr('disabled', false).autocomplete('search', $(e.target).val());
424						$loading.addClass('hidden').removeClass('inlineblock');
425					}
426				});
427			}
428		},
429
430		_toggleLoading: function(state) {
431			this._loading = state;
432			this.$el.find('.localShareView, .noSharingPlaceholder').toggleClass('hidden', state);
433			this.$el.find('.loading').toggleClass('hidden', !state);
434		},
435
436		_onRequest: function() {
437			// only show the loading spinner for the first request (for now)
438			if (!this._loadingOnce) {
439				this._toggleLoading(true);
440			}
441		},
442
443		_onEndRequest: function() {
444			var self = this;
445			this._toggleLoading(false);
446			if (!this._loadingOnce) {
447				this._loadingOnce = true;
448				// the first time, focus on the share field after the spinner disappeared
449				if (!bowser.msie) {
450					_.defer(function () {
451						self.$('.shareWithField').focus();
452					});
453				}
454			}
455		},
456
457		render: function() {
458			var baseTemplate = this._getTemplate('base', TEMPLATE_BASE);
459
460			this.$el.html(baseTemplate({
461				cid: this.cid,
462				shareLabel: t('core', 'Share'),
463				sharePlaceholder: this._renderSharePlaceholderPart(),
464				remoteShareInfo: this._renderRemoteShareInfoPart(),
465				isSharingAllowed: this.model.sharePermissionPossible(),
466				isLinkSharingAllowed: this.configModel.isShareWithLinkAllowed(),
467				localSharesLabel: t('core', 'User and Groups'),
468				publicSharesLabel: t('core', 'Public Links'),
469				noSharingPlaceholder: t('core', 'Sharing is not allowed')
470			}));
471
472			var $shareField = this.$el.find('.shareWithField');
473			if ($shareField.length) {
474				$shareField.autocomplete({
475					minLength: 1,
476					delay: 750,
477					focus: function(event) {
478						event.preventDefault();
479					},
480					source: this.autocompleteHandler,
481					select: this._onSelectRecipient,
482					appendTo: '#shareTabView'
483				}).data('ui-autocomplete')._renderItem = _.bind(this.autocompleteRenderItem, this);
484			}
485
486			this.resharerInfoView.$el = this.$el.find('.resharerInfoView');
487			this.resharerInfoView.render();
488
489			var resharingAllowed = this.model.sharePermissionPossible();
490			if (!resharingAllowed || !this.configModel.isShareWithLinkAllowed()) {
491				this.$('.tabHeaders, .linkShareView').remove();
492			}
493
494			this.shareeListView.$el = this.$el.find('.shareeListView');
495			this.shareeListView.render();
496
497			this.$el.find('.hasTooltip').tooltip();
498
499			return this;
500		},
501
502		_renderRemoteShareInfoPart: function() {
503			var remoteShareInfo = '';
504			if(this.configModel.get('isRemoteShareAllowed')) {
505				var infoTemplate = this._getRemoteShareInfoTemplate();
506				remoteShareInfo = infoTemplate({
507					docLink: this.configModel.getFederatedShareDocLink(),
508					tooltip: t('core', 'Share with people on other ownClouds using the syntax [email protected]/owncloud')
509				});
510			}
511
512			return remoteShareInfo;
513		},
514
515		_renderSharePlaceholderPart: function () {
516			var sharePlaceholder = t('core', 'Share with users…');
517
518			if (this.configModel.get('allowGroupSharing')) {
519				if (this.configModel.get('isRemoteShareAllowed')) {
520					sharePlaceholder = t('core', 'Share with users, groups or federated users…');
521				} else {
522					sharePlaceholder = t('core', 'Share with users or groups…')
523				}
524			} else if (this.configModel.get('isRemoteShareAllowed')) {
525					sharePlaceholder = t('core', 'Share with users or federated users…');
526			}
527
528			return sharePlaceholder;
529		},
530
531		/**
532		 *
533		 * @param {string} key - an identifier for the template
534		 * @param {string} template - the HTML to be compiled by Handlebars
535		 * @returns {Function} from Handlebars
536		 * @private
537		 */
538		_getTemplate: function (key, template) {
539			if (!this._templates[key]) {
540				this._templates[key] = Handlebars.compile(template);
541			}
542			return this._templates[key];
543		},
544
545		/**
546		 * returns the info template for remote sharing
547		 *
548		 * @returns {Function}
549		 * @private
550		 */
551		_getRemoteShareInfoTemplate: function() {
552			return this._getTemplate('remoteShareInfo', TEMPLATE_REMOTE_SHARE_INFO);
553		},
554
555		/**
556		 * Returns the autocomplete item template
557		 *
558		 * @returns {Function}
559		 * @private
560		 */
561		_getAutocompleteItemTemplate: function() {
562			return this._getTemplate('autocompleteItem', TEMPLATE_AUTOCOMPLETE_ITEM);
563		},
564
565		/**
566		 * Shows all users with whom the batch share action failed.
567		 *
568		 * @param {Array.<String>} users
569		 * @private
570		 */
571		_showFailedBatchSharees: function(users) {
572			var failedUsersStr = users.join(', ');
573			OC.Notification.show(
574				t('core', 'Could not be shared with the following users: {users}', {users: failedUsersStr}),
575				{type: 'error'}
576			);
577		},
578
579		/**
580		 * Returns the label for the batch action
581		 *
582		 * @returns {string}
583		 * @private
584		 */
585		_getBatchActionLabel: function() {
586			return t('core', 'Add multiple users');
587		},
588
589		/**
590		 * Displays an error, e.g. when the autocomplete doesn't have results.
591		 *
592		 * @param {string} title - title of the error
593		 * @private
594		 */
595		_displayError: function(title) {
596			$('.shareWithField').addClass('error')
597				.attr('data-original-title', title)
598				.tooltip('hide')
599				.tooltip({
600					placement: 'bottom',
601					trigger: 'manual'
602				})
603				.tooltip('fixTitle')
604				.tooltip('show');
605		},
606
607		/**
608		 * Returns a promise which includes all fetched users for batch actions once resolved.
609		 *
610		 * @param {string} search - trimmed search term
611		 * @returns {Promise}
612		 * @private
613		 */
614		_getUsersForBatchAction: function(search) {
615			var view = this;
616			var foundUsers = [];
617			var notFound = [];
618			var promises = [];
619			var users = Array.from(new Set(search.replace(new RegExp(this.batchActionSeparator + '\\s+', 'g'), this.batchActionSeparator).split(this.batchActionSeparator)));
620
621			for (var i = 0; i < users.length; i++) {
622				if (!users[i] || users[i] === OC.currentUser) {
623					continue;
624				}
625				var user = users[i].trim();
626				promises.push(
627					$.ajax({
628						url: OC.linkToOCS('apps/files_sharing/api/v1') + 'sharees',
629						contentType: 'application/json',
630						dataType: 'json',
631						data: {
632							format: 'json',
633							search: user,
634							perPage: 200,
635							itemType: view.model.get('itemType')
636						},
637						context: { user: user }
638					})
639					.done(function (result) {
640						if (result.ocs.data.exact.users.length) {
641							var addShare = true;
642							var shares = view.model.get('shares');
643							if (!shares.length) {
644								foundUsers.push(result.ocs.data.exact.users[0].value);
645								return;
646							}
647
648							// filter out all sharees that are already shared with
649							for (j= 0; j < shares.length; j++) {
650								if (shares[j].share_type === OC.Share.SHARE_TYPE_USER
651									&& result.ocs.data.exact.users[0].value.shareWith === shares[j].share_with) {
652									addShare = false;
653									break;
654								}
655							}
656
657							if (addShare) {
658								foundUsers.push(result.ocs.data.exact.users[0].value);
659							}
660						} else {
661							notFound.push(this.user);
662						}
663					})
664				);
665			}
666
667			return Promise.all(promises).then(function() {
668				return {found: foundUsers, notFound: notFound};
669			});
670		}
671	});
672
673	OC.Share.ShareDialogView = ShareDialogView;
674
675})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.