PageSourceSearch

https://cloud.zambello.it/owncloud/core/js/shareitemmodel.js?v=7371f8fbd38fb04840db67d138f1720b

js zambello.it collected 2026-10-09 00:34:47 UTC 18,394 bytes, 734 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		OC.Share.Types = {};
15	}
16
17	/**
18	 * @typedef {object} OC.Share.Types.LinkShareInfo
19	 * @property {bool} isLinkShare
20	 * @property {string} token
21	 * @property {string|null} password
22	 * @property {string} link
23	 * @property {number} permissions
24	 * @property {Date} expiration
25	 * @property {number} stime share time
26	 */
27
28	/**
29	 * @typedef {object} OC.Share.Types.Reshare
30	 * @property {string} uid_owner
31	 * @property {number} share_type
32	 * @property {string} share_with
33	 * @property {string} displayname_owner
34	 * @property {number} permissions
35	 */
36
37	/**
38	 * @typedef {object} OC.Share.Types.ShareInfo
39	 * @property {number} share_type
40	 * @property {number} permissions
41	 * @property {number} file_source optional
42	 * @property {number} item_source
43	 * @property {string} token
44	 * @property {string} share_with
45	 * @property {string} share_with_displayname
46	 * @property {string} mail_send
47	 * @property {Date} expiration optional?
48	 * @property {number} stime optional?
49	 * @property {string} uid_owner
50	 */
51
52	/**
53	 * @typedef {object} OC.Share.Types.ShareItemInfo
54	 * @property {OC.Share.Types.Reshare} reshare
55	 * @property {OC.Share.Types.ShareInfo[]} shares
56	 * @property {OC.Share.Types.LinkShareInfo|undefined} linkShare
57	 */
58
59	/**
60	 * These properties are sometimes returned by the server as strings instead
61	 * of integers, so we need to convert them accordingly...
62	 */
63	var SHARE_RESPONSE_INT_PROPS = [
64		'id', 'file_parent', 'mail_send', 'file_source', 'item_source', 'permissions',
65		'storage', 'share_type', 'parent', 'stime'
66	];
67
68	/**
69	 * @class OCA.Share.ShareItemModel
70	 * @classdesc
71	 *
72	 * Represents the GUI of the share dialogue
73	 *
74	 * // FIXME: use OC Share API once #17143 is done
75	 *
76	 * // TODO: this really should be a collection of share item models instead,
77	 * where the link share is one of them
78	 */
79	var ShareItemModel = OC.Backbone.Model.extend({
80		/**
81		 * @type share id of the link share, if applicable
82		 */
83		_linkShareId: null,
84
85		_linkSharesCollection: null,
86
87		initialize: function(attributes, options) {
88			if(!_.isUndefined(options.configModel)) {
89				this.configModel = options.configModel;
90			}
91			if(!_.isUndefined(options.fileInfoModel)) {
92				/** @type {OC.Files.FileInfo} **/
93				this.fileInfoModel = options.fileInfoModel;
94			}
95
96			this._linkSharesCollection = new OC.Share.SharesCollection();
97
98			_.bindAll(this, 'addShare');
99		},
100
101		defaults: {
102			permissions: 0,
103			linkShare: {}
104		},
105
106		/**
107		 * Returns the collection of link shares
108		 *
109		 * @return {OC.Share.SharesCollection} shares collection
110		 */
111		getLinkSharesCollection: function() {
112			return this._linkSharesCollection;
113		},
114
115		/**
116		 * Returns the file info for the file/folder on which this share information relate to
117		 *
118		 * @return {OC.Files.FileInfoModel} file info model
119		 */
120		getFileInfo: function() {
121			return this.fileInfoModel;
122		},
123
124		addShare: function(attributes, options) {
125			var shareType = attributes.shareType;
126			options = options || {};
127			attributes = _.extend({}, attributes);
128
129			// Default permissions are Edit (CRUD) and Share
130			// Check if these permissions are possible
131			var permissions = OC.PERMISSION_READ;
132			if (this.updatePermissionPossible()) {
133				permissions = permissions | OC.PERMISSION_UPDATE;
134			}
135			if (this.createPermissionPossible()) {
136				permissions = permissions | OC.PERMISSION_CREATE;
137			}
138			if (this.deletePermissionPossible()) {
139				permissions = permissions | OC.PERMISSION_DELETE;
140			}
141			if (this.configModel.get('isResharingAllowed') && (this.sharePermissionPossible())) {
142				permissions = permissions | OC.PERMISSION_SHARE;
143			}
144
145			attributes.permissions = permissions;
146			if (_.isUndefined(attributes.path)) {
147				attributes.path = this.fileInfoModel.getFullPath();
148			}
149
150			var self = this;
151			return $.ajax({
152				type: 'POST',
153				url: this._getUrl('shares'),
154				data: attributes,
155				dataType: 'json'
156			}).done(function() {
157				self.fetch().done(function() {
158					if (_.isFunction(options.success)) {
159						options.success(self);
160					}
161				});
162			}).fail(function(xhr) {
163				var msg = t('core', 'Error');
164				var result = xhr.responseJSON;
165				if (result && result.ocs && result.ocs.meta) {
166					msg = result.ocs.meta.message;
167				}
168
169				if (_.isFunction(options.error)) {
170					options.error(self, msg);
171				} else {
172					OC.dialogs.alert(msg, t('core', 'Error while sharing'));
173				}
174			});
175		},
176
177		updateShare: function(shareId, attrs, options) {
178			var self = this;
179			options = options || {};
180			return $.ajax({
181				type: 'PUT',
182				url: this._getUrl('shares/' + encodeURIComponent(shareId)),
183				data: attrs,
184				dataType: 'json'
185			}).done(function() {
186				self.fetch({
187					success: function() {
188						if (_.isFunction(options.success)) {
189							options.success(self);
190						}
191					}
192				});
193			}).fail(function(xhr) {
194				var msg = t('core', 'Error');
195				var result = xhr.responseJSON;
196				if (result.ocs && result.ocs.meta) {
197					msg = result.ocs.meta.message;
198				}
199
200				if (_.isFunction(options.error)) {
201					options.error(self, msg);
202				} else {
203					OC.dialogs.alert(msg, t('core', 'Error while sharing'));
204				}
205			});
206		},
207
208		/**
209		 * Deletes the share with the given id
210		 *
211		 * @param {int} shareId share id
212		 * @return {jQuery}
213		 */
214		removeShare: function(shareId, options) {
215			var self = this;
216			options = options || {};
217			return $.ajax({
218				type: 'DELETE',
219				url: this._getUrl('shares/' + encodeURIComponent(shareId)),
220			}).done(function() {
221				self.fetch({
222					success: function() {
223						if (_.isFunction(options.success)) {
224							options.success(self);
225						}
226					}
227				});
228			}).fail(function(xhr) {
229				var msg = t('core', 'Error');
230				var result = xhr.responseJSON;
231				if (result.ocs && result.ocs.meta) {
232					msg = result.ocs.meta.message;
233				}
234
235				if (_.isFunction(options.error)) {
236					options.error(self, msg);
237				} else {
238					OC.dialogs.alert(msg, t('core', 'Error removing share'));
239				}
240			});
241		},
242
243		/**
244		 * @returns {boolean}
245		 */
246		isFolder: function() {
247			return this.get('itemType') === 'folder';
248		},
249
250		/**
251		 * @returns {boolean}
252		 */
253		isFile: function() {
254			return this.get('itemType') === 'file';
255		},
256
257		/**
258		 * whether this item has reshare information
259		 * @returns {boolean}
260		 */
261		hasReshare: function() {
262			var reshare = this.get('reshare');
263			return _.isObject(reshare) && !_.isUndefined(reshare.uid_owner);
264		},
265
266		/**
267		 * whether this item has user share information
268		 * @returns {boolean}
269		 */
270		hasUserShares: function() {
271			return this.getSharesWithCurrentItem().length > 0;
272		},
273
274		/**
275		 * Returns whether this item has a link share
276		 *
277		 * @return {bool} true if a link share exists, false otherwise
278		 */
279		hasLinkShare: function() {
280			return this._linkSharesCollection.length > 0;
281		},
282
283		/**
284		 * @returns {string}
285		 */
286		getReshareOwner: function() {
287			return this.get('reshare').uid_owner;
288		},
289
290		/**
291		 * @returns {string}
292		 */
293		getReshareOwnerDisplayname: function() {
294			return this.get('reshare').displayname_owner;
295		},
296
297		/**
298		 * @returns {string}
299		 */
300		getReshareWith: function() {
301			return this.get('reshare').share_with;
302		},
303
304		/**
305		 * @returns {string}
306		 */
307		getReshareWithDisplayName: function() {
308			var reshare = this.get('reshare');
309			return reshare.share_with_displayname || reshare.share_with;
310		},
311
312		/**
313		 * @returns {number}
314		 */
315		getReshareType: function() {
316			return this.get('reshare').share_type;
317		},
318
319		/**
320		 * Returns all share entries that only apply to the current item
321		 * (file/folder)
322		 *
323		 * @return {Array.<OC.Share.Types.ShareInfo>}
324		 */
325		getSharesWithCurrentItem: function() {
326			var shares = this.get('shares') || [];
327			var fileId = this.fileInfoModel.get('id');
328			return _.filter(shares, function(share) {
329				return share.item_source === fileId;
330			});
331		},
332
333		/**
334		 * @param shareIndex
335		 * @returns {string}
336		 */
337		getShareWith: function(shareIndex) {
338			/** @type OC.Share.Types.ShareInfo **/
339			var share = this.get('shares')[shareIndex];
340			if(!_.isObject(share)) {
341				throw "Unknown Share";
342			}
343			return share.share_with;
344		},
345
346		/**
347		 * @param shareIndex
348		 * @returns {string}
349		 */
350		getShareWithDisplayName: function(shareIndex) {
351			/** @type OC.Share.Types.ShareInfo **/
352			var share = this.get('shares')[shareIndex];
353			if(!_.isObject(share)) {
354				throw "Unknown Share";
355			}
356			return share.share_with_displayname;
357		},
358
359		getShareType: function(shareIndex) {
360			/** @type OC.Share.Types.ShareInfo **/
361			var share = this.get('shares')[shareIndex];
362			if(!_.isObject(share)) {
363				throw "Unknown Share";
364			}
365			return share.share_type;
366		},
367
368		/**
369		 * whether a share from shares has the requested permission
370		 *
371		 * @param {number} shareIndex
372		 * @param {number} permission
373		 * @returns {boolean}
374		 * @private
375		 */
376		_shareHasPermission: function(shareIndex, permission) {
377			/** @type OC.Share.Types.ShareInfo **/
378			var share = this.get('shares')[shareIndex];
379			if(!_.isObject(share)) {
380				throw "Unknown Share";
381			}
382			return (share.permissions & permission) === permission;
383		},
384
385		notificationMailWasSent: function(shareIndex) {
386			/** @type OC.Share.Types.ShareInfo **/
387			var share = this.get('shares')[shareIndex];
388			if(!_.isObject(share)) {
389				throw "Unknown Share";
390			}
391			return share.mail_send === 1;
392		},
393
394		/**
395		 * Sends an email notification for the given share
396		 *
397		 * @param {int} shareType share type
398		 * @param {string} shareWith recipient
399		 * @param {bool} state whether to set the notification flag or remove it
400		 */
401		sendNotificationForShare: function(shareType, shareWith, state) {
402			var itemType = this.get('itemType');
403			var itemSource = this.get('itemSource');
404
405			return $.post(
406				OC.generateUrl('core/ajax/share.php'),
407				{
408					action: state ? 'informRecipients' : 'informRecipientsDisabled',
409					recipient: shareWith,
410					shareType: shareType,
411					itemSource: itemSource,
412					itemType: itemType
413				},
414				function(result) {
415					if (result.status !== 'success') {
416						// FIXME: a model should not show dialogs
417						OC.dialogs.alert(t('core', result.data.message), t('core', 'Warning'));
418					}
419				}
420			);
421		},
422
423		/**
424		 * @returns {boolean}
425		 */
426		sharePermissionPossible: function() {
427			return (this.get('permissions') & OC.PERMISSION_SHARE) === OC.PERMISSION_SHARE;
428		},
429
430		/**
431		 * @param {number} shareIndex
432		 * @returns {boolean}
433		 */
434		hasSharePermission: function(shareIndex) {
435			return this._shareHasPermission(shareIndex, OC.PERMISSION_SHARE);
436		},
437
438		/**
439		 * @returns {boolean}
440		 */
441		createPermissionPossible: function() {
442			return (this.get('permissions') & OC.PERMISSION_CREATE) === OC.PERMISSION_CREATE;
443		},
444
445		/**
446		 * @param {number} shareIndex
447		 * @returns {boolean}
448		 */
449		hasCreatePermission: function(shareIndex) {
450			return this._shareHasPermission(shareIndex, OC.PERMISSION_CREATE);
451		},
452
453		/**
454		 * @returns {boolean}
455		 */
456		updatePermissionPossible: function() {
457			return (this.get('permissions') & OC.PERMISSION_UPDATE) === OC.PERMISSION_UPDATE;
458		},
459
460		/**
461		 * @param {number} shareIndex
462		 * @returns {boolean}
463		 */
464		hasUpdatePermission: function(shareIndex) {
465			return this._shareHasPermission(shareIndex, OC.PERMISSION_UPDATE);
466		},
467
468		/**
469		 * @returns {boolean}
470		 */
471		deletePermissionPossible: function() {
472			return (this.get('permissions') & OC.PERMISSION_DELETE) === OC.PERMISSION_DELETE;
473		},
474
475		/**
476		 * @param {number} shareIndex
477		 * @returns {boolean}
478		 */
479		hasDeletePermission: function(shareIndex) {
480			return this._shareHasPermission(shareIndex, OC.PERMISSION_DELETE);
481		},
482
483		/**
484		 * @returns {boolean}
485		 */
486		editPermissionPossible: function() {
487			return    this.createPermissionPossible()
488				   || this.updatePermissionPossible()
489				   || this.deletePermissionPossible();
490		},
491
492		/**
493		 * @returns {boolean}
494		 */
495		hasEditPermission: function(shareIndex) {
496			return    this.hasCreatePermission(shareIndex)
497				   || this.hasUpdatePermission(shareIndex)
498				   || this.hasDeletePermission(shareIndex);
499		},
500
501		_getUrl: function(base, params) {
502			params = _.extend({format: 'json'}, params || {});
503			return OC.linkToOCS('apps/files_sharing/api/v1', 2) + base + '?' + OC.buildQueryString(params);
504		},
505
506		_fetchShares: function() {
507			var path = this.fileInfoModel.getFullPath();
508			return $.ajax({
509				type: 'GET',
510				url: this._getUrl('shares', {path: path, reshares: true})
511			});
512		},
513
514		_fetchReshare: function() {
515			// only fetch original share once
516			if (!this._reshareFetched) {
517				var path = this.fileInfoModel.getFullPath();
518				this._reshareFetched = true;
519				return $.ajax({
520					type: 'GET',
521					url: this._getUrl('shares', {path: path, shared_with_me: true})
522				});
523			} else {
524				return $.Deferred().resolve([{
525					ocs: {
526						data: [this.get('reshare')]
527					}
528				}]);
529			}
530		},
531
532		/**
533		 * Group reshares into a single super share element.
534		 * Does this by finding the most precise share and
535		 * combines the permissions to be the most permissive.
536		 *
537		 * @param {Array} reshares
538		 * @return {Object} reshare
539		 */
540		_groupReshares: function(reshares) {
541			if (!reshares || !reshares.length) {
542				return false;
543			}
544
545			var superShare = reshares.shift();
546			var combinedPermissions = superShare.permissions;
547			_.each(reshares, function(reshare) {
548				// use share have higher priority than group share
549				if (reshare.share_type === OC.Share.SHARE_TYPE_USER && superShare.share_type === OC.Share.SHARE_TYPE_GROUP) {
550					superShare = reshare;
551				}
552				combinedPermissions |= reshare.permissions;
553			});
554
555			superShare.permissions = combinedPermissions;
556			return superShare;
557		},
558
559		fetch: function() {
560			var model = this;
561			this.trigger('request', this);
562
563			var deferred = $.when(
564				this._fetchShares(),
565				this._fetchReshare()
566			);
567			deferred.done(function(data1, data2) {
568				model.trigger('sync', 'GET', this);
569				var sharesMap = {};
570				_.each(data1[0].ocs.data, function(shareItem) {
571					sharesMap[shareItem.id] = shareItem;
572				});
573
574				var reshare = false;
575				if (data2[0].ocs.data.length) {
576					reshare = model._groupReshares(data2[0].ocs.data);
577				}
578
579				model.set(model.parse({
580					shares: sharesMap,
581					reshare: reshare
582				}));
583			});
584
585			return deferred;
586		},
587
588		/**
589		 * Updates OC.Share.itemShares and OC.Share.statuses.
590		 *
591		 * This is required in case the user navigates away and comes back,
592		 * the share statuses from the old arrays are still used to fill in the icons
593		 * in the file list.
594		 */
595		_legacyFillCurrentShares: function(shares) {
596			var fileId = this.fileInfoModel.get('id');
597			if (!shares || !shares.length) {
598				delete OC.Share.statuses[fileId];
599				OC.Share.currentShares = {};
600				OC.Share.itemShares = [];
601				return;
602			}
603
604			var currentShareStatus = OC.Share.statuses[fileId];
605			if (!currentShareStatus) {
606				currentShareStatus = {link: false};
607				OC.Share.statuses[fileId] = currentShareStatus;
608			}
609			currentShareStatus.link = false;
610
611			OC.Share.currentShares = {};
612			OC.Share.itemShares = [];
613			_.each(shares,
614				/**
615				 * @param {OC.Share.Types.ShareInfo} share
616				 */
617				function(share) {
618					if (share.share_type === OC.Share.SHARE_TYPE_LINK) {
619						OC.Share.itemShares[share.share_type] = true;
620						currentShareStatus.link = true;
621					} else {
622						if (!OC.Share.itemShares[share.share_type]) {
623							OC.Share.itemShares[share.share_type] = [];
624						}
625						OC.Share.itemShares[share.share_type].push(share.share_with);
626					}
627				}
628			);
629		},
630
631		parse: function(data) {
632			if(data === false) {
633				console.warn('no data was returned');
634				this.trigger('fetchError');
635				return {};
636			}
637
638			var permissions = this.get('possiblePermissions');
639			if(!_.isUndefined(data.reshare) && !_.isUndefined(data.reshare.permissions) && data.reshare.uid_owner !== OC.currentUser) {
640				permissions = permissions & data.reshare.permissions;
641			}
642
643			/** @type {OC.Share.Types.ShareInfo[]} **/
644			var shares = _.map(data.shares, function(share) {
645				// properly parse some values because sometimes the server
646				// returns integers as string...
647				var i;
648				for (i = 0; i < SHARE_RESPONSE_INT_PROPS.length; i++) {
649					var prop = SHARE_RESPONSE_INT_PROPS[i];
650					if (!_.isUndefined(share[prop])) {
651						share[prop] = parseInt(share[prop], 10);
652					}
653				}
654				return share;
655			});
656
657			this._legacyFillCurrentShares(shares);
658
659			var linkShares = [];
660			// filter out the share by link
661			shares = _.reject(shares,
662				/**
663				 * @param {OC.Share.Types.ShareInfo} share
664				 */
665				function(share) {
666					var isShareLink =
667						share.share_type === OC.Share.SHARE_TYPE_LINK
668						&& (   share.file_source === this.get('itemSource')
669						|| share.item_source === this.get('itemSource'));
670
671					if (isShareLink) {
672						/*
673						 * Ignore reshared link shares for now
674						 * FIXME: Find a way to display properly
675						 */
676						if (share.uid_owner !== OC.currentUser) {
677							return share;
678						}
679
680						linkShares.push(share);
681						return share;
682					}
683				},
684				this
685			);
686
687			// populate link shares collection with found link shares
688			this._linkSharesCollection.set(linkShares, {parse: true});
689
690			// use the old crappy way for other shares for now
691			return {
692				reshare: data.reshare,
693				shares: shares,
694				permissions: permissions
695			};
696		},
697
698		/**
699		 * Parses a string to an valid integer (unix timestamp)
700		 * @param time
701		 * @returns {*}
702		 * @internal Only used to work around a bug in the backend
703		 */
704		_parseTime: function(time) {
705			if (_.isString(time)) {
706				// skip empty strings and hex values
707				if (time === '' || (time.length > 1 && time[0] === '0' && time[1] === 'x')) {
708					return null;
709				}
710				time = parseInt(time, 10);
711				if(isNaN(time)) {
712					time = null;
713				}
714			}
715			return time;
716		},
717
718		/**
719		 * Returns a list of share types from the existing shares.
720		 *
721		 * @return {Array.<int>} array of share types
722		 */
723		getShareTypes: function() {
724			var result;
725			result = _.pluck(this.getSharesWithCurrentItem(), 'share_type');
726			if (this.hasLinkShare()) {
727				result.push(OC.Share.SHARE_TYPE_LINK);
728			}
729			return _.uniq(result);
730		}
731	});
732
733	OC.Share.ShareItemModel = ShareItemModel;
734})();

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.