From b063ddb05b71988d92fe140ca003ad58301cfcc8 Mon Sep 17 00:00:00 2001
From: Vincent Petry <pvince81@owncloud.com>
Date: Fri, 22 Jan 2016 17:30:18 +0100
Subject: [PATCH] Share dialog use OCS API

---
 core/js/share.js                              |   7 -
 core/js/sharedialogexpirationview.js          |  10 +-
 core/js/sharedialoglinkshareview.js           |  22 +-
 core/js/sharedialogshareelistview.js          |  60 +--
 core/js/shareitemmodel.js                     | 333 ++++++-------
 .../tests/specs/sharedialogshareelistview.js  |  10 +-
 core/js/tests/specs/sharedialogviewSpec.js    | 238 +++------
 core/js/tests/specs/shareitemmodelSpec.js     | 468 +++++++++++++-----
 8 files changed, 611 insertions(+), 537 deletions(-)

diff --git a/core/js/share.js b/core/js/share.js
index e14e19a2543..9baa34d9bb7 100644
--- a/core/js/share.js
+++ b/core/js/share.js
@@ -387,13 +387,6 @@ OC.Share = _.extend(OC.Share || {}, {
 			}
 		});
 	},
-	setPermissions:function(itemType, itemSource, shareType, shareWith, permissions) {
-		$.post(OC.filePath('core', 'ajax', 'share.php'), { action: 'setPermissions', itemType: itemType, itemSource: itemSource, shareType: shareType, shareWith: shareWith, permissions: permissions }, function(result) {
-			if (!result || result.status !== 'success') {
-				OC.dialogs.alert(t('core', 'Error while changing permissions'), t('core', 'Error'));
-			}
-		});
-	},
 	showDropDown:function(itemType, itemSource, appendTo, link, possiblePermissions, filename) {
 		var configModel = new OC.Share.ShareConfigModel();
 		var attributes = {itemType: itemType, itemSource: itemSource, possiblePermissions: possiblePermissions};
diff --git a/core/js/sharedialogexpirationview.js b/core/js/sharedialogexpirationview.js
index eef440c9af5..fa5c0c00986 100644
--- a/core/js/sharedialogexpirationview.js
+++ b/core/js/sharedialogexpirationview.js
@@ -93,8 +93,9 @@
 			this.$el.find('.expirationDateContainer').toggleClass('hidden', !state);
 			if (!state) {
 				// discard expiration date
-				this.model.setExpirationDate('');
-				this.model.saveLinkShare();
+				this.model.saveLinkShare({
+					expireDate: ''
+				});
 			}
 		},
 
@@ -103,8 +104,9 @@
 			$target.tooltip('hide');
 			$target.removeClass('error');
 
-			this.model.setExpirationDate($target.val());
-			this.model.saveLinkShare(null, {
+			this.model.saveLinkShare({
+				expiration: moment($target.val(), 'DD-MM-YYYY').format('YYYY-MM-DD')
+			}, {
 				error: function(model, message) {
 					if (!message) {
 						$target.attr('title', t('core', 'Error setting expiration date'));
diff --git a/core/js/sharedialoglinkshareview.js b/core/js/sharedialoglinkshareview.js
index 452599fb7f2..efae618ba99 100644
--- a/core/js/sharedialoglinkshareview.js
+++ b/core/js/sharedialoglinkshareview.js
@@ -157,8 +157,9 @@
 		onShowPasswordClick: function() {
 			this.$el.find('.linkPass').slideToggle(OC.menuSpeed);
 			if(!this.$el.find('.showPasswordCheckbox').is(':checked')) {
-				this.model.setPassword('');
-				this.model.saveLinkShare();
+				this.model.saveLinkShare({
+					password: ''
+				});
 			} else {
 				this.$el.find('.linkPassText').focus();
 			}
@@ -171,7 +172,6 @@
 		},
 
 		onPasswordEntered: function() {
-			var self = this;
 			var $loading = this.$el.find('.linkPass .icon-loading-small');
 			if (!$loading.hasClass('hidden')) {
 				// still in process
@@ -189,8 +189,9 @@
 				.removeClass('hidden')
 				.addClass('inlineblock');
 
-			this.model.setPassword(password);
-			this.model.saveLinkShare({}, {
+			this.model.saveLinkShare({
+				password: password
+			}, {
 				error: function(model, msg) {
 					$loading.removeClass('inlineblock').addClass('hidden');
 					$input.addClass('error');
@@ -204,8 +205,15 @@
 		onAllowPublicUploadChange: function() {
 			var $checkbox = this.$('.publicUploadCheckbox');
 			$checkbox.siblings('.icon-loading-small').removeClass('hidden').addClass('inlineblock');
-			this.model.setPublicUpload($checkbox.is(':checked'));
-			this.model.saveLinkShare();
+
+			var permissions = OC.PERMISSION_READ;
+			if($checkbox.is(':checked')) {
+				permissions = OC.PERMISSION_UPDATE | OC.PERMISSION_CREATE | OC.PERMISSION_READ;
+			}
+
+			this.model.saveLinkShare({
+				permissions: permissions
+			});
 		},
 
 		_onEmailPrivateLink: function(event) {
diff --git a/core/js/sharedialogshareelistview.js b/core/js/sharedialogshareelistview.js
index 5728fbfd210..e26421dcaed 100644
--- a/core/js/sharedialogshareelistview.js
+++ b/core/js/sharedialogshareelistview.js
@@ -16,11 +16,7 @@
 	var TEMPLATE =
 			'<ul id="shareWithList" class="shareWithList">' +
 			'{{#each sharees}}' +
-			'    {{#if isCollection}}' +
-			'    <li data-collection="{{collectionID}}">{{text}}</li>' +
-			'    {{/if}}' +
-			'    {{#unless isCollection}}' +
-			'    <li data-share-type="{{shareType}}" data-share-with="{{shareWith}}" title="{{shareWith}}">' +
+			'    <li data-share-id="{{shareId}}" data-share-type="{{shareType}}" data-share-with="{{shareWith}}" title="{{shareWith}}">' +
 			'        <a href="#" class="unshare"><span class="icon-loading-small hidden"></span><img class="svg" alt="{{unshareLabel}}" title="{{unshareLabel}}" src="{{unshareImage}}" /></a>' +
 			'        {{#if avatarEnabled}}' +
 			'        <div class="avatar {{#if modSeed}}imageplaceholderseed{{/if}}" data-username="{{shareWith}}" {{#if modSeed}}data-seed="{{shareWith}} {{shareType}}"{{/if}}></div>' +
@@ -56,7 +52,6 @@
 			'        </div>' +
 			'        {{/unless}}' +
 			'    </li>' +
-			'    {{/unless}}' +
 			'{{/each}}' +
 			'</ul>'
 		;
@@ -81,12 +76,6 @@
 		/** @type {Function} **/
 		_template: undefined,
 
-		/** @type {boolean} **/
-		showLink: true,
-
-		/** @type {object} **/
-		_collections: {},
-
 		events: {
 			'click .unshare': 'onUnshare',
 			'click .permissions': 'onPermissionChange',
@@ -107,23 +96,6 @@
 			});
 		},
 
-		processCollectionShare: function(shareIndex) {
-			var type = this.model.getCollectionType(shareIndex);
-			var id = this.model.getCollectionPath(shareIndex);
-			if(type !== 'file' && type !== 'folder') {
-				id = this.model.getCollectionSource(shareIndex);
-			}
-			var displayName = this.model.getShareWithDisplayName(shareIndex);
-			if(!_.isUndefined(this._collections[id])) {
-				this._collections[id].text = this._collections[id].text + ", " + displayName;
-			} else {
-				this._collections[id] = {};
-				this._collections[id].text = t('core', 'Shared in {item} with {user}', {'item': id, user: displayName});
-				this._collections[id].id = id;
-				this._collections[id].isCollection = true;
-			}
-		},
-
 		/**
 		 *
 		 * @param {OC.Share.Types.ShareInfo} shareInfo
@@ -156,6 +128,7 @@
 				shareWith: shareWith,
 				shareWithDisplayName: shareWithDisplayName,
 				shareType: shareType,
+				shareId: this.model.get('shares')[shareIndex].id,
 				modSeed: shareType !== OC.Share.SHARE_TYPE_USER,
 				isRemoteShare: shareType === OC.Share.SHARE_TYPE_REMOTE
 			});
@@ -187,8 +160,6 @@
 				deletePermission: OC.PERMISSION_DELETE
 			};
 
-			this._collections = {};
-
 			if(!this.model.hasUserShares()) {
 				return [];
 			}
@@ -196,15 +167,10 @@
 			var shares = this.model.get('shares');
 			var list = [];
 			for(var index = 0; index < shares.length; index++) {
-				if(this.model.isCollection(index)) {
-					this.processCollectionShare(index);
-				} else {
-					// first empty {} is necessary, otherwise we get in trouble
-					// with references
-					list.push(_.extend({}, universal, this.getShareeObject(index)));
-				}
+				// first empty {} is necessary, otherwise we get in trouble
+				// with references
+				list.push(_.extend({}, universal, this.getShareeObject(index)));
 			}
-			list = _.union(_.values(this._collections), list);
 
 			return list;
 		},
@@ -244,6 +210,7 @@
 		},
 
 		onUnshare: function(event) {
+			var self = this;
 			var $element = $(event.target);
 			if (!$element.is('a')) {
 				$element = $element.closest('a');
@@ -257,17 +224,24 @@
 			$loading.removeClass('hidden');
 
 			var $li = $element.closest('li');
-			var shareType = $li.data('share-type');
-			var shareWith = $li.attr('data-share-with');
 
-			this.model.removeShare(shareType, shareWith);
+			var shareId = $li.data('share-id');
 
+			self.model.removeShare(shareId)
+				.done(function() {
+					$li.remove();
+				})
+				.fail(function() {
+					$loading.addClass('hidden');
+					OC.Notification.showTemporary(t('core', 'Could not unshare'));
+				});
 			return false;
 		},
 
 		onPermissionChange: function(event) {
 			var $element = $(event.target);
 			var $li = $element.closest('li');
+			var shareId = $li.data('share-id');
 			var shareType = $li.data('share-type');
 			var shareWith = $li.attr('data-share-with');
 
@@ -289,7 +263,7 @@
 				permissions |= $(checkbox).data('permissions');
 			});
 
-			this.model.setPermissions(shareType, shareWith, permissions);
+			this.model.updateShare(shareId, {permissions: permissions});
 		},
 
 		onCrudsToggle: function(event) {
diff --git a/core/js/shareitemmodel.js b/core/js/shareitemmodel.js
index ae3cb0ce2e3..c9fc85c91b2 100644
--- a/core/js/shareitemmodel.js
+++ b/core/js/shareitemmodel.js
@@ -25,13 +25,6 @@
 	 * @property {number} stime share time
 	 */
 
-	/**
-	 * @typedef {object} OC.Share.Types.Collection
-	 * @property {string} item_type
-	 * @property {string} path
-	 * @property {string} item_source TODO: verify
-	 */
-
 	/**
 	 * @typedef {object} OC.Share.Types.Reshare
 	 * @property {string} uid_owner
@@ -51,7 +44,6 @@
 	 * @property {string} share_with
 	 * @property {string} share_with_displayname
 	 * @property {string} mail_send
-	 * @property {OC.Share.Types.Collection|undefined} collection
 	 * @property {Date} expiration optional?
 	 * @property {number} stime optional?
 	 */
@@ -84,6 +76,11 @@
 	 * where the link share is one of them
 	 */
 	var ShareItemModel = OC.Backbone.Model.extend({
+		/**
+		 * @type share id of the link share, if applicable
+		 */
+		_linkShareId: null,
+
 		initialize: function(attributes, options) {
 			if(!_.isUndefined(options.configModel)) {
 				this.configModel = options.configModel;
@@ -110,118 +107,48 @@
 		 * TODO: this should be a separate model
 		 */
 		saveLinkShare: function(attributes, options) {
-			var model = this;
-			var itemType = this.get('itemType');
-			var itemSource = this.get('itemSource');
-
-			// TODO: use backbone's default value mechanism once this is a separate model
-			var requiredAttributes = [
-				{ name: 'password', defaultValue: '' },
-				{ name: 'passwordChanged', defaultValue: false },
-				{ name: 'permissions', defaultValue: OC.PERMISSION_READ },
-				{ name: 'expiration', defaultValue: this.configModel.getDefaultExpirationDateString() }
-			];
-
-			attributes = attributes || {};
-
-			// get attributes from the model and fill in with default values
-			_.each(requiredAttributes, function(attribute) {
-				// a provided options overrides a present value of the link
-				// share. If neither is given, the default value is used.
-				if(_.isUndefined(attribute[attribute.name])) {
-					attributes[attribute.name] = attribute.defaultValue;
-					var currentValue = model.get('linkShare')[attribute.name];
-					if(!_.isUndefined(currentValue)) {
-						attributes[attribute.name] = currentValue;
-					}
-				}
-			});
+			options = options || {};
+			attributes = _.extend({}, attributes);
 
-			var password = {
-				password: attributes.password,
-				passwordChanged: attributes.passwordChanged
-			};
+			var shareId = null;
+			var call;
 
-			OC.Share.share(
-				itemType,
-				itemSource,
-				OC.Share.SHARE_TYPE_LINK,
-				password,
-				attributes.permissions,
-				this.fileInfoModel.get('name'),
-				attributes.expiration,
-				function(result) {
-					if (!result || result.status !== 'success') {
-						model.fetch({
-							success: function() {
-								if (options && _.isFunction(options.success)) {
-									options.success(model);
-								}
-							}
-						});
-					} else {
-						if (options && _.isFunction(options.error)) {
-							options.error(model);
-						}
-					}
-				},
-				function(result) {
-					var msg = t('core', 'Error');
-					if (result.data && result.data.message) {
-						msg = result.data.message;
-					}
+			// oh yeah...
+			if (attributes.expiration) {
+				attributes.expireDate = attributes.expiration;
+				delete attributes.expiration;
+			}
 
-					if (options && _.isFunction(options.error)) {
-						options.error(model, msg);
-					} else {
-						OC.dialogs.alert(msg, t('core', 'Error while sharing'));
-					}
-				}
-			);
-		},
+			if (this.get('linkShare') && this.get('linkShare').isLinkShare) {
+				shareId = this.get('linkShare').id;
 
-		removeLinkShare: function() {
-			this.removeShare(OC.Share.SHARE_TYPE_LINK, '');
-		},
+				// note: update can only update a single value at a time
+				call = this.updateShare(shareId, attributes);
+			} else {
+				attributes = _.defaults(attributes, {
+					password: '',
+					passwordChanged: false,
+					permissions: OC.PERMISSION_READ,
+					expireDate: this.configModel.getDefaultExpirationDateString(),
+					shareType: OC.Share.SHARE_TYPE_LINK
+				});
 
-		/**
-		 * Sets the public upload flag
-		 *
-		 * @param {bool} allow whether public upload is allowed
-		 */
-		setPublicUpload: function(allow) {
-			var permissions = OC.PERMISSION_READ;
-			if(allow) {
-				permissions = OC.PERMISSION_UPDATE | OC.PERMISSION_CREATE | OC.PERMISSION_READ;
+				call = this.addShare(attributes);
 			}
 
-			this.get('linkShare').permissions = permissions;
+			return call;
 		},
 
-		/**
-		 * Sets the expiration date of the public link
-		 *
-		 * @param {string} expiration expiration date
-		 */
-		setExpirationDate: function(expiration) {
-			this.get('linkShare').expiration = expiration;
-		},
-
-		/**
-		 * Set password of the public link share
-		 *
-		 * @param {string} password
-		 */
-		setPassword: function(password) {
-			this.get('linkShare').password = password;
-			this.get('linkShare').passwordChanged = true;
+		removeLinkShare: function() {
+			if (this.get('linkShare')) {
+				return this.removeShare(this.get('linkShare').id);
+			}
 		},
 
 		addShare: function(attributes, options) {
 			var shareType = attributes.shareType;
-			var shareWith = attributes.shareWith;
-			var fileName = this.fileInfoModel.get('name');
 			options = options || {};
+			attributes = _.extend({}, attributes);
 
 			// Default permissions are Edit (CRUD) and Share
 			// Check if these permissions are possible
@@ -243,29 +170,64 @@
 				}
 			}
 
-			var model = this;
-			var itemType = this.get('itemType');
-			var itemSource = this.get('itemSource');
-			OC.Share.share(itemType, itemSource, shareType, shareWith, permissions, fileName, options.expiration, function() {
-				model.fetch();
-			});
-		},
+			attributes.permissions = permissions;
+			if (_.isUndefined(attributes.path)) {
+				attributes.path = this.fileInfoModel.getFullPath();
+			}
 
-		setPermissions: function(shareType, shareWith, permissions) {
-			var itemType = this.get('itemType');
-			var itemSource = this.get('itemSource');
+			var self = this;
+			return $.ajax({
+				type: 'POST',
+				url: this._getUrl('shares'),
+				data: attributes,
+				dataType: 'json'
+			}).done(function() {
+				self.fetch({
+					success: function() {
+						if (_.isFunction(options.success)) {
+							options.success(self);
+						}
+					}
+				});
+			}).fail(function(result) {
+				var msg = t('core', 'Error');
+				if (result.ocs && result.ocs.meta) {
+					msg = result.ocs.meta.message;
+				}
 
-			// TODO: in the future, only set the permissions on the model but don't save directly
-			OC.Share.setPermissions(itemType, itemSource, shareType, shareWith, permissions);
+				if (_.isFunction(options.error)) {
+					options.error(self, msg);
+				} else {
+					OC.dialogs.alert(msg, t('core', 'Error while sharing'));
+				}
+			});
 		},
 
-		removeShare: function(shareType, shareWith) {
-			var model = this;
-			var itemType = this.get('itemType');
-			var itemSource = this.get('itemSource');
+		updateShare: function(shareId, attrs) {
+			var self = this;
+			return $.ajax({
+				type: 'PUT',
+				url: this._getUrl('shares/' + encodeURIComponent(shareId)),
+				data: attrs,
+				dataType: 'json'
+			}).done(function() {
+				self.fetch();
+			});
+		},
 
-			OC.Share.unshare(itemType, itemSource, shareType, shareWith, function() {
-				model.fetch();
+		/**
+		 * Deletes the share with the given id
+		 *
+		 * @param {int} shareId share id
+		 * @return {jQuery}
+		 */
+		removeShare: function(shareId) {
+			var self = this;
+			return $.ajax({
+				type: 'DELETE',
+				url: this._getUrl('shares/' + encodeURIComponent(shareId)),
+			}).done(function() {
+				self.fetch();
 			});
 		},
 
@@ -320,71 +282,6 @@
 			return false;
 		},
 
-		/**
-		 * @param {number} shareIndex
-		 * @returns {string}
-		 */
-		getCollectionType: function(shareIndex) {
-			/** @type OC.Share.Types.ShareInfo **/
-			var share = this.get('shares')[shareIndex];
-			if(!_.isObject(share)) {
-				throw "Unknown Share";
-			} else if(_.isUndefined(share.collection)) {
-				throw "Share is not a collection";
-			}
-
-			return share.collection.item_type;
-		},
-
-		/**
-		 * @param {number} shareIndex
-		 * @returns {string}
-		 */
-		getCollectionPath: function(shareIndex) {
-			/** @type OC.Share.Types.ShareInfo **/
-			var share = this.get('shares')[shareIndex];
-			if(!_.isObject(share)) {
-				throw "Unknown Share";
-			} else if(_.isUndefined(share.collection)) {
-				throw "Share is not a collection";
-			}
-
-			return share.collection.path;
-		},
-
-		/**
-		 * @param {number} shareIndex
-		 * @returns {string}
-		 */
-		getCollectionSource: function(shareIndex) {
-			/** @type OC.Share.Types.ShareInfo **/
-			var share = this.get('shares')[shareIndex];
-			if(!_.isObject(share)) {
-				throw "Unknown Share";
-			} else if(_.isUndefined(share.collection)) {
-				throw "Share is not a collection";
-			}
-
-			return share.collection.item_source;
-		},
-
-		/**
-		 * @param {number} shareIndex
-		 * @returns {boolean}
-		 */
-		isCollection: function(shareIndex) {
-			/** @type OC.Share.Types.ShareInfo **/
-			var share = this.get('shares')[shareIndex];
-			if(!_.isObject(share)) {
-				throw "Unknown Share";
-			}
-			if(_.isUndefined(share.collection)) {
-				return false;
-			}
-			return true;
-		},
-
-
 		/**
 		 * @returns {string}
 		 */
@@ -635,13 +532,64 @@
 				   || this.hasDeletePermission(shareIndex);
 		},
 
+		_getUrl: function(base, params) {
+			params = _.extend({format: 'json'}, params || {});
+			return OC.linkToOCS('apps/files_sharing/api/v1', 2) + base + '?' + OC.buildQueryString(params);
+		},
+
+		_fetchShares: function() {
+			var path = this.fileInfoModel.getFullPath();
+			return $.ajax({
+				type: 'GET',
+				url: this._getUrl('shares', {path: path, reshares: true})
+			});
+		},
+
+		_fetchReshare: function() {
+			// only fetch original share once
+			if (!this._reshareFetched) {
+				var path = this.fileInfoModel.getFullPath();
+				this._reshareFetched = true;
+				return $.ajax({
+					type: 'GET',
+					url: this._getUrl('shares', {path: path, shared_with_me: true})
+				});
+			} else {
+				return $.Deferred().resolve([{
+					ocs: {
+						data: [this.get('reshare')]
+					}
+				}]);
+			}
+		},
+
 		fetch: function() {
 			var model = this;
 			this.trigger('request', this);
-			OC.Share.loadItem(this.get('itemType'), this.get('itemSource'), function(data) {
+
+			var deferred = $.when(
+				this._fetchShares(),
+				this._fetchReshare()
+			);
+			deferred.done(function(data1, data2) {
 				model.trigger('sync', 'GET', this);
-				model.set(model.parse(data));
+				var sharesMap = {};
+				_.each(data1[0].ocs.data, function(shareItem) {
+					sharesMap[shareItem.id] = shareItem;
+				});
+
+				var reshare = false;
+				if (data2[0].ocs.data.length) {
+					reshare = data2[0].ocs.data[0];
+				}
+
+				model.set(model.parse({
+					shares: sharesMap,
+					reshare: reshare
+				}));
 			});
+
+			return deferred;
 		},
 
 		/**
@@ -690,7 +638,7 @@
 		parse: function(data) {
 			if(data === false) {
 				console.warn('no data was returned');
-				trigger('fetchError');
+				this.trigger('fetchError');
 				return {};
 			}
 
@@ -752,6 +700,7 @@
 						}
 						linkShare = {
 							isLinkShare: true,
+							id: share.id,
 							token: share.token,
 							password: share.share_with,
 							link: link,
diff --git a/core/js/tests/specs/sharedialogshareelistview.js b/core/js/tests/specs/sharedialogshareelistview.js
index 44611d0d964..cef97469753 100644
--- a/core/js/tests/specs/sharedialogshareelistview.js
+++ b/core/js/tests/specs/sharedialogshareelistview.js
@@ -27,7 +27,7 @@ describe('OC.Share.ShareDialogShareeListView', function () {
 	var configModel;
 	var shareModel;
 	var listView;
-	var setPermissionsStub;
+	var updateShareStub;
 
 	beforeEach(function () {
 		/* jshint camelcase:false */
@@ -81,7 +81,7 @@ describe('OC.Share.ShareDialogShareeListView', function () {
 
 		oldCurrentUser = OC.currentUser;
 		OC.currentUser = 'user0';
-		setPermissionsStub = sinon.stub(listView.model, 'setPermissions');
+		updateShareStub = sinon.stub(OC.Share.ShareItemModel.prototype, 'updateShare');
 	});
 
 	afterEach(function () {
@@ -89,7 +89,7 @@ describe('OC.Share.ShareDialogShareeListView', function () {
 		/* jshint camelcase:false */
 		oc_appconfig.core = oldAppConfig;
 		listView.remove();
-		setPermissionsStub.restore();
+		updateShareStub.restore();
 	});
 
 	describe('Manages checkbox events correctly', function () {
@@ -105,7 +105,7 @@ describe('OC.Share.ShareDialogShareeListView', function () {
 			listView.render();
 			listView.$el.find("input[name='edit']").click();
 			expect(listView.$el.find("input[name='update']").is(':checked')).toEqual(true);
-			expect(setPermissionsStub.called).toEqual(true);
+			expect(updateShareStub.calledOnce).toEqual(true);
 		});
 
 		it('Checks edit box when create/update/delete are checked', function () {
@@ -120,7 +120,7 @@ describe('OC.Share.ShareDialogShareeListView', function () {
 			listView.render();
 			listView.$el.find("input[name='update']").click();
 			expect(listView.$el.find("input[name='edit']").is(':checked')).toEqual(true);
-			expect(setPermissionsStub.called).toEqual(true);
+			expect(updateShareStub.calledOnce).toEqual(true);
 		});
 
 		it('shows cruds checkboxes when toggled', function () {
diff --git a/core/js/tests/specs/sharedialogviewSpec.js b/core/js/tests/specs/sharedialogviewSpec.js
index 63cfe5299ae..6899e625c45 100644
--- a/core/js/tests/specs/sharedialogviewSpec.js
+++ b/core/js/tests/specs/sharedialogviewSpec.js
@@ -28,8 +28,10 @@ describe('OC.Share.ShareDialogView', function() {
 	var avatarStub;
 	var placeholderStub;
 	var oldCurrentUser;
+	var saveLinkShareStub;
 
 	var fetchStub;
+	var notificationStub;
 
 	var configModel;
 	var shareModel;
@@ -46,6 +48,7 @@ describe('OC.Share.ShareDialogView', function() {
 		oc_appconfig.core.enforcePasswordForPublicLink = false;
 
 		fetchStub = sinon.stub(OC.Share.ShareItemModel.prototype, 'fetch');
+		saveLinkShareStub = sinon.stub(OC.Share.ShareItemModel.prototype, 'saveLinkShare');
 
 		fileInfoModel = new OCA.Files.FileInfoModel({
 			id: 123,
@@ -116,6 +119,7 @@ describe('OC.Share.ShareDialogView', function() {
 
 		dialog.remove();
 		fetchStub.restore();
+		saveLinkShareStub.restore();
 
 		autocompleteStub.restore();
 		avatarStub.restore();
@@ -128,55 +132,32 @@ describe('OC.Share.ShareDialogView', function() {
 		it('update password on focus out', function() {
 			$('#allowShareWithLink').val('yes');
 
+			dialog.model.set('linkShare', {
+				isLinkShare: true
+			});
 			dialog.render();
 
-			// Toggle linkshare
-			dialog.$el.find('.linkCheckbox').click();
-			fakeServer.requests[0].respond(
-				200,
-				{ 'Content-Type': 'application/json' },
-				JSON.stringify({data: {token: 'xyz'}, status: 'success'})
-			);
-
 			// Enable password, enter password and focusout
 			dialog.$el.find('[name=showPassword]').click();
 			dialog.$el.find('.linkPassText').focus();
 			dialog.$el.find('.linkPassText').val('foo');
 			dialog.$el.find('.linkPassText').focusout();
 
-			expect(fakeServer.requests[1].method).toEqual('POST');
-			var body = OC.parseQueryString(fakeServer.requests[1].requestBody);
-			expect(body['shareWith[password]']).toEqual('foo');
-			expect(body['shareWith[passwordChanged]']).toEqual('true');
-
-			fetchStub.reset();
-
-			// Set password response
-			fakeServer.requests[1].respond(
-				200,
-				{ 'Content-Type': 'application/json' },
-				JSON.stringify({data: {token: 'xyz'}, status: 'success'})
-			);
-
-			expect(fetchStub.calledOnce).toEqual(true);
-			// fetching the model will rerender the view
-			dialog.render();
-
-			expect(dialog.$el.find('.linkPassText').val()).toEqual('');
-			expect(dialog.$el.find('.linkPassText').attr('placeholder')).toEqual('**********');
+			expect(saveLinkShareStub.calledOnce).toEqual(true);
+			expect(saveLinkShareStub.firstCall.args[0]).toEqual({
+				password: 'foo'
+			});
 		});
 		it('update password on enter', function() {
 			$('#allowShareWithLink').val('yes');
 
+			dialog.model.set('linkShare', {
+				isLinkShare: true
+			});
 			dialog.render();
 
 			// Toggle linkshare
 			dialog.$el.find('.linkCheckbox').click();
-			fakeServer.requests[0].respond(
-				200,
-				{ 'Content-Type': 'application/json' },
-				JSON.stringify({data: {token: 'xyz'}, status: 'success'})
-			);
 
 			// Enable password and enter password
 			dialog.$el.find('[name=showPassword]').click();
@@ -184,26 +165,10 @@ describe('OC.Share.ShareDialogView', function() {
 			dialog.$el.find('.linkPassText').val('foo');
 			dialog.$el.find('.linkPassText').trigger(new $.Event('keyup', {keyCode: 13}));
 
-			expect(fakeServer.requests[1].method).toEqual('POST');
-			var body = OC.parseQueryString(fakeServer.requests[1].requestBody);
-			expect(body['shareWith[password]']).toEqual('foo');
-			expect(body['shareWith[passwordChanged]']).toEqual('true');
-
-			fetchStub.reset();
-
-			// Set password response
-			fakeServer.requests[1].respond(
-				200,
-				{ 'Content-Type': 'application/json' },
-				JSON.stringify({data: {token: 'xyz'}, status: 'success'})
-			);
-
-			expect(fetchStub.calledOnce).toEqual(true);
-			// fetching the model will rerender the view
-			dialog.render();
-
-			expect(dialog.$el.find('.linkPassText').val()).toEqual('');
-			expect(dialog.$el.find('.linkPassText').attr('placeholder')).toEqual('**********');
+			expect(saveLinkShareStub.calledOnce).toEqual(true);
+			expect(saveLinkShareStub.firstCall.args[0]).toEqual({
+				password: 'foo'
+			});
 		});
 		it('shows share with link checkbox when allowed', function() {
 			$('#allowShareWithLink').val('yes');
@@ -241,16 +206,11 @@ describe('OC.Share.ShareDialogView', function() {
 		it('autofocus link text when clicked', function() {
 			$('#allowShareWithLink').val('yes');
 
+			dialog.model.set('linkShare', {
+				isLinkShare: true
+			});
 			dialog.render();
 
-			// Toggle linkshare
-			dialog.$el.find('.linkCheckbox').click();
-			fakeServer.requests[0].respond(
-				200,
-				{ 'Content-Type': 'application/json' },
-				JSON.stringify({data: {token: 'xyz'}, status: 'success'})
-			);
-
 			var focusStub = sinon.stub($.fn, 'focus');
 			var selectStub = sinon.stub($.fn, 'select');
 			dialog.$el.find('.linkText').click();
@@ -603,106 +563,6 @@ describe('OC.Share.ShareDialogView', function() {
 			});
 		});
 	});
-	describe('share permissions', function() {
-		beforeEach(function() {
-			oc_appconfig.core.resharingAllowed = true;
-		});
-
-		/**
-		 * Tests sharing with the given possible permissions
-		 *
-		 * @param {int} possiblePermissions
-		 * @return {int} permissions sent to the server
-		 */
-		function testWithPermissions(possiblePermissions) {
-			shareModel.set({
-				permissions: possiblePermissions,
-				possiblePermissions: possiblePermissions
-			});
-			dialog.render();
-			var autocompleteOptions = autocompleteStub.getCall(0).args[0];
-			// simulate autocomplete selection
-			autocompleteOptions.select(new $.Event('select'), {
-				item: {
-					label: 'User Two',
-					value: {
-						shareType: OC.Share.SHARE_TYPE_USER,
-						shareWith: 'user2'
-					}
-				}
-			});
-			autocompleteStub.reset();
-			var requestBody = OC.parseQueryString(_.last(fakeServer.requests).requestBody);
-			return parseInt(requestBody.permissions, 10);
-		}
-
-		describe('regular sharing', function() {
-			it('shares with given permissions with default config', function() {
-				shareModel.set({
-					reshare: {},
-					shares: []
-				});
-				expect(
-					testWithPermissions(OC.PERMISSION_READ | OC.PERMISSION_UPDATE | OC.PERMISSION_SHARE)
-				).toEqual(OC.PERMISSION_READ | OC.PERMISSION_UPDATE | OC.PERMISSION_SHARE);
-				expect(
-					testWithPermissions(OC.PERMISSION_READ | OC.PERMISSION_SHARE)
-				).toEqual(OC.PERMISSION_READ | OC.PERMISSION_SHARE);
-			});
-			it('removes share permission when not allowed', function() {
-				configModel.set('isResharingAllowed', false);
-				shareModel.set({
-					reshare: {},
-					shares: []
-				});
-				expect(
-					testWithPermissions(OC.PERMISSION_READ | OC.PERMISSION_UPDATE | OC.PERMISSION_SHARE)
-				).toEqual(OC.PERMISSION_READ | OC.PERMISSION_UPDATE);
-			});
-			it('automatically adds READ permission even when not specified', function() {
-				configModel.set('isResharingAllowed', false);
-				shareModel.set({
-					reshare: {},
-					shares: []
-				});
-				expect(
-					testWithPermissions(OC.PERMISSION_UPDATE | OC.PERMISSION_SHARE)
-				).toEqual(OC.PERMISSION_READ | OC.PERMISSION_UPDATE | OC.PERMISSION_UPDATE);
-			});
-			it('does not show sharing options when sharing not allowed', function() {
-				shareModel.set({
-					reshare: {},
-					shares: [],
-					permissions: OC.PERMISSION_READ
-				});
-				dialog.render();
-				expect(dialog.$el.find('.shareWithField').prop('disabled')).toEqual(true);
-			});
-			it('shows reshare owner', function() {
-				shareModel.set({
-					reshare: {
-						uid_owner: 'user1'
-					},
-					shares: [],
-					permissions: OC.PERMISSION_READ
-				});
-				dialog.render();
-				expect(dialog.$el.find('.resharerInfoView .reshare').length).toEqual(1);
-			});
-			it('does not show reshare owner if owner is current user', function() {
-				shareModel.set({
-					reshare: {
-						uid_owner: OC.currentUser
-					},
-					shares: [],
-					permissions: OC.PERMISSION_READ
-				});
-				dialog.render();
-				expect(dialog.$el.find('.resharerInfoView .reshare').length).toEqual(0);
-			});
-		});
-	});
-
 	describe('remote sharing', function() {
 		it('shows remote share info when allowed', function() {
 			configModel.set({
@@ -1093,5 +953,61 @@ describe('OC.Share.ShareDialogView', function() {
 				expect(el.hasClass('user')).toEqual(true);
 			});
 		});
+
+		it('calls addShare after selection', function() {
+			dialog.render();
+			var addShareStub = sinon.stub(shareModel, 'addShare');
+			var autocompleteOptions = autocompleteStub.getCall(0).args[0];
+			autocompleteOptions.select(new $.Event('select'), {
+				item: {
+					label: 'User Two',
+					value: {
+						shareType: OC.Share.SHARE_TYPE_USER,
+						shareWith: 'user2'
+					}
+				}
+			});
+
+			expect(addShareStub.calledOnce).toEqual(true);
+			expect(addShareStub.firstCall.args[0]).toEqual({
+				shareType: OC.Share.SHARE_TYPE_USER,
+				shareWith: 'user2'
+			});
+
+			addShareStub.restore();
+		});
+	});
+	describe('reshare permissions', function() {
+		it('does not show sharing options when sharing not allowed', function() {
+			shareModel.set({
+				reshare: {},
+				shares: [],
+				permissions: OC.PERMISSION_READ
+			});
+			dialog.render();
+			expect(dialog.$el.find('.shareWithField').prop('disabled')).toEqual(true);
+		});
+		it('shows reshare owner', function() {
+			shareModel.set({
+				reshare: {
+					uid_owner: 'user1'
+				},
+				shares: [],
+				permissions: OC.PERMISSION_READ
+			});
+			dialog.render();
+			expect(dialog.$el.find('.resharerInfoView .reshare').length).toEqual(1);
+		});
+		it('does not show reshare owner if owner is current user', function() {
+			shareModel.set({
+				reshare: {
+					uid_owner: OC.currentUser
+				},
+				shares: [],
+				permissions: OC.PERMISSION_READ
+			});
+			dialog.render();
+			expect(dialog.$el.find('.resharerInfoView .reshare').length).toEqual(0);
+		});
 	});
 });
diff --git a/core/js/tests/specs/shareitemmodelSpec.js b/core/js/tests/specs/shareitemmodelSpec.js
index 28daf860393..d696b159e47 100644
--- a/core/js/tests/specs/shareitemmodelSpec.js
+++ b/core/js/tests/specs/shareitemmodelSpec.js
@@ -21,14 +21,20 @@
 
 /* global oc_appconfig */
 describe('OC.Share.ShareItemModel', function() {
-	var loadItemStub;
+	var fetchSharesStub, fetchReshareStub;
+	var fetchSharesDeferred, fetchReshareDeferred;
 	var fileInfoModel, configModel, model;
 	var oldCurrentUser;
 
 	beforeEach(function() {
 		oldCurrentUser = OC.currentUser;
 
-		loadItemStub = sinon.stub(OC.Share, 'loadItem');
+		fetchSharesDeferred = new $.Deferred();
+		fetchSharesStub = sinon.stub(OC.Share.ShareItemModel.prototype, '_fetchShares')
+			.returns(fetchSharesDeferred.promise());
+		fetchReshareDeferred = new $.Deferred();
+		fetchReshareStub = sinon.stub(OC.Share.ShareItemModel.prototype, '_fetchReshare')
+			.returns(fetchReshareDeferred.promise());
 
 		fileInfoModel = new OCA.Files.FileInfoModel({
 			id: 123,
@@ -52,27 +58,70 @@ describe('OC.Share.ShareItemModel', function() {
 		});
 	});
 	afterEach(function() {
-		loadItemStub.restore();
+		if (fetchSharesStub) {
+			fetchSharesStub.restore();
+		}
+		if (fetchReshareStub) {
+			fetchReshareStub.restore();
+		}
 		OC.currentUser = oldCurrentUser;
 	});
 
+	function makeOcsResponse(data) {
+		return [{
+			ocs: {
+				data: data
+			}
+		}];
+	}
+
 	describe('Fetching and parsing', function() {
-		it('fetching calls loadItem with the correct arguments', function() {
+		it('fetches both outgoing shares and the current incoming share', function() {
 			model.fetch();
 
-			expect(loadItemStub.calledOnce).toEqual(true);
-			expect(loadItemStub.calledWith('file', 123)).toEqual(true);
+			expect(fetchSharesStub.calledOnce).toEqual(true);
+			expect(fetchReshareStub.calledOnce).toEqual(true);
+		});
+		it('fetches shares for the current path', function() {
+			fetchSharesStub.restore();
+
+			model._fetchShares();
+
+			expect(fakeServer.requests.length).toEqual(1);
+			expect(fakeServer.requests[0].method).toEqual('GET');
+			expect(fakeServer.requests[0].url).toEqual(
+				OC.linkToOCS('apps/files_sharing/api/v1', 2) +
+				'shares?format=json&path=%2Fsubdir%2Fshared_file_name.txt&reshares=true'
+			);
+
+			fetchSharesStub = null;
+		});
+		it('fetches reshare for the current path', function() {
+			fetchReshareStub.restore();
+
+			model._fetchReshare();
+
+			expect(fakeServer.requests.length).toEqual(1);
+			expect(fakeServer.requests[0].method).toEqual('GET');
+			expect(fakeServer.requests[0].url).toEqual(
+				OC.linkToOCS('apps/files_sharing/api/v1', 2) +
+				'shares?format=json&path=%2Fsubdir%2Fshared_file_name.txt&shared_with_me=true'
+			);
+
+			fetchReshareStub = null;
 		});
 		it('populates attributes with parsed response', function() {
-			loadItemStub.yields({
-				/* jshint camelcase: false */
-				reshare: {
+			/* jshint camelcase: false */
+			fetchReshareDeferred.resolve(makeOcsResponse([
+				{
 					share_type: OC.Share.SHARE_TYPE_USER,
 					uid_owner: 'owner',
 					displayname_owner: 'Owner',
 					permissions: 31
-				},
-				shares: [{
+				}
+			]));
+			fetchSharesDeferred.resolve(makeOcsResponse([
+				{
 					id: 100,
 					item_source: 123,
 					permissions: 31,
@@ -112,8 +161,9 @@ describe('OC.Share.ShareItemModel', function() {
 					storage: 1,
 					token: 'tehtoken',
 					uid_owner: 'root'
-				}]
-			});
+				}
+			]));
+
 			model.fetch();
 
 			var shares = model.get('shares');
@@ -130,10 +180,9 @@ describe('OC.Share.ShareItemModel', function() {
 			// TODO: check more attributes
 		});
 		it('does not parse link share when for a different file', function() {
-			loadItemStub.yields({
-				reshare: [],
-				/* jshint camelcase: false */
-				shares: [{
+			/* jshint camelcase: false */
+			fetchReshareDeferred.resolve(makeOcsResponse([]));
+			fetchSharesDeferred.resolve(makeOcsResponse([{
 					displayname_owner: 'root',
 					expiration: null,
 					file_source: 456,
@@ -152,7 +201,7 @@ describe('OC.Share.ShareItemModel', function() {
 					token: 'tehtoken',
 					uid_owner: 'root'
 				}]
-			});
+			));
 
 			model.fetch();
 
@@ -164,10 +213,9 @@ describe('OC.Share.ShareItemModel', function() {
 			expect(linkShare.isLinkShare).toEqual(false);
 		});
 		it('parses correct link share when a nested link share exists along with parent one', function() {
-			loadItemStub.yields({
-				reshare: [],
-				/* jshint camelcase: false */
-				shares: [{
+			/* jshint camelcase: false */
+			fetchReshareDeferred.resolve(makeOcsResponse([]));
+			fetchSharesDeferred.resolve(makeOcsResponse([{
 					displayname_owner: 'root',
 					expiration: '2015-10-12 00:00:00',
 					file_source: 123,
@@ -204,7 +252,7 @@ describe('OC.Share.ShareItemModel', function() {
 					token: 'anothertoken',
 					uid_owner: 'root'
 				}]
-			});
+			));
 
 			model.fetch();
 
@@ -219,26 +267,26 @@ describe('OC.Share.ShareItemModel', function() {
 			// TODO: check child too
 		});
 		it('reduces reshare permissions to the ones from the original share', function() {
-			loadItemStub.yields({
-				reshare: {
-					permissions: OC.PERMISSION_READ,
-					uid_owner: 'user1'
-				},
-				shares: []
-			});
+			/* jshint camelcase: false */
+			fetchReshareDeferred.resolve(makeOcsResponse([{
+				id: 123,
+				permissions: OC.PERMISSION_READ,
+				uid_owner: 'user1'
+			}]));
+			fetchSharesDeferred.resolve(makeOcsResponse([]));
 			model.fetch();
 
 			// no resharing allowed
 			expect(model.get('permissions')).toEqual(OC.PERMISSION_READ);
 		});
 		it('reduces reshare permissions to possible permissions', function() {
-			loadItemStub.yields({
-				reshare: {
-					permissions: OC.PERMISSION_ALL,
-					uid_owner: 'user1'
-				},
-				shares: []
-			});
+			/* jshint camelcase: false */
+			fetchReshareDeferred.resolve(makeOcsResponse([{
+				id: 123,
+				permissions: OC.PERMISSION_ALL,
+				uid_owner: 'user1'
+			}]));
+			fetchSharesDeferred.resolve(makeOcsResponse([]));
 
 			model.set('possiblePermissions', OC.PERMISSION_READ);
 			model.fetch();
@@ -248,10 +296,8 @@ describe('OC.Share.ShareItemModel', function() {
 		});
 		it('allows owner to share their own share when they are also the recipient', function() {
 			OC.currentUser = 'user1';
-			loadItemStub.yields({
-				reshare: {},
-				shares: []
-			});
+			fetchReshareDeferred.resolve(makeOcsResponse([]));
+			fetchSharesDeferred.resolve(makeOcsResponse([]));
 
 			model.fetch();
 
@@ -259,9 +305,9 @@ describe('OC.Share.ShareItemModel', function() {
 			expect(model.get('permissions') & OC.PERMISSION_SHARE).toEqual(OC.PERMISSION_SHARE);
 		});
 		it('properly parses integer values when the server is in the mood of returning ints as string', function() {
-			loadItemStub.yields({
-				reshare: {},
-				shares: [{
+			/* jshint camelcase: false */
+			fetchReshareDeferred.resolve(makeOcsResponse([]));
+			fetchSharesDeferred.resolve(makeOcsResponse([{
 					displayname_owner: 'root',
 					expiration: '2015-10-12 00:00:00',
 					file_source: '123',
@@ -280,7 +326,7 @@ describe('OC.Share.ShareItemModel', function() {
 					token: 'tehtoken',
 					uid_owner: 'root'
 				}]
-			});
+			));
 
 			model.fetch();
 
@@ -306,55 +352,50 @@ describe('OC.Share.ShareItemModel', function() {
 	});
 	describe('hasUserShares', function() {
 		it('returns false when no user shares exist', function() {
-			loadItemStub.yields({
-				reshare: {},
-				shares: []
-			});
+			fetchReshareDeferred.resolve(makeOcsResponse([]));
+			fetchSharesDeferred.resolve(makeOcsResponse([]));
 
 			model.fetch();
 
 			expect(model.hasUserShares()).toEqual(false);
 		});
 		it('returns true when user shares exist on the current item', function() {
-			loadItemStub.yields({
-				reshare: {},
-				shares: [{
-					id: 1,
-					share_type: OC.Share.SHARE_TYPE_USER,
-					share_with: 'user1',
-					item_source: '123'
-				}]
-			});
+			/* jshint camelcase: false */
+			fetchReshareDeferred.resolve(makeOcsResponse([]));
+			fetchSharesDeferred.resolve(makeOcsResponse([{
+				id: 1,
+				share_type: OC.Share.SHARE_TYPE_USER,
+				share_with: 'user1',
+				item_source: '123'
+			}]));
 
 			model.fetch();
 
 			expect(model.hasUserShares()).toEqual(true);
 		});
 		it('returns true when group shares exist on the current item', function() {
-			loadItemStub.yields({
-				reshare: {},
-				shares: [{
-					id: 1,
-					share_type: OC.Share.SHARE_TYPE_GROUP,
-					share_with: 'group1',
-					item_source: '123'
-				}]
-			});
+			/* jshint camelcase: false */
+			fetchReshareDeferred.resolve(makeOcsResponse([]));
+			fetchSharesDeferred.resolve(makeOcsResponse([{
+				id: 1,
+				share_type: OC.Share.SHARE_TYPE_GROUP,
+				share_with: 'group1',
+				item_source: '123'
+			}]));
 
 			model.fetch();
 
 			expect(model.hasUserShares()).toEqual(true);
 		});
 		it('returns false when share exist on parent item', function() {
-			loadItemStub.yields({
-				reshare: {},
-				shares: [{
-					id: 1,
-					share_type: OC.Share.SHARE_TYPE_GROUP,
-					share_with: 'group1',
-					item_source: '111'
-				}]
-			});
+			/* jshint camelcase: false */
+			fetchReshareDeferred.resolve(makeOcsResponse([]));
+			fetchSharesDeferred.resolve(makeOcsResponse([{
+				id: 1,
+				share_type: OC.Share.SHARE_TYPE_GROUP,
+				share_with: 'group1',
+				item_source: '111'
+			}]));
 
 			model.fetch();
 
@@ -381,27 +422,28 @@ describe('OC.Share.ShareItemModel', function() {
 
 	describe('sendEmailPrivateLink', function() {
 		it('succeeds', function() {
-			loadItemStub.yields({
-				shares: [{
-					displayname_owner: 'root',
-					expiration: null,
-					file_source: 123,
-					file_target: '/folder',
-					id: 20,
-					item_source: '123',
-					item_type: 'folder',
-					mail_send: '0',
-					parent: null,
-					path: '/folder',
-					permissions: OC.PERMISSION_READ,
-					share_type: OC.Share.SHARE_TYPE_LINK,
-					share_with: null,
-					stime: 1403884258,
-					storage: 1,
-					token: 'tehtoken',
-					uid_owner: 'root'
-				}]
-			});
+			/* jshint camelcase: false */
+			fetchReshareDeferred.resolve(makeOcsResponse([]));
+			fetchSharesDeferred.resolve(makeOcsResponse([{
+				displayname_owner: 'root',
+				expiration: null,
+				file_source: 123,
+				file_target: '/folder',
+				id: 20,
+				item_source: '123',
+				item_type: 'folder',
+				mail_send: '0',
+				parent: null,
+				path: '/folder',
+				permissions: OC.PERMISSION_READ,
+				share_type: OC.Share.SHARE_TYPE_LINK,
+				share_with: null,
+				stime: 1403884258,
+				storage: 1,
+				token: 'tehtoken',
+				uid_owner: 'root'
+			}]));
+
 			model.fetch();
 
 			var res = model.sendEmailPrivateLink('foo@bar.com');
@@ -430,27 +472,28 @@ describe('OC.Share.ShareItemModel', function() {
 		});
 
 		it('fails', function() {
-			loadItemStub.yields({
-				shares: [{
-					displayname_owner: 'root',
-					expiration: null,
-					file_source: 123,
-					file_target: '/folder',
-					id: 20,
-					item_source: '123',
-					item_type: 'folder',
-					mail_send: '0',
-					parent: null,
-					path: '/folder',
-					permissions: OC.PERMISSION_READ,
-					share_type: OC.Share.SHARE_TYPE_LINK,
-					share_with: null,
-					stime: 1403884258,
-					storage: 1,
-					token: 'tehtoken',
-					uid_owner: 'root'
-				}]
-			});
+			/* jshint camelcase: false */
+			fetchReshareDeferred.resolve(makeOcsResponse([]));
+			fetchSharesDeferred.resolve(makeOcsResponse([{
+				displayname_owner: 'root',
+				expiration: null,
+				file_source: 123,
+				file_target: '/folder',
+				id: 20,
+				item_source: '123',
+				item_type: 'folder',
+				mail_send: '0',
+				parent: null,
+				path: '/folder',
+				permissions: OC.PERMISSION_READ,
+				share_type: OC.Share.SHARE_TYPE_LINK,
+				share_with: null,
+				stime: 1403884258,
+				storage: 1,
+				token: 'tehtoken',
+				uid_owner: 'root'
+			}]));
+
 			model.fetch();
 
 			var res = model.sendEmailPrivateLink('foo@bar.com');
@@ -478,5 +521,194 @@ describe('OC.Share.ShareItemModel', function() {
 			expect(res.state()).toEqual('rejected');
 		});
 	});
+	describe('share permissions', function() {
+		beforeEach(function() {
+			oc_appconfig.core.resharingAllowed = true;
+		});
+
+		/**
+		 * Tests sharing with the given possible permissions
+		 *
+		 * @param {int} possiblePermissions
+		 * @return {int} permissions sent to the server
+		 */
+		function testWithPermissions(possiblePermissions) {
+			model.set({
+				permissions: possiblePermissions,
+				possiblePermissions: possiblePermissions
+			});
+			model.addShare({
+				shareType: OC.Share.SHARE_TYPE_USER,
+				shareWith: 'user2'
+			});
+
+			var requestBody = OC.parseQueryString(_.last(fakeServer.requests).requestBody);
+			return parseInt(requestBody.permissions, 10);
+		}
+
+		describe('regular sharing', function() {
+			it('shares with given permissions with default config', function() {
+				configModel.set('isResharingAllowed', true);
+				model.set({
+					reshare: {},
+					shares: []
+				});
+				expect(
+					testWithPermissions(OC.PERMISSION_READ | OC.PERMISSION_UPDATE | OC.PERMISSION_SHARE)
+				).toEqual(OC.PERMISSION_READ | OC.PERMISSION_UPDATE | OC.PERMISSION_SHARE);
+				expect(
+					testWithPermissions(OC.PERMISSION_READ | OC.PERMISSION_SHARE)
+				).toEqual(OC.PERMISSION_READ | OC.PERMISSION_SHARE);
+			});
+			it('removes share permission when not allowed', function() {
+				configModel.set('isResharingAllowed', false);
+				model.set({
+					reshare: {},
+					shares: []
+				});
+				expect(
+					testWithPermissions(OC.PERMISSION_READ | OC.PERMISSION_UPDATE | OC.PERMISSION_SHARE)
+				).toEqual(OC.PERMISSION_READ | OC.PERMISSION_UPDATE);
+			});
+			it('automatically adds READ permission even when not specified', function() {
+				configModel.set('isResharingAllowed', false);
+				model.set({
+					reshare: {},
+					shares: []
+				});
+				expect(
+					testWithPermissions(OC.PERMISSION_UPDATE | OC.PERMISSION_SHARE)
+				).toEqual(OC.PERMISSION_READ | OC.PERMISSION_UPDATE | OC.PERMISSION_UPDATE);
+			});
+		});
+	});
+
+	describe('saveLinkShare', function() {
+		var addShareStub;
+		var updateShareStub;
+
+		beforeEach(function() {
+			addShareStub = sinon.stub(model, 'addShare');
+			updateShareStub = sinon.stub(model, 'updateShare');
+		});
+		afterEach(function() { 
+			addShareStub.restore();
+			updateShareStub.restore();
+		});
+
+		it('creates a new share if no link share exists', function() {
+			model.set({
+				linkShare: {
+					isLinkShare: false
+				}
+			});
+
+			model.saveLinkShare();
+
+			expect(addShareStub.calledOnce).toEqual(true);
+			expect(addShareStub.firstCall.args[0]).toEqual({
+				password: '',
+				passwordChanged: false,
+				permissions: OC.PERMISSION_READ,
+				expireDate: '',
+				shareType: OC.Share.SHARE_TYPE_LINK
+			});
+			expect(updateShareStub.notCalled).toEqual(true);
+		});
+		it('creates a new share with default expiration date', function() {
+			var clock = sinon.useFakeTimers(Date.UTC(2015, 6, 17, 1, 2, 0, 3));
+			configModel.set({
+				isDefaultExpireDateEnabled: true,
+				defaultExpireDate: 7
+			});
+			model.set({
+				linkShare: {
+					isLinkShare: false
+				}
+			});
+
+			model.saveLinkShare();
+
+			expect(addShareStub.calledOnce).toEqual(true);
+			expect(addShareStub.firstCall.args[0]).toEqual({
+				password: '',
+				passwordChanged: false,
+				permissions: OC.PERMISSION_READ,
+				expireDate: '2015-7-24 00:00:00',
+				shareType: OC.Share.SHARE_TYPE_LINK
+			});
+			expect(updateShareStub.notCalled).toEqual(true);
+			clock.restore();
+		});
+		it('updates link share if it exists', function() {
+			model.set({
+				linkShare: {
+					isLinkShare: true,
+					id: 123
+				}
+			});
+
+			model.saveLinkShare({
+				password: 'test'
+			});
+
+			expect(addShareStub.notCalled).toEqual(true);
+			expect(updateShareStub.calledOnce).toEqual(true);
+			expect(updateShareStub.firstCall.args[0]).toEqual(123);
+			expect(updateShareStub.firstCall.args[1]).toEqual({
+				password: 'test'
+			});
+		});
+	});
+	describe('creating shares', function() {
+		it('sends POST method to endpoint with passed values', function() {
+			model.addShare({
+				shareType: OC.Share.SHARE_TYPE_GROUP,
+				shareWith: 'group1'
+			});
+
+			expect(fakeServer.requests.length).toEqual(1);
+			expect(fakeServer.requests[0].method).toEqual('POST');
+			expect(fakeServer.requests[0].url).toEqual(
+				OC.linkToOCS('apps/files_sharing/api/v1', 2) +
+				'shares?format=json'
+			);
+			expect(OC.parseQueryString(fakeServer.requests[0].requestBody)).toEqual({
+				path: '/subdir/shared_file_name.txt',
+				permissions: '' + OC.PERMISSION_READ,
+				shareType: '' + OC.Share.SHARE_TYPE_GROUP,
+				shareWith: 'group1'
+			});
+		});
+	});
+	describe('updating shares', function() {
+		it('sends PUT method to endpoint with passed values', function() {
+			model.updateShare(123, {
+				permissions: OC.PERMISSION_READ | OC.PERMISSION_SHARE
+			});
+
+			expect(fakeServer.requests.length).toEqual(1);
+			expect(fakeServer.requests[0].method).toEqual('PUT');
+			expect(fakeServer.requests[0].url).toEqual(
+				OC.linkToOCS('apps/files_sharing/api/v1', 2) +
+				'shares/123?format=json'
+			);
+			expect(OC.parseQueryString(fakeServer.requests[0].requestBody)).toEqual({
+				permissions: '' + (OC.PERMISSION_READ | OC.PERMISSION_SHARE)
+			});
+		});
+	});
+	describe('removing shares', function() {
+		it('sends DELETE method to endpoint with share id', function() {
+			model.removeShare(123);
+
+			expect(fakeServer.requests.length).toEqual(1);
+			expect(fakeServer.requests[0].method).toEqual('DELETE');
+			expect(fakeServer.requests[0].url).toEqual(
+				OC.linkToOCS('apps/files_sharing/api/v1', 2) +
+				'shares/123?format=json'
+			);
+		});
+	});
 });
 
-- 
GitLab