diff --git a/apps/files_sharing/js/sharetabview.js b/apps/files_sharing/js/sharetabview.js
index 1ad173659570ba9d00be3112cda4417c7b0dec19..68288500ae0d28ad7801be13aa6039847cd5f075 100644
--- a/apps/files_sharing/js/sharetabview.js
+++ b/apps/files_sharing/js/sharetabview.js
@@ -55,6 +55,7 @@
 					owner: owner
 				}));
 
+				// TODO: the model should read these directly off the passed fileInfoModel
 				var attributes = {
 					itemType: this.model.isDirectory() ? 'folder' : 'file',
 				   	itemSource: this.model.get('id'),
diff --git a/apps/files_sharing/tests/js/shareSpec.js b/apps/files_sharing/tests/js/shareSpec.js
index b6368b901eebafc2e16ee20b15d8bce2bca69709..96a96f1b81466c958da449cee4dd38c5915707d5 100644
--- a/apps/files_sharing/tests/js/shareSpec.js
+++ b/apps/files_sharing/tests/js/shareSpec.js
@@ -97,7 +97,6 @@ describe('OCA.Sharing.Util tests', function() {
 			}]);
 			$tr = fileList.$el.find('tbody tr:first');
 			$action = $tr.find('.action-share');
-			expect($action.hasClass('permanent')).toEqual(true);
 			expect(OC.basename($action.find('img').attr('src'))).toEqual('share.svg');
 			expect(OC.basename(getImageUrl($tr.find('.filename .thumbnail')))).toEqual('folder.svg');
 			expect($action.find('img').length).toEqual(1);
@@ -116,7 +115,6 @@ describe('OCA.Sharing.Util tests', function() {
 			}]);
 			$tr = fileList.$el.find('tbody tr:first');
 			$action = $tr.find('.action-share');
-			expect($action.hasClass('permanent')).toEqual(true);
 			expect($action.find('>span').text().trim()).toEqual('Shared');
 			expect(OC.basename($action.find('img').attr('src'))).toEqual('share.svg');
 			expect(OC.basename(getImageUrl($tr.find('.filename .thumbnail')))).toEqual('folder-shared.svg');
@@ -137,7 +135,6 @@ describe('OCA.Sharing.Util tests', function() {
 			}]);
 			$tr = fileList.$el.find('tbody tr:first');
 			$action = $tr.find('.action-share');
-			expect($action.hasClass('permanent')).toEqual(true);
 			expect($action.find('>span').text().trim()).toEqual('Shared');
 			expect(OC.basename($action.find('img').attr('src'))).toEqual('public.svg');
 			expect(OC.basename(getImageUrl($tr.find('.filename .thumbnail')))).toEqual('folder-public.svg');
@@ -158,7 +155,6 @@ describe('OCA.Sharing.Util tests', function() {
 			}]);
 			$tr = fileList.$el.find('tbody tr:first');
 			$action = $tr.find('.action-share');
-			expect($action.hasClass('permanent')).toEqual(true);
 			expect($action.find('>span').text().trim()).toEqual('User One');
 			expect(OC.basename($action.find('img').attr('src'))).toEqual('share.svg');
 			expect(OC.basename(getImageUrl($tr.find('.filename .thumbnail')))).toEqual('folder-shared.svg');
@@ -178,7 +174,6 @@ describe('OCA.Sharing.Util tests', function() {
 			}]);
 			$tr = fileList.$el.find('tbody tr:first');
 			$action = $tr.find('.action-share');
-			expect($action.hasClass('permanent')).toEqual(true);
 			expect($action.find('>span').text().trim()).toEqual('Shared with User One, User Two');
 			expect(OC.basename($action.find('img').attr('src'))).toEqual('share.svg');
 			expect(OC.basename(getImageUrl($tr.find('.filename .thumbnail')))).toEqual('folder-shared.svg');
@@ -200,7 +195,6 @@ describe('OCA.Sharing.Util tests', function() {
 			$tr = fileList.$el.find('tbody tr:first');
 			expect($tr.find('.action-share').length).toEqual(0);
 			$action = $tr.find('.action-share-notification');
-			expect($action.hasClass('permanent')).toEqual(true);
 			expect($action.find('>span').text().trim()).toEqual('User One');
 			expect(OC.basename($action.find('img').attr('src'))).toEqual('share.svg');
 			expect(OC.basename(getImageUrl($tr.find('.filename .thumbnail')))).toEqual('folder-shared.svg');
@@ -225,7 +219,7 @@ describe('OCA.Sharing.Util tests', function() {
 		});
 	});
 	describe('Share action', function() {
-		var showDropDownStub;
+		var shareTab;
 
 		function makeDummyShareItem(displayName) {
 			return {
@@ -234,12 +228,35 @@ describe('OCA.Sharing.Util tests', function() {
 		}
 
 		beforeEach(function() {
-			showDropDownStub = sinon.stub(OC.Share, 'showDropDown', function() {
-				$('#testArea').append($('<div id="dropdown"></div>'));
-			});
+			// make it look like not the "All files" list
+			fileList.id = 'test';
+			shareTab = fileList._detailsView._tabViews[0];
 		});
 		afterEach(function() {
-			showDropDownStub.restore();
+			shareTab = null;
+		});
+		it('clicking share action opens sidebar and share tab', function() {
+			var showDetailsViewStub = sinon.stub(fileList, 'showDetailsView');
+
+			fileList.setFiles([{
+				id: 1,
+				type: 'file',
+				name: 'One.txt',
+				path: '/subdir',
+				mimetype: 'text/plain',
+				size: 12,
+				permissions: OC.PERMISSION_ALL,
+				etag: 'abc'
+			}]);
+
+			var $tr = fileList.$el.find('tr:first');
+			$tr.find('.action-share').click();
+
+			expect(showDetailsViewStub.calledOnce).toEqual(true);
+			expect(showDetailsViewStub.getCall(0).args[0]).toEqual('One.txt');
+			expect(showDetailsViewStub.getCall(0).args[1]).toEqual('shareTabView');
+
+			showDetailsViewStub.restore();
 		});
 		it('adds share icon after sharing a non-shared file', function() {
 			var $action, $tr;
@@ -257,24 +274,20 @@ describe('OCA.Sharing.Util tests', function() {
 			$action = fileList.$el.find('tbody tr:first .action-share');
 			$tr = fileList.$el.find('tr:first');
 
-			expect($action.hasClass('permanent')).toEqual(true);
-
 			$tr.find('.action-share').click();
 
-			expect(showDropDownStub.calledOnce).toEqual(true);
-
-			// simulate what the dropdown does
-			var shares = {};
-			OC.Share.itemShares[OC.Share.SHARE_TYPE_USER] = ['user1', 'user2'];
-			OC.Share.itemShares[OC.Share.SHARE_TYPE_GROUP] = ['group1', 'group2'];
-			shares[OC.Share.SHARE_TYPE_USER] = _.map(['User One', 'User Two'], makeDummyShareItem);
-			shares[OC.Share.SHARE_TYPE_GROUP] = _.map(['Group One', 'Group Two'], makeDummyShareItem);
-			$('#dropdown').trigger(new $.Event('sharesChanged', {shares: shares}));
+			// simulate updating shares
+			shareTab._dialog.model.set({
+				shares: [
+					{share_with_displayname: 'User One'},
+					{share_with_displayname: 'User Two'},
+					{share_with_displayname: 'Group One'},
+					{share_with_displayname: 'Group Two'}
+				]
+			});
 
 			expect($tr.attr('data-share-recipients')).toEqual('Group One, Group Two, User One, User Two');
 
-			OC.Share.updateIcon('file', 1);
-			expect($action.hasClass('permanent')).toEqual(true);
 			expect($action.find('>span').text().trim()).toEqual('Shared with Group One, Group Two, User One, User Two');
 			expect(OC.basename($action.find('img').attr('src'))).toEqual('share.svg');
 		});
@@ -294,23 +307,19 @@ describe('OCA.Sharing.Util tests', function() {
 			$action = fileList.$el.find('tbody tr:first .action-share');
 			$tr = fileList.$el.find('tr:first');
 
-			expect($action.hasClass('permanent')).toEqual(true);
-
 			$tr.find('.action-share').click();
 
-			expect(showDropDownStub.calledOnce).toEqual(true);
-
-			// simulate what the dropdown does
-			var shares = {};
-			OC.Share.itemShares[OC.Share.SHARE_TYPE_USER] = ['user1', 'user2', 'user3'];
-			shares[OC.Share.SHARE_TYPE_USER] = _.map(['User One', 'User Two', 'User Three'], makeDummyShareItem);
-			$('#dropdown').trigger(new $.Event('sharesChanged', {shares: shares}));
+			// simulate updating shares
+			shareTab._dialog.model.set({
+				shares: [
+					{share_with_displayname: 'User One'},
+					{share_with_displayname: 'User Two'},
+					{share_with_displayname: 'User Three'}
+				]
+			});
 
 			expect($tr.attr('data-share-recipients')).toEqual('User One, User Three, User Two');
 
-			OC.Share.updateIcon('file', 1);
-
-			expect($action.hasClass('permanent')).toEqual(true);
 			expect($action.find('>span').text().trim()).toEqual('Shared with User One, User Three, User Two');
 			expect(OC.basename($action.find('img').attr('src'))).toEqual('share.svg');
 		});
@@ -331,20 +340,14 @@ describe('OCA.Sharing.Util tests', function() {
 			$action = fileList.$el.find('tbody tr:first .action-share');
 			$tr = fileList.$el.find('tr:first');
 
-			expect($action.hasClass('permanent')).toEqual(true);
-
 			$tr.find('.action-share').click();
 
-			expect(showDropDownStub.calledOnce).toEqual(true);
-
-			// simulate what the dropdown does
-			OC.Share.itemShares = {};
-			$('#dropdown').trigger(new $.Event('sharesChanged', {shares: {}}));
+			// simulate updating shares
+			shareTab._dialog.model.set({
+				shares: []
+			});
 
 			expect($tr.attr('data-share-recipients')).not.toBeDefined();
-
-			OC.Share.updateIcon('file', 1);
-			expect($action.hasClass('permanent')).toEqual(true);
 		});
 		it('keep share text after updating reshare', function() {
 			var $action, $tr;
@@ -363,23 +366,15 @@ describe('OCA.Sharing.Util tests', function() {
 			$action = fileList.$el.find('tbody tr:first .action-share');
 			$tr = fileList.$el.find('tr:first');
 
-			expect($action.hasClass('permanent')).toEqual(true);
-
 			$tr.find('.action-share').click();
 
-			expect(showDropDownStub.calledOnce).toEqual(true);
-
-			// simulate what the dropdown does
-			var shares = {};
-			OC.Share.itemShares[OC.Share.SHARE_TYPE_USER] = ['user2'];
-			shares[OC.Share.SHARE_TYPE_USER] = _.map(['User Two'], makeDummyShareItem);
-			$('#dropdown').trigger(new $.Event('sharesChanged', {shares: shares}));
+			// simulate updating shares
+			shareTab._dialog.model.set({
+				shares: [{share_with_displayname: 'User Two'}]
+			});
 
 			expect($tr.attr('data-share-recipients')).toEqual('User Two');
 
-			OC.Share.updateIcon('file', 1);
-
-			expect($action.hasClass('permanent')).toEqual(true);
 			expect($action.find('>span').text().trim()).toEqual('User One');
 			expect(OC.basename($action.find('img').attr('src'))).toEqual('share.svg');
 		});
@@ -401,21 +396,15 @@ describe('OCA.Sharing.Util tests', function() {
 			$action = fileList.$el.find('tbody tr:first .action-share');
 			$tr = fileList.$el.find('tr:first');
 
-			expect($action.hasClass('permanent')).toEqual(true);
-
 			$tr.find('.action-share').click();
 
-			expect(showDropDownStub.calledOnce).toEqual(true);
-
-			// simulate what the dropdown does
-			OC.Share.itemShares = {};
-			$('#dropdown').trigger(new $.Event('sharesChanged', {shares: {}}));
+			// simulate updating shares
+			shareTab._dialog.model.set({
+				shares: []
+			});
 
 			expect($tr.attr('data-share-recipients')).not.toBeDefined();
 
-			OC.Share.updateIcon('file', 1);
-
-			expect($action.hasClass('permanent')).toEqual(true);
 			expect($action.find('>span').text().trim()).toEqual('User One');
 			expect(OC.basename($action.find('img').attr('src'))).toEqual('share.svg');
 		});
diff --git a/core/js/core.json b/core/js/core.json
index a67491c4a357d2c7d9befc57ddfa31a5185ae3bf..a80636e8463667846e8d4f0087ae608bf88db057 100644
--- a/core/js/core.json
+++ b/core/js/core.json
@@ -24,6 +24,13 @@
 		"l10n.js",
 		"apps.js",
 		"share.js",
+		"shareconfigmodel.js",
+		"shareitemmodel.js",
+		"sharedialogview.js",
+		"sharedialogexpirationview.js",
+		"sharedialoglinkshareview.js",
+		"sharedialogresharerinfoview.js",
+		"sharedialogshareelistview.js",
 		"octemplate.js",
 		"eventsource.js",
 		"config.js",
diff --git a/core/js/share.js b/core/js/share.js
index 0a3bba49b6c2bafd63570df0b9f7c260756f57a0..a2e6e6af0fccd664a9c0a5fa302baf9d48334e87 100644
--- a/core/js/share.js
+++ b/core/js/share.js
@@ -3,7 +3,7 @@
 /**
  * @namespace
  */
-OC.Share = _.extend(OC.Share, {
+OC.Share = _.extend(OC.Share || {}, {
 	SHARE_TYPE_USER:0,
 	SHARE_TYPE_GROUP:1,
 	SHARE_TYPE_LINK:3,
@@ -445,7 +445,7 @@ $(document).ready(function() {
 		var target = $(event.target);
 		var isMatched = !target.is('.drop, .ui-datepicker-next, .ui-datepicker-prev, .ui-icon')
 			&& !target.closest('#ui-datepicker-div').length && !target.closest('.ui-autocomplete').length;
-		if (OC.Share.droppedDown && isMatched && $('#dropdown').has(event.target).length === 0) {
+		if (OC.Share && OC.Share.droppedDown && isMatched && $('#dropdown').has(event.target).length === 0) {
 			OC.Share.hideDropDown();
 		}
 	});
diff --git a/core/js/shareconfigmodel.js b/core/js/shareconfigmodel.js
index 16f7e2514d8491bcc6ca780a3737e4adf953f3f8..8729698d136f1057ee615548fd92f1b22a21670d 100644
--- a/core/js/shareconfigmodel.js
+++ b/core/js/shareconfigmodel.js
@@ -14,6 +14,8 @@
 		OC.Share.Types = {};
 	}
 
+	// FIXME: the config model should populate its own model attributes based on
+	// the old DOM-based config
 	var ShareConfigModel = OC.Backbone.Model.extend({
 		defaults: {
 			publicUploadEnabled: false,
diff --git a/core/js/sharedialoglinkshareview.js b/core/js/sharedialoglinkshareview.js
index 1191aa401b4a4bf9265426151e4c8d137d1a6432..2c7914b254565b2e3cbb1785bed62a8ce0ed1224 100644
--- a/core/js/sharedialoglinkshareview.js
+++ b/core/js/sharedialoglinkshareview.js
@@ -236,7 +236,7 @@
 			}));
 
 			// TODO: move this to delegate events instead
-			this.$el.find('#linkCheckbox').change(this.onLinkCheckBoxChange);
+			this.$el.find('#linkCheckbox').click(this.onLinkCheckBoxChange);
 			this.$el.find('#sharingDialogAllowPublicUpload').change(this.onAllowPublicUploadChange);
 			this.$el.find('#linkText').click(this.onLinkTextClick);
 			this.$el.find('#showPassword').click(this.onShowPasswordClick);
diff --git a/core/js/tests/specs/shareSpec.js b/core/js/tests/specs/shareSpec.js
index 5a59a117d77da7aec604055eb0340f60c35c479f..9e80f4fe19d63d14c2a705438ae2c094d8a30f44 100644
--- a/core/js/tests/specs/shareSpec.js
+++ b/core/js/tests/specs/shareSpec.js
@@ -21,1090 +21,6 @@
 
 /* global oc_appconfig */
 describe('OC.Share tests', function() {
-	describe('dropdown', function() {
-		var $container;
-		var oldAppConfig;
-		var loadItemStub;
-		var autocompleteStub;
-		var oldEnableAvatars;
-		var avatarStub;
-		var placeholderStub;
-		var oldCurrentUser;
-
-		beforeEach(function() {
-			$('#testArea').append($('<div id="shareContainer"></div>'));
-			// horrible parameters
-			$('#testArea').append('<input id="allowShareWithLink" type="hidden" value="yes">');
-			$('#testArea').append('<input id="mailPublicNotificationEnabled" name="mailPublicNotificationEnabled" type="hidden" value="yes">');
-			$container = $('#shareContainer');
-			/* jshint camelcase:false */
-			oldAppConfig = _.extend({}, oc_appconfig.core);
-			oc_appconfig.core.enforcePasswordForPublicLink = false;
-
-			loadItemStub = sinon.stub(OC.Share, 'loadItem');
-			loadItemStub.returns({
-				reshare: [],
-				shares: []
-			});
-
-			autocompleteStub = sinon.stub($.fn, 'autocomplete', function() {
-				// dummy container with the expected attributes
-				if (!$(this).length) {
-					// simulate the real autocomplete that returns
-					// nothing at all when no element is specified
-					// (and potentially break stuff)
-					return null;
-				}
-				var $el = $('<div></div>').data('ui-autocomplete', {});
-				return $el;
-			});
-
-			oldEnableAvatars = oc_config.enable_avatars;
-			oc_config.enable_avatars = false;
-			avatarStub = sinon.stub($.fn, 'avatar');
-			placeholderStub = sinon.stub($.fn, 'imageplaceholder');
-
-			oldCurrentUser = OC.currentUser;
-			OC.currentUser = 'user0';
-		});
-		afterEach(function() {
-			OC.currentUser = oldCurrentUser;
-			/* jshint camelcase:false */
-			oc_appconfig.core = oldAppConfig;
-			loadItemStub.restore();
-
-			autocompleteStub.restore();
-			avatarStub.restore();
-			placeholderStub.restore();
-			oc_config.enable_avatars = oldEnableAvatars;
-			$('#dropdown').remove();
-		});
-		it('calls loadItem with the correct arguments', function() {
-			OC.Share.showDropDown(
-				'file',
-			   	123,
-			   	$container,
-				true,
-				31,
-				'shared_file_name.txt'
-			);
-			expect(loadItemStub.calledOnce).toEqual(true);
-			expect(loadItemStub.calledWith('file', 123)).toEqual(true);
-		});
-		it('shows the dropdown with default values', function() {
-			var $el;
-			OC.Share.showDropDown(
-				'file',
-			   	123,
-			   	$container,
-				true,
-				31,
-				'shared_file_name.txt'
-			);
-			$el = $container.find('#dropdown');
-			expect($el.length).toEqual(1);
-			expect($el.attr('data-item-type')).toEqual('file');
-			expect($el.attr('data-item-source')).toEqual('123');
-			// TODO: expect that other parts are rendered correctly
-		});
-		describe('Share with link', function() {
-			// TODO: test ajax calls
-			// TODO: test password field visibility (whenever enforced or not)
-			it('update password on focus out', function() {
-				$('#allowShareWithLink').val('yes');
-
-				OC.Share.showDropDown(
-					'file',
-					123,
-					$container,
-					true,
-					31,
-					'shared_file_name.txt'
-				);
-
-				// Toggle linkshare
-				$('#dropdown [name=linkCheckbox]').click();
-				fakeServer.requests[0].respond(
-					200,
-					{ 'Content-Type': 'application/json' },
-					JSON.stringify({data: {token: 'xyz'}, status: 'success'})
-				);
-
-				// Enable password, enter password and focusout
-				$('#dropdown [name=showPassword]').click();
-				$('#dropdown #linkPassText').focus();
-				$('#dropdown #linkPassText').val('foo');
-				$('#dropdown #linkPassText').focusout();
-
-				expect(fakeServer.requests[1].method).toEqual('POST');
-				var body = OC.parseQueryString(fakeServer.requests[1].requestBody);
-				expect(body['shareWith']).toEqual('foo');
-
-				// Set password response
-				fakeServer.requests[1].respond(
-					200,
-					{ 'Content-Type': 'application/json' },
-					JSON.stringify({data: {token: 'xyz'}, status: 'success'})
-				);
-
-				expect($('#dropdown #linkPassText').val()).toEqual('');
-				expect($('#dropdown #linkPassText').attr('placeholder')).toEqual('Password protected');
-			});
-			it('update password on enter', function() {
-				$('#allowShareWithLink').val('yes');
-
-				OC.Share.showDropDown(
-					'file',
-					123,
-					$container,
-					true,
-					31,
-					'shared_file_name.txt'
-				);
-
-				// Toggle linkshare
-				$('#dropdown [name=linkCheckbox]').click();
-				fakeServer.requests[0].respond(
-					200,
-					{ 'Content-Type': 'application/json' },
-					JSON.stringify({data: {token: 'xyz'}, status: 'success'})
-				);
-
-				// Enable password and enter password
-				$('#dropdown [name=showPassword]').click();
-				$('#dropdown #linkPassText').focus();
-				$('#dropdown #linkPassText').val('foo');
-				$('#dropdown #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']).toEqual('foo');
-
-				// Set password response
-				fakeServer.requests[1].respond(
-					200,
-					{ 'Content-Type': 'application/json' },
-					JSON.stringify({data: {token: 'xyz'}, status: 'success'})
-				);
-
-				expect($('#dropdown #linkPassText').val()).toEqual('');
-				expect($('#dropdown #linkPassText').attr('placeholder')).toEqual('Password protected');
-			});
-			it('shows share with link checkbox when allowed', function() {
-				$('#allowShareWithLink').val('yes');
-				OC.Share.showDropDown(
-					'file',
-					123,
-					$container,
-					true,		
-					31,
-					'shared_file_name.txt'
-				);
-				expect($('#dropdown #linkCheckbox').length).toEqual(1);
-			});
-			it('does not show share with link checkbox when not allowed', function() {
-				$('#allowShareWithLink').val('no');
-				OC.Share.showDropDown(
-					'file',
-					123,
-					$container,
-					true,		
-					31,
-					'shared_file_name.txt'
-				);
-				expect($('#dropdown #linkCheckbox').length).toEqual(0);
-			});
-			it('Reset link when password is enforced and link is toggled', function() { 
-				var old = oc_appconfig.core.enforcePasswordForPublicLink;
-				oc_appconfig.core.enforcePasswordForPublicLink = true;
-				$('#allowShareWithLink').val('yes');
-
-				OC.Share.showDropDown(
-					'file',
-					123,
-					$container,
-					true,
-					31,
-					'shared_file_name.txt'
-				);
-
-				// Toggle linkshare
-				$('#dropdown [name=linkCheckbox]').click();
-				expect($('#dropdown #linkText').val()).toEqual('');
-
-				// Set password
-				$('#dropdown #linkPassText').val('foo');
-				$('#dropdown #linkPassText').trigger(new $.Event('keyup', {keyCode: 13}));
-				fakeServer.requests[0].respond(
-					200,
-					{ 'Content-Type': 'application/json' },
-					JSON.stringify({data: {token: 'xyz'}, status: 'success'})
-				);
-
-				// Remove link
-				$('#dropdown [name=linkCheckbox]').click();
-				fakeServer.requests[1].respond(
-					200,
-					{ 'Content-Type': 'application/json' },
-					JSON.stringify({status: 'success'})
-				);
-
-				/*
-				 * Try to share again
-				 * The linkText should be emptied
-				 */
-				$('#dropdown [name=linkCheckbox]').click();
-				expect($('#dropdown #linkText').val()).toEqual('');
-
-				/*
-				 * Do not set password but untoggle
-				 * Since there is no share this should not result in another request to the server
-				 */
-				$('#dropdown [name=linkCheckbox]').click();
-				expect(fakeServer.requests.length).toEqual(2);
-
-				oc_appconfig.core.enforcePasswordForPublicLink = old;
-			});
-
-			it('Reset password placeholder when password is enforced and link is toggled', function() { 
-				var old = oc_appconfig.core.enforcePasswordForPublicLink;
-				oc_appconfig.core.enforcePasswordForPublicLink = true;
-				$('#allowShareWithLink').val('yes');
-
-				OC.Share.showDropDown(
-					'file',
-					123,
-					$container,
-					true,
-					31,
-					'shared_file_name.txt'
-				);
-
-				// Toggle linkshare
-				$('#dropdown [name=linkCheckbox]').click();
-				expect($('#dropdown #linkPassText').attr('placeholder')).toEqual('Choose a password for the public link');
-
-				// Set password
-				$('#dropdown #linkPassText').val('foo');
-				$('#dropdown #linkPassText').trigger(new $.Event('keyup', {keyCode: 13}));
-				fakeServer.requests[0].respond(
-					200,
-					{ 'Content-Type': 'application/json' },
-					JSON.stringify({data: {token: 'xyz'}, status: 'success'})
-				);
-				expect($('#dropdown #linkPassText').attr('placeholder')).toEqual('**********');
-
-				// Remove link
-				$('#dropdown [name=linkCheckbox]').click();
-				fakeServer.requests[1].respond(
-					200,
-					{ 'Content-Type': 'application/json' },
-					JSON.stringify({status: 'success'})
-				);
-
-				// Try to share again
-				$('#dropdown [name=linkCheckbox]').click();
-				expect($('#dropdown #linkPassText').attr('placeholder')).toEqual('Choose a password for the public link');
-
-				oc_appconfig.core.enforcePasswordForPublicLink = old;
-			});
-			it('reset password on toggle of share', function() {
-				$('#allowShareWithLink').val('yes');
-				OC.Share.showDropDown(
-					'file',
-					123,
-					$container,
-					true,
-					31,
-					'shared_file_name.txt'
-				);
-				$('#dropdown [name=linkCheckbox]').click();
-				fakeServer.requests[0].respond(
-					200,
-					{ 'Content-Type': 'application/json' },
-					JSON.stringify({data: {token: 'xyz'}, status: 'success'})
-				);
-
-				//Password protection should be unchecked and password field not visible
-				expect($('#dropdown [name=showPassword]').prop('checked')).toEqual(false);
-				expect($('#dropdown #linkPass').is(":visible")).toEqual(false);
-
-				// Toggle and set password
-				$('#dropdown [name=showPassword]').click();
-				$('#dropdown #linkPassText').val('foo');
-				$('#dropdown #linkPassText').trigger(new $.Event('keyup', {keyCode: 13}));
-				fakeServer.requests[1].respond(
-					200,
-					{ 'Content-Type': 'application/json' },
-					JSON.stringify({data: {token: 'xyz2'}, status: 'success'})
-				);
-
-				// Unshare
-				$('#dropdown [name=linkCheckbox]').click();
-				fakeServer.requests[2].respond(
-					200,
-					{ 'Content-Type': 'application/json' },
-					JSON.stringify({status: 'success'})
-				);
-
-				// Toggle share again
-				$('#dropdown [name=linkCheckbox]').click();
-				fakeServer.requests[3].respond(
-					200,
-					{ 'Content-Type': 'application/json' },
-					JSON.stringify({data: {token: 'xyz3'}, status: 'success'})
-				);
-
-
-				// Password checkbox should be unchecked
-				expect($('#dropdown [name=showPassword]').prop('checked')).toEqual(false);
-				expect($('#dropdown #linkPass').is(":visible")).toEqual(false);
-			});
-			it('reset expiration on toggle of share', function() {
-				$('#allowShareWithLink').val('yes');
-				OC.Share.showDropDown(
-					'file',
-					123,
-					$container,
-					true,
-					31,
-					'shared_file_name.txt'
-				);
-				$('#dropdown [name=linkCheckbox]').click();
-				fakeServer.requests[0].respond(
-					200,
-					{ 'Content-Type': 'application/json' },
-					JSON.stringify({data: {token: 'xyz'}, status: 'success'})
-				);
-
-				//Expiration should be unchecked and expiration field not visible
-				expect($('#dropdown [name=expirationCheckbox]').prop('checked')).toEqual(false);
-				expect($('#dropdown #expirationDate').is(":visible")).toEqual(false);
-
-				// Toggle and set password
-				$('#dropdown [name=expirationCheckbox]').click();
-				d = new Date();
-				d.setDate(d.getDate() + 1);
-				date=d.getDate() + '-' + (d.getMonth()+1) + '-' + d.getFullYear();
-				$('#dropdown #expirationDate').val(date);
-				$('#dropdown #expirationDate').change();
-				fakeServer.requests[1].respond(
-					200,
-					{ 'Content-Type': 'application/json' },
-					JSON.stringify({data: {token: 'xyz2'}, status: 'success'})
-				);
-
-				// Unshare
-				$('#dropdown [name=linkCheckbox]').click();
-				fakeServer.requests[2].respond(
-					200,
-					{ 'Content-Type': 'application/json' },
-					JSON.stringify({status: 'success'})
-				);
-
-				// Toggle share again
-				$('#dropdown [name=linkCheckbox]').click();
-				fakeServer.requests[3].respond(
-					200,
-					{ 'Content-Type': 'application/json' },
-					JSON.stringify({data: {token: 'xyz3'}, status: 'success'})
-				);
-
-				// Recheck expire visibility
-				expect($('#dropdown [name=expirationCheckbox]').prop('checked')).toEqual(false);
-				expect($('#dropdown #expirationDate').is(":visible")).toEqual(false);
-			});
-			it('shows populated link share when a link share exists', function() {
-				loadItemStub.returns({
-					reshare: [],
-					/* jshint camelcase: false */
-					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'
-					}]
-				});
-				OC.Share.showDropDown(
-					'file',
-					123,
-					$container,
-					true,
-					31,
-					'folder'
-				);
-				expect($('#dropdown #linkCheckbox').prop('checked')).toEqual(true);
-				// this is how the OC.Share class does it...
-				var link = parent.location.protocol + '//' + location.host +
-					OC.generateUrl('/s/') + 'tehtoken';
-				expect($('#dropdown #linkText').val()).toEqual(link);
-			});
-			it('does not show populated link share when a link share exists for a different file', function() {
-				loadItemStub.returns({
-					reshare: [],
-					/* jshint camelcase: false */
-					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'
-					}]
-				});
-				OC.Share.showDropDown(
-					'file',
-					456, // another file
-					$container,
-					true,
-					31,
-					'folder'
-				);
-				expect($('#dropdown #linkCheckbox').prop('checked')).toEqual(false);
-			});
-			it('shows correct link share when a nest link share exists along with parent one', function() {
-				loadItemStub.returns({
-					reshare: [],
-					/* jshint camelcase: false */
-					shares: [{
-						displayname_owner: 'root',
-						expiration: null,
-						file_source: 123,
-						file_target: '/folder',
-						id: 20,
-						item_source: '123',
-						item_type: 'file',
-						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'
-					}, {
-						displayname_owner: 'root',
-						expiration: null,
-						file_source: 456,
-						file_target: '/file_in_folder.txt',
-						id: 21,
-						item_source: '456',
-						item_type: 'file',
-						mail_send: '0',
-						parent: null,
-						path: '/folder/file_in_folder.txt',
-						permissions: OC.PERMISSION_READ,
-						share_type: OC.Share.SHARE_TYPE_LINK,
-						share_with: null,
-						stime: 1403884509,
-						storage: 1,
-						token: 'anothertoken',
-						uid_owner: 'root'
-					}]
-				});
-
-				// parent one
-				OC.Share.showDropDown(
-					'folder',
-					123,
-					$container,
-					true,
-					31,
-					'folder'
-				);
-				expect($('#dropdown #linkCheckbox').prop('checked')).toEqual(true);
-				// this is how the OC.Share class does it...
-				var link = parent.location.protocol + '//' + location.host +
-					OC.generateUrl('/s/') + 'tehtoken';
-				expect($('#dropdown #linkText').val()).toEqual(link);
-
-				// nested one
-				OC.Share.showDropDown(
-					'file',
-					456,
-					$container,
-					true,
-					31,
-					'file_in_folder.txt'
-				);
-				expect($('#dropdown #linkCheckbox').prop('checked')).toEqual(true);
-				// this is how the OC.Share class does it...
-				link = parent.location.protocol + '//' + location.host +
-					OC.generateUrl('/s/') + 'anothertoken';
-				expect($('#dropdown #linkText').val()).toEqual(link);
-			});
-			describe('expiration date', function() {
-				var shareData;
-				var shareItem;
-				var clock;
-				var expectedMinDate;
-
-				function showDropDown() {
-					OC.Share.showDropDown(
-						'file',
-						123,
-						$container,
-						true,
-						31,
-						'folder'
-					);
-				}
-
-				beforeEach(function() {
-					// pick a fake date
-					clock = sinon.useFakeTimers(new Date(2014, 0, 20, 14, 0, 0).getTime());
-					expectedMinDate = new Date(2014, 0, 21, 14, 0, 0);
-					shareItem = {
-						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'
-					};
-					shareData = {
-						reshare: [],
-						shares: []
-					};
-					loadItemStub.returns(shareData);
-					oc_appconfig.core.defaultExpireDate = 7;
-					oc_appconfig.core.enforcePasswordForPublicLink = false;
-					oc_appconfig.core.defaultExpireDateEnabled = false;
-					oc_appconfig.core.defaultExpireDateEnforced = false;
-				});
-				afterEach(function() {
-					clock.restore();
-				});
-
-				it('does not check expiration date checkbox when no date was set', function() {
-					shareItem.expiration = null;
-					shareData.shares.push(shareItem);
-					showDropDown();
-					expect($('#dropdown [name=expirationCheckbox]').prop('checked')).toEqual(false);
-					expect($('#dropdown #expirationDate').val()).toEqual('');
-				});
-				it('does not check expiration date checkbox for new share', function() {
-					showDropDown();
-					expect($('#dropdown [name=expirationCheckbox]').prop('checked')).toEqual(false);
-					expect($('#dropdown #expirationDate').val()).toEqual('');
-				});
-				it('checks expiration date checkbox and populates field when expiration date was set', function() {
-					shareItem.expiration = 1234;
-					shareData.shares.push(shareItem);
-					showDropDown();
-					expect($('#dropdown [name=expirationCheckbox]').prop('checked')).toEqual(true);
-					expect($('#dropdown #expirationDate').val()).toEqual('1234');
-				});
-				it('sets default date when default date setting is enabled', function() {
-					/* jshint camelcase:false */
-					oc_appconfig.core.defaultExpireDateEnabled = true;
-					showDropDown();
-					$('#dropdown [name=linkCheckbox]').click();
-					// enabled by default
-					expect($('#dropdown [name=expirationCheckbox]').prop('checked')).toEqual(true);
-					// TODO: those zeros must go...
-					expect($('#dropdown #expirationDate').val()).toEqual('2014-1-27 00:00:00');
-
-					// disabling is allowed
-					$('#dropdown [name=expirationCheckbox]').click();
-					expect($('#dropdown [name=expirationCheckbox]').prop('checked')).toEqual(false);
-				});
-				it('enforces default date when enforced date setting is enabled', function() {
-					/* jshint camelcase:false */
-					oc_appconfig.core.defaultExpireDateEnabled = true;
-					oc_appconfig.core.defaultExpireDateEnforced = true;
-					showDropDown();
-					$('#dropdown [name=linkCheckbox]').click();
-					expect($('#dropdown [name=expirationCheckbox]').prop('checked')).toEqual(true);
-					// TODO: those zeros must go...
-					expect($('#dropdown #expirationDate').val()).toEqual('2014-1-27 00:00:00');
-
-					// disabling is not allowed
-					expect($('#dropdown [name=expirationCheckbox]').prop('disabled')).toEqual(true);
-					$('#dropdown [name=expirationCheckbox]').click();
-					expect($('#dropdown [name=expirationCheckbox]').prop('checked')).toEqual(true);
-				});
-				it('enforces default date when enforced date setting is enabled and password is enforced', function() {
-					/* jshint camelcase:false */
-					oc_appconfig.core.enforcePasswordForPublicLink = true;
-					oc_appconfig.core.defaultExpireDateEnabled = true;
-					oc_appconfig.core.defaultExpireDateEnforced = true;
-					showDropDown();
-					$('#dropdown [name=linkCheckbox]').click();
-
-					//Enter password
-					$('#dropdown #linkPassText').val('foo');
-					$('#dropdown #linkPassText').trigger(new $.Event('keyup', {keyCode: 13}));
-					fakeServer.requests[0].respond(
-						200,
-						{ 'Content-Type': 'application/json' },
-						JSON.stringify({data: {token: 'xyz'}, status: 'success'})
-					);
-
-					expect($('#dropdown [name=expirationCheckbox]').prop('checked')).toEqual(true);
-					// TODO: those zeros must go...
-					expect($('#dropdown #expirationDate').val()).toEqual('2014-1-27 00:00:00');
-
-					// disabling is not allowed
-					expect($('#dropdown [name=expirationCheckbox]').prop('disabled')).toEqual(true);
-					$('#dropdown [name=expirationCheckbox]').click();
-					expect($('#dropdown [name=expirationCheckbox]').prop('checked')).toEqual(true);
-				});
-				it('displayes email form when sending emails is enabled', function() {
-					$('input[name=mailPublicNotificationEnabled]').val('yes');
-					showDropDown();
-					expect($('#emailPrivateLink').length).toEqual(1);
-				});
-				it('not renders email form when sending emails is disabled', function() {
-					$('input[name=mailPublicNotificationEnabled]').val('no');
-					showDropDown();
-					expect($('#emailPrivateLink').length).toEqual(0);
-				});
-				it('sets picker minDate to today and no maxDate by default', function() {
-					showDropDown();
-					$('#dropdown [name=linkCheckbox]').click();
-					$('#dropdown [name=expirationCheckbox]').click();
-					expect($.datepicker._defaults.minDate).toEqual(expectedMinDate);
-					expect($.datepicker._defaults.maxDate).toEqual(null);
-				});
-				it('limits the date range to X days after share time when enforced', function() {
-					/* jshint camelcase:false */
-					oc_appconfig.core.defaultExpireDateEnabled = true;
-					oc_appconfig.core.defaultExpireDateEnforced = true;
-					showDropDown();
-					$('#dropdown [name=linkCheckbox]').click();
-					expect($.datepicker._defaults.minDate).toEqual(expectedMinDate);
-					expect($.datepicker._defaults.maxDate).toEqual(new Date(2014, 0, 27, 0, 0, 0, 0));
-				});
-				it('limits the date range to X days after share time when enforced, even when redisplayed the next days', function() {
-					// item exists, was created two days ago
-					shareItem.expiration = '2014-1-27';
-					// share time has time component but must be stripped later
-					shareItem.stime = new Date(2014, 0, 20, 11, 0, 25).getTime() / 1000;
-					shareData.shares.push(shareItem);
-					/* jshint camelcase:false */
-					oc_appconfig.core.defaultExpireDateEnabled = true;
-					oc_appconfig.core.defaultExpireDateEnforced = true;
-					showDropDown();
-					expect($.datepicker._defaults.minDate).toEqual(expectedMinDate);
-					expect($.datepicker._defaults.maxDate).toEqual(new Date(2014, 0, 27, 0, 0, 0, 0));
-				});
-			});
-		});
-		describe('check for avatar', function() {
-			beforeEach(function() {
-				loadItemStub.returns({
-					reshare: {
-						share_type: OC.Share.SHARE_TYPE_USER,
-						uid_owner: 'owner',
-						displayname_owner: 'Owner',
-						permissions: 31
-					},
-					shares: [{
-						id: 100,
-						item_source: 123,
-						permissions: 31,
-						share_type: OC.Share.SHARE_TYPE_USER,
-						share_with: 'user1',
-						share_with_displayname: 'User One'
-					},{
-						id: 101,
-						item_source: 123,
-						permissions: 31,
-						share_type: OC.Share.SHARE_TYPE_GROUP,
-						share_with: 'group',
-						share_with_displayname: 'group'
-					},{
-						id: 102,
-						item_source: 123,
-						permissions: 31,
-						share_type: OC.Share.SHARE_TYPE_REMOTE,
-						share_with: 'foo@bar.com/baz',
-						share_with_displayname: 'foo@bar.com/baz'
-
-					}]
-				});
-			});
-
-			describe('avatars enabled', function() {
-				beforeEach(function() {
-					oc_config.enable_avatars = true;
-					OC.Share.showDropDown(
-						'file',
-						123,
-						$container,
-						true,
-						31,
-						'shared_file_name.txt'
-					);
-				});
-
-				afterEach(function() {
-					oc_config.enable_avatars = false;
-				});
-
-				it('test correct function calls', function() {
-					expect(avatarStub.calledTwice).toEqual(true);
-					expect(placeholderStub.calledTwice).toEqual(true);
-					expect($('#shareWithList').children().length).toEqual(3);
-					expect($('.avatar').length).toEqual(4);
-				});
-
-				it('test avatar owner', function() {
-					var args = avatarStub.getCall(0).args;
-					expect(args.length).toEqual(2);
-					expect(args[0]).toEqual('owner');
-				});
-
-				it('test avatar user', function() {
-					var args = avatarStub.getCall(1).args;
-					expect(args.length).toEqual(2);
-					expect(args[0]).toEqual('user1');
-				});
-
-				it('test avatar for groups', function() {
-					var args = placeholderStub.getCall(0).args;
-					expect(args.length).toEqual(1);
-					expect(args[0]).toEqual('group ' + OC.Share.SHARE_TYPE_GROUP);
-				});
-
-				it('test avatar for remotes', function() {
-					var args = placeholderStub.getCall(1).args;
-					expect(args.length).toEqual(1);
-					expect(args[0]).toEqual('foo@bar.com/baz ' + OC.Share.SHARE_TYPE_REMOTE);
-				});
-			});
-
-			describe('avatars disabled', function() {
-				beforeEach(function() {
-					OC.Share.showDropDown(
-						'file',
-						123,
-						$container,
-						true,
-						31,
-						'shared_file_name.txt'
-					);
-				});
-
-				it('no avatar classes', function() {
-					expect($('.avatar').length).toEqual(0);
-					expect(avatarStub.callCount).toEqual(0);
-					expect(placeholderStub.callCount).toEqual(0);
-				});
-			});
-		});
-		describe('"sharesChanged" event', function() {
-			var autocompleteOptions;
-			var handler;
-			beforeEach(function() {
-				handler = sinon.stub();
-				loadItemStub.returns({
-					reshare: [],
-					shares: [{
-						id: 100,
-						item_source: 123,
-						permissions: 31,
-						share_type: OC.Share.SHARE_TYPE_USER,
-						share_with: 'user1',
-						share_with_displayname: 'User One'
-					}]
-				});
-				OC.Share.showDropDown(
-					'file',
-					123,
-					$container,
-					true,
-					31,
-					'shared_file_name.txt'
-				);
-				$('#dropdown').on('sharesChanged', handler);
-				autocompleteOptions = autocompleteStub.getCall(0).args[0];
-			});
-			afterEach(function() {
-				autocompleteOptions = null;
-				handler = null;
-			});
-			it('triggers "sharesChanged" event when adding shares', function() {
-				// simulate autocomplete selection
-				autocompleteOptions.select(new $.Event('select'), {
-					item: {
-						label: 'User Two',
-						value: {
-							shareType: OC.Share.SHARE_TYPE_USER,
-							shareWith: 'user2'
-						}
-					}
-				});
-				fakeServer.requests[0].respond(
-					200,
-					{ 'Content-Type': 'application/json' },
-					JSON.stringify({status: 'success'})
-				);
-				expect(handler.calledOnce).toEqual(true);
-				var shares = handler.getCall(0).args[0].shares;
-				expect(shares).toBeDefined();
-				expect(shares[OC.Share.SHARE_TYPE_USER][0].share_with_displayname).toEqual('User One');
-				expect(shares[OC.Share.SHARE_TYPE_USER][1].share_with_displayname).toEqual('User Two');
-				expect(shares[OC.Share.SHARE_TYPE_GROUP]).not.toBeDefined();
-			});
-			it('triggers "sharesChanged" event when deleting shares', function() {
-				$('#dropdown .unshare:eq(0)').click();
-				fakeServer.requests[0].respond(
-					200,
-					{ 'Content-Type': 'application/json' },
-					JSON.stringify({status: 'success'})
-				);
-				expect(handler.calledOnce).toEqual(true);
-				var shares = handler.getCall(0).args[0].shares;
-				expect(shares).toBeDefined();
-				expect(shares[OC.Share.SHARE_TYPE_USER]).toEqual([]);
-				expect(shares[OC.Share.SHARE_TYPE_GROUP]).not.toBeDefined();
-			});
-			it('triggers "sharesChanged" event when toggling link share', function() {
-				// simulate autocomplete selection
-				$('#dropdown #linkCheckbox').click();
-				fakeServer.requests[0].respond(
-					200,
-					{ 'Content-Type': 'application/json' },
-					JSON.stringify({status: 'success', data: { token: 'abc' }})
-				);
-				expect(handler.calledOnce).toEqual(true);
-				var shares = handler.getCall(0).args[0].shares;
-				expect(shares).toBeDefined();
-				expect(shares[OC.Share.SHARE_TYPE_USER][0].share_with_displayname).toEqual('User One');
-				expect(shares[OC.Share.SHARE_TYPE_GROUP]).not.toBeDefined();
-
-				handler.reset();
-
-				// uncheck checkbox
-				$('#dropdown #linkCheckbox').click();
-				fakeServer.requests[1].respond(
-					200,
-					{ 'Content-Type': 'application/json' },
-					JSON.stringify({status: 'success'})
-				);
-
-				expect(handler.calledOnce).toEqual(true);
-				shares = handler.getCall(0).args[0].shares;
-				expect(shares).toBeDefined();
-				expect(shares[OC.Share.SHARE_TYPE_USER][0].share_with_displayname).toEqual('User One');
-				expect(shares[OC.Share.SHARE_TYPE_GROUP]).not.toBeDefined();
-			});
-		});
-		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) {
-				OC.Share.showDropDown(
-					'file',
-					123,
-					$container,
-					true,
-					possiblePermissions,
-					'shared_file_name.txt'
-				);
-				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() {
-					loadItemStub.returns({
-						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() {
-					oc_appconfig.core.resharingAllowed = false;
-					loadItemStub.returns({
-						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() {
-					oc_appconfig.core.resharingAllowed = false;
-					loadItemStub.returns({
-						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() {
-					loadItemStub.returns({
-						reshare: [],
-						shares: []
-					});
-					OC.Share.showDropDown(
-						'file',
-						123,
-						$container,
-						true,
-						OC.PERMISSION_READ,
-						'shared_file_name.txt'
-					);
-					expect($('#dropdown #shareWithList').length).toEqual(0);
-				});
-			});
-			describe('resharing', function() {
-				it('shares with given permissions when original share had all permissions', function() {
-					loadItemStub.returns({
-						reshare: {
-							permissions: OC.PERMISSION_ALL
-						},
-						shares: []
-					});
-					expect(
-						testWithPermissions(OC.PERMISSION_READ | OC.PERMISSION_UPDATE | OC.PERMISSION_SHARE)
-					).toEqual(OC.PERMISSION_READ | OC.PERMISSION_UPDATE | OC.PERMISSION_SHARE);
-				});
-				it('reduces reshare permissions to the ones from the original share', function() {
-					loadItemStub.returns({
-						reshare: {
-							permissions: OC.PERMISSION_READ,
-							uid_owner: 'user1'
-						},
-						shares: []
-					});
-					OC.Share.showDropDown(
-						'file',
-						123,
-						$container,
-						true,
-						OC.PERMISSION_ALL,
-						'shared_file_name.txt'
-					);
-					// no resharing allowed
-					expect($('#dropdown #shareWithList').length).toEqual(0);
-				});
-				it('reduces reshare permissions to possible permissions', function() {
-					loadItemStub.returns({
-						reshare: {
-							permissions: OC.PERMISSION_ALL,
-							uid_owner: 'user1'
-						},
-						shares: []
-					});
-					OC.Share.showDropDown(
-						'file',
-						123,
-						$container,
-						true,
-						OC.PERMISSION_READ,
-						'shared_file_name.txt'
-					);
-					// no resharing allowed
-					expect($('#dropdown #shareWithList').length).toEqual(0);
-				});
-				it('does not show sharing options when resharing not allowed', function() {
-					loadItemStub.returns({
-						reshare: {
-							permissions: OC.PERMISSION_READ | OC.PERMISSION_UPDATE | OC.PERMISSION_DELETE,
-							uid_owner: 'user1'
-						},
-						shares: []
-					});
-					OC.Share.showDropDown(
-						'file',
-						123,
-						$container,
-						true,
-						OC.PERMISSION_ALL,
-						'shared_file_name.txt'
-					);
-					expect($('#dropdown #shareWithList').length).toEqual(0);
-				});
-				it('allows owner to share their own share when they are also the recipient', function() {
-					OC.currentUser = 'user1';
-					loadItemStub.returns({
-						reshare: {
-							permissions: OC.PERMISSION_READ,
-							uid_owner: 'user1'
-						},
-						shares: []
-					});
-					OC.Share.showDropDown(
-						'file',
-						123,
-						$container,
-						true,
-						OC.PERMISSION_ALL,
-						'shared_file_name.txt'
-					);
-					// sharing still allowed
-					expect($('#dropdown #shareWithList').length).toEqual(1);
-				});
-			});
-		});
-	});
 	describe('markFileAsShared', function() {
 		var $file;
 		var tipsyStub;
@@ -1316,21 +232,5 @@ describe('OC.Share tests', function() {
 			});
 		});
 	});
-	describe('OC.Share utils', function() {
-		it('parseTime should properly parse strings', function() {
-
-			_.each([
-				[ '123456', 123456],
-				[  123456 , 123456],
-				['0123456', 123456],
-				['abcdefg',   null],
-				['0x12345',   null],
-				[       '',   null],
-			], function(value) {
-				expect(OC.Share._parseTime(value[0])).toEqual(value[1]);
-			});
-
-		});
-	});
 });
 
diff --git a/core/js/tests/specs/sharedialogviewSpec.js b/core/js/tests/specs/sharedialogviewSpec.js
new file mode 100644
index 0000000000000000000000000000000000000000..b50432a034fb5868cd72bb8936db6a3a092f6355
--- /dev/null
+++ b/core/js/tests/specs/sharedialogviewSpec.js
@@ -0,0 +1,911 @@
+/**
+* ownCloud
+*
+* @author Vincent Petry
+* @copyright 2015 Vincent Petry <pvince81@owncloud.com>
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+* License as published by the Free Software Foundation; either
+* version 3 of the License, or any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+*
+* You should have received a copy of the GNU Affero General Public
+* License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*
+*/
+
+/* global oc_appconfig */
+describe('OC.Share.ShareDialogView', function() {
+	var $container;
+	var oldAppConfig;
+	var autocompleteStub;
+	var oldEnableAvatars;
+	var avatarStub;
+	var placeholderStub;
+	var oldCurrentUser;
+
+	var fetchStub;
+
+	var configModel;
+	var shareModel;
+	var fileInfoModel;
+	var dialog;
+
+	beforeEach(function() {
+		// horrible parameters
+		$('#testArea').append('<input id="allowShareWithLink" type="hidden" value="yes">');
+		$('#testArea').append('<input id="mailPublicNotificationEnabled" name="mailPublicNotificationEnabled" type="hidden" value="yes">');
+		$container = $('#shareContainer');
+		/* jshint camelcase:false */
+		oldAppConfig = _.extend({}, oc_appconfig.core);
+		oc_appconfig.core.enforcePasswordForPublicLink = false;
+
+		fetchStub = sinon.stub(OC.Share.ShareItemModel.prototype, 'fetch');
+
+		fileInfoModel = new OCA.Files.FileInfoModel({
+			id: 123,
+			name: 'shared_file_name.txt',
+			path: '/subdir',
+			size: 100,
+			mimetype: 'text/plain',
+			permissions: 31,
+			sharePermissions: 31
+		});
+
+		var attributes = {
+			itemType: fileInfoModel.isDirectory() ? 'folder' : 'file',
+			itemSource: fileInfoModel.get('id'),
+			possiblePermissions: 31,
+			permissions: 31
+		};
+		configModel = new OC.Share.ShareConfigModel();
+		shareModel = new OC.Share.ShareItemModel(attributes, {
+			configModel: configModel,
+			fileInfoModel: fileInfoModel
+		});
+		dialog = new OC.Share.ShareDialogView({
+			configModel: configModel,
+			model: shareModel
+		});
+
+		// triggers rendering
+		shareModel.set({
+			shares: [],
+			linkShare: {isLinkShare: false}
+		});
+
+		autocompleteStub = sinon.stub($.fn, 'autocomplete', function() {
+			// dummy container with the expected attributes
+			if (!$(this).length) {
+				// simulate the real autocomplete that returns
+				// nothing at all when no element is specified
+				// (and potentially break stuff)
+				return null;
+			}
+			var $el = $('<div></div>').data('ui-autocomplete', {});
+			return $el;
+		});
+
+		oldEnableAvatars = oc_config.enable_avatars;
+		oc_config.enable_avatars = false;
+		avatarStub = sinon.stub($.fn, 'avatar');
+		placeholderStub = sinon.stub($.fn, 'imageplaceholder');
+
+		oldCurrentUser = OC.currentUser;
+		OC.currentUser = 'user0';
+	});
+	afterEach(function() {
+		OC.currentUser = oldCurrentUser;
+		/* jshint camelcase:false */
+		oc_appconfig.core = oldAppConfig;
+
+		fetchStub.restore();
+
+		autocompleteStub.restore();
+		avatarStub.restore();
+		placeholderStub.restore();
+		oc_config.enable_avatars = oldEnableAvatars;
+	});
+	describe('Share with link', function() {
+		beforeEach(function() {
+			configModel.set('enforcePasswordForPublicLink', false);
+		});
+		// TODO: test ajax calls
+		// TODO: test password field visibility (whenever enforced or not)
+		it('update password on focus out', function() {
+			$('#allowShareWithLink').val('yes');
+
+			dialog.render();
+
+			// Toggle linkshare
+			dialog.$el.find('[name=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']).toEqual('foo');
+
+			// Set password response
+			fakeServer.requests[1].respond(
+				200,
+				{ 'Content-Type': 'application/json' },
+				JSON.stringify({data: {token: 'xyz'}, status: 'success'})
+			);
+
+			expect(dialog.$el.find('#linkPassText').val()).toEqual('');
+			expect(dialog.$el.find('#linkPassText').attr('placeholder')).toEqual('Password protected');
+		});
+		it('update password on enter', function() {
+			$('#allowShareWithLink').val('yes');
+
+			dialog.render();
+
+			// Toggle linkshare
+			dialog.$el.find('[name=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();
+			dialog.$el.find('#linkPassText').focus();
+			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']).toEqual('foo');
+
+			// Set password response
+			fakeServer.requests[1].respond(
+				200,
+				{ 'Content-Type': 'application/json' },
+				JSON.stringify({data: {token: 'xyz'}, status: 'success'})
+			);
+
+			expect(dialog.$el.find('#linkPassText').val()).toEqual('');
+			expect(dialog.$el.find('#linkPassText').attr('placeholder')).toEqual('Password protected');
+		});
+		it('shows share with link checkbox when allowed', function() {
+			$('#allowShareWithLink').val('yes');
+
+			dialog.render();
+
+			expect(dialog.$el.find('#linkCheckbox').length).toEqual(1);
+		});
+		it('does not show share with link checkbox when not allowed', function() {
+			$('#allowShareWithLink').val('no');
+
+			dialog.render();
+
+			expect(dialog.$el.find('#linkCheckbox').length).toEqual(0);
+		});
+		it('Reset link when password is enforced and link is toggled', function() { 
+			var old = oc_appconfig.core.enforcePasswordForPublicLink;
+			oc_appconfig.core.enforcePasswordForPublicLink = true;
+			$('#allowShareWithLink').val('yes');
+
+			dialog.render();
+
+			// Toggle linkshare
+			dialog.$el.find('[name=linkCheckbox]').click();
+			expect(dialog.$el.find('#linkText').val()).toEqual('');
+
+			// Set password
+			dialog.$el.find('#linkPassText').val('foo');
+			dialog.$el.find('#linkPassText').trigger(new $.Event('keyup', {keyCode: 13}));
+			fakeServer.requests[0].respond(
+				200,
+				{ 'Content-Type': 'application/json' },
+				JSON.stringify({data: {token: 'xyz'}, status: 'success'})
+			);
+
+			// Remove link
+			dialog.$el.find('[name=linkCheckbox]').click();
+			fakeServer.requests[1].respond(
+				200,
+				{ 'Content-Type': 'application/json' },
+				JSON.stringify({status: 'success'})
+			);
+
+			/*
+			 * Try to share again
+			 * The linkText should be emptied
+			 */
+			dialog.$el.find('[name=linkCheckbox]').click();
+			expect(dialog.$el.find('#linkText').val()).toEqual('');
+
+			/*
+			 * Do not set password but untoggle
+			 * Since there is no share this should not result in another request to the server
+			 */
+			dialog.$el.find('[name=linkCheckbox]').click();
+			expect(fakeServer.requests.length).toEqual(2);
+
+			oc_appconfig.core.enforcePasswordForPublicLink = old;
+		});
+
+		it('Reset password placeholder when password is enforced and link is toggled', function() { 
+			var old = oc_appconfig.core.enforcePasswordForPublicLink;
+			oc_appconfig.core.enforcePasswordForPublicLink = true;
+			$('#allowShareWithLink').val('yes');
+
+			dialog.render();
+
+			// Toggle linkshare
+			dialog.$el.find('[name=linkCheckbox]').click();
+			expect(dialog.$el.find('#linkPassText').attr('placeholder')).toEqual('Choose a password for the public link');
+
+			// Set password
+			dialog.$el.find('#linkPassText').val('foo');
+			dialog.$el.find('#linkPassText').trigger(new $.Event('keyup', {keyCode: 13}));
+			fakeServer.requests[0].respond(
+				200,
+				{ 'Content-Type': 'application/json' },
+				JSON.stringify({data: {token: 'xyz'}, status: 'success'})
+			);
+			expect(dialog.$el.find('#linkPassText').attr('placeholder')).toEqual('**********');
+
+			// Remove link
+			dialog.$el.find('[name=linkCheckbox]').click();
+			fakeServer.requests[1].respond(
+				200,
+				{ 'Content-Type': 'application/json' },
+				JSON.stringify({status: 'success'})
+			);
+
+			// Try to share again
+			dialog.$el.find('[name=linkCheckbox]').click();
+			expect(dialog.$el.find('#linkPassText').attr('placeholder')).toEqual('Choose a password for the public link');
+
+			oc_appconfig.core.enforcePasswordForPublicLink = old;
+		});
+		it('reset password on toggle of share', function() {
+			$('#allowShareWithLink').val('yes');
+
+			dialog.render();
+
+			dialog.$el.find('[name=linkCheckbox]').click();
+			fakeServer.requests[0].respond(
+				200,
+				{ 'Content-Type': 'application/json' },
+				JSON.stringify({data: {token: 'xyz'}, status: 'success'})
+			);
+
+			//Password protection should be unchecked and password field not visible
+			expect(dialog.$el.find('[name=showPassword]').prop('checked')).toEqual(false);
+			expect(dialog.$el.find('#linkPass').is(":visible")).toEqual(false);
+
+			// Toggle and set password
+			dialog.$el.find('[name=showPassword]').click();
+			dialog.$el.find('#linkPassText').val('foo');
+			dialog.$el.find('#linkPassText').trigger(new $.Event('keyup', {keyCode: 13}));
+			fakeServer.requests[1].respond(
+				200,
+				{ 'Content-Type': 'application/json' },
+				JSON.stringify({data: {token: 'xyz2'}, status: 'success'})
+			);
+
+			// Unshare
+			dialog.$el.find('[name=linkCheckbox]').click();
+			fakeServer.requests[2].respond(
+				200,
+				{ 'Content-Type': 'application/json' },
+				JSON.stringify({status: 'success'})
+			);
+
+			// Toggle share again
+			dialog.$el.find('[name=linkCheckbox]').click();
+			fakeServer.requests[3].respond(
+				200,
+				{ 'Content-Type': 'application/json' },
+				JSON.stringify({data: {token: 'xyz3'}, status: 'success'})
+			);
+
+
+			// Password checkbox should be unchecked
+			expect(dialog.$el.find('[name=showPassword]').prop('checked')).toEqual(false);
+			expect(dialog.$el.find('#linkPass').is(":visible")).toEqual(false);
+		});
+		it('reset expiration on toggle of share', function() {
+			$('#allowShareWithLink').val('yes');
+
+			dialog.render();
+
+			dialog.$el.find('[name=linkCheckbox]').click();
+			fakeServer.requests[0].respond(
+				200,
+				{ 'Content-Type': 'application/json' },
+				JSON.stringify({data: {token: 'xyz'}, status: 'success'})
+			);
+
+			//Expiration should be unchecked and expiration field not visible
+			expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(false);
+			expect(dialog.$el.find('#expirationDate').is(":visible")).toEqual(false);
+
+			// Toggle and set password
+			dialog.$el.find('[name=expirationCheckbox]').click();
+			d = new Date();
+			d.setDate(d.getDate() + 1);
+			date=d.getDate() + '-' + (d.getMonth()+1) + '-' + d.getFullYear();
+			dialog.$el.find('#expirationDate').val(date);
+			dialog.$el.find('#expirationDate').change();
+			fakeServer.requests[1].respond(
+				200,
+				{ 'Content-Type': 'application/json' },
+				JSON.stringify({data: {token: 'xyz2'}, status: 'success'})
+			);
+
+			// Unshare
+			dialog.$el.find('[name=linkCheckbox]').click();
+			fakeServer.requests[2].respond(
+				200,
+				{ 'Content-Type': 'application/json' },
+				JSON.stringify({status: 'success'})
+			);
+
+			// Toggle share again
+			dialog.$el.find('[name=linkCheckbox]').click();
+			fakeServer.requests[3].respond(
+				200,
+				{ 'Content-Type': 'application/json' },
+				JSON.stringify({data: {token: 'xyz3'}, status: 'success'})
+			);
+
+			// Recheck expire visibility
+			expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(false);
+			expect(dialog.$el.find('#expirationDate').is(":visible")).toEqual(false);
+		});
+		it('shows populated link share when a link share exists', function() {
+			shareModel.set('linkShare', {
+				isLinkShare: true,
+				token: 'tehtoken',
+				link: 'TODO',
+				expiration: '',
+				permissions: OC.PERMISSION_READ,
+				stime: 1403884258,
+			});
+
+			expect(dialog.$el.find('#linkCheckbox').prop('checked')).toEqual(true);
+			// this is how the OC.Share class does it...
+			var link = parent.location.protocol + '//' + location.host +
+				OC.generateUrl('/s/') + 'tehtoken';
+			expect(dialog.$el.find('#linkText').val()).toEqual(link);
+		});
+		describe('expiration date', function() {
+			var shareData;
+			var shareItem;
+			var clock;
+			var expectedMinDate;
+
+			beforeEach(function() {
+				// pick a fake date
+				clock = sinon.useFakeTimers(new Date(2014, 0, 20, 14, 0, 0).getTime());
+				expectedMinDate = new Date(2014, 0, 21, 14, 0, 0);
+
+				oc_appconfig.core.defaultExpireDate = 7;
+				oc_appconfig.core.enforcePasswordForPublicLink = false;
+				oc_appconfig.core.defaultExpireDateEnabled = false;
+				oc_appconfig.core.defaultExpireDateEnforced = false;
+
+				shareModel.set('linkShare', {
+					isLinkShare: true,
+					token: 'tehtoken',
+					permissions: OC.PERMISSION_READ,
+					expiration: null
+				});
+			});
+			afterEach(function() {
+				clock.restore();
+			});
+
+			it('does not check expiration date checkbox when no date was set', function() {
+				shareModel.get('linkShare').expiration = null;
+				dialog.render();
+
+				expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(false);
+				expect(dialog.$el.find('#expirationDate').val()).toEqual('');
+			});
+			it('does not check expiration date checkbox for new share', function() {
+				dialog.render();
+
+				expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(false);
+				expect(dialog.$el.find('#expirationDate').val()).toEqual('');
+			});
+			it('checks expiration date checkbox and populates field when expiration date was set', function() {
+				shareModel.get('linkShare').expiration = 1234;
+				dialog.render();
+				expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(true);
+				expect(dialog.$el.find('#expirationDate').val()).toEqual('1234');
+			});
+			it('sets default date when default date setting is enabled', function() {
+				/* jshint camelcase:false */
+				oc_appconfig.core.defaultExpireDateEnabled = true;
+				dialog.render();
+				dialog.$el.find('[name=linkCheckbox]').click();
+				// enabled by default
+				expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(true);
+				// TODO: those zeros must go...
+				expect(dialog.$el.find('#expirationDate').val()).toEqual('2014-1-27 00:00:00');
+
+				// disabling is allowed
+				dialog.$el.find('[name=expirationCheckbox]').click();
+				expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(false);
+			});
+			it('enforces default date when enforced date setting is enabled', function() {
+				/* jshint camelcase:false */
+				oc_appconfig.core.defaultExpireDateEnabled = true;
+				oc_appconfig.core.defaultExpireDateEnforced = true;
+				dialog.render();
+				dialog.$el.find('[name=linkCheckbox]').click();
+				expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(true);
+				// TODO: those zeros must go...
+				expect(dialog.$el.find('#expirationDate').val()).toEqual('2014-1-27 00:00:00');
+
+				// disabling is not allowed
+				expect(dialog.$el.find('[name=expirationCheckbox]').prop('disabled')).toEqual(true);
+				dialog.$el.find('[name=expirationCheckbox]').click();
+				expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(true);
+			});
+			it('enforces default date when enforced date setting is enabled and password is enforced', function() {
+				/* jshint camelcase:false */
+				oc_appconfig.core.enforcePasswordForPublicLink = true;
+				oc_appconfig.core.defaultExpireDateEnabled = true;
+				oc_appconfig.core.defaultExpireDateEnforced = true;
+				dialog.render();
+				dialog.$el.find('[name=linkCheckbox]').click();
+
+				//Enter password
+				dialog.$el.find('#linkPassText').val('foo');
+				dialog.$el.find('#linkPassText').trigger(new $.Event('keyup', {keyCode: 13}));
+				fakeServer.requests[0].respond(
+					200,
+					{ 'Content-Type': 'application/json' },
+					JSON.stringify({data: {token: 'xyz'}, status: 'success'})
+				);
+
+				expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(true);
+				// TODO: those zeros must go...
+				expect(dialog.$el.find('#expirationDate').val()).toEqual('2014-1-27 00:00:00');
+
+				// disabling is not allowed
+				expect(dialog.$el.find('[name=expirationCheckbox]').prop('disabled')).toEqual(true);
+				dialog.$el.find('[name=expirationCheckbox]').click();
+				expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(true);
+			});
+			it('displayes email form when sending emails is enabled', function() {
+				$('input[name=mailPublicNotificationEnabled]').val('yes');
+				dialog.render();
+				expect($('#emailPrivateLink').length).toEqual(1);
+			});
+			it('not renders email form when sending emails is disabled', function() {
+				$('input[name=mailPublicNotificationEnabled]').val('no');
+				dialog.render();
+				expect($('#emailPrivateLink').length).toEqual(0);
+			});
+			it('sets picker minDate to today and no maxDate by default', function() {
+				dialog.render();
+				dialog.$el.find('[name=linkCheckbox]').click();
+				dialog.$el.find('[name=expirationCheckbox]').click();
+				expect($.datepicker._defaults.minDate).toEqual(expectedMinDate);
+				expect($.datepicker._defaults.maxDate).toEqual(null);
+			});
+			it('limits the date range to X days after share time when enforced', function() {
+				/* jshint camelcase:false */
+				oc_appconfig.core.defaultExpireDateEnabled = true;
+				oc_appconfig.core.defaultExpireDateEnforced = true;
+				dialog.render();
+				dialog.$el.find('[name=linkCheckbox]').click();
+				expect($.datepicker._defaults.minDate).toEqual(expectedMinDate);
+				expect($.datepicker._defaults.maxDate).toEqual(new Date(2014, 0, 27, 0, 0, 0, 0));
+			});
+			it('limits the date range to X days after share time when enforced, even when redisplayed the next days', function() {
+				// item exists, was created two days ago
+				shareItem.expiration = '2014-1-27';
+				// share time has time component but must be stripped later
+				shareItem.stime = new Date(2014, 0, 20, 11, 0, 25).getTime() / 1000;
+				shareData.shares.push(shareItem);
+				/* jshint camelcase:false */
+				oc_appconfig.core.defaultExpireDateEnabled = true;
+				oc_appconfig.core.defaultExpireDateEnforced = true;
+				dialog.render();
+				expect($.datepicker._defaults.minDate).toEqual(expectedMinDate);
+				expect($.datepicker._defaults.maxDate).toEqual(new Date(2014, 0, 27, 0, 0, 0, 0));
+			});
+		});
+	});
+	describe('check for avatar', function() {
+		beforeEach(function() {
+			loadItemStub.returns({
+				reshare: {
+					share_type: OC.Share.SHARE_TYPE_USER,
+					uid_owner: 'owner',
+					displayname_owner: 'Owner',
+					permissions: 31
+				},
+				shares: [{
+					id: 100,
+					item_source: 123,
+					permissions: 31,
+					share_type: OC.Share.SHARE_TYPE_USER,
+					share_with: 'user1',
+					share_with_displayname: 'User One'
+				},{
+					id: 101,
+					item_source: 123,
+					permissions: 31,
+					share_type: OC.Share.SHARE_TYPE_GROUP,
+					share_with: 'group',
+					share_with_displayname: 'group'
+				},{
+					id: 102,
+					item_source: 123,
+					permissions: 31,
+					share_type: OC.Share.SHARE_TYPE_REMOTE,
+					share_with: 'foo@bar.com/baz',
+					share_with_displayname: 'foo@bar.com/baz'
+
+				}]
+			});
+		});
+
+		describe('avatars enabled', function() {
+			beforeEach(function() {
+				oc_config.enable_avatars = true;
+				OC.Share.showDropDown(
+					'file',
+					123,
+					$container,
+					true,
+					31,
+					'shared_file_name.txt'
+				);
+			});
+
+			afterEach(function() {
+				oc_config.enable_avatars = false;
+			});
+
+			it('test correct function calls', function() {
+				expect(avatarStub.calledTwice).toEqual(true);
+				expect(placeholderStub.calledTwice).toEqual(true);
+				expect($('#shareWithList').children().length).toEqual(3);
+				expect($('.avatar').length).toEqual(4);
+			});
+
+			it('test avatar owner', function() {
+				var args = avatarStub.getCall(0).args;
+				expect(args.length).toEqual(2);
+				expect(args[0]).toEqual('owner');
+			});
+
+			it('test avatar user', function() {
+				var args = avatarStub.getCall(1).args;
+				expect(args.length).toEqual(2);
+				expect(args[0]).toEqual('user1');
+			});
+
+			it('test avatar for groups', function() {
+				var args = placeholderStub.getCall(0).args;
+				expect(args.length).toEqual(1);
+				expect(args[0]).toEqual('group ' + OC.Share.SHARE_TYPE_GROUP);
+			});
+
+			it('test avatar for remotes', function() {
+				var args = placeholderStub.getCall(1).args;
+				expect(args.length).toEqual(1);
+				expect(args[0]).toEqual('foo@bar.com/baz ' + OC.Share.SHARE_TYPE_REMOTE);
+			});
+		});
+
+		describe('avatars disabled', function() {
+			beforeEach(function() {
+				OC.Share.showDropDown(
+					'file',
+					123,
+					$container,
+					true,
+					31,
+					'shared_file_name.txt'
+				);
+			});
+
+			it('no avatar classes', function() {
+				expect($('.avatar').length).toEqual(0);
+				expect(avatarStub.callCount).toEqual(0);
+				expect(placeholderStub.callCount).toEqual(0);
+			});
+		});
+	});
+	describe('"sharesChanged" event', function() {
+		var autocompleteOptions;
+		var handler;
+		beforeEach(function() {
+			handler = sinon.stub();
+			loadItemStub.returns({
+				reshare: [],
+				shares: [{
+					id: 100,
+					item_source: 123,
+					permissions: 31,
+					share_type: OC.Share.SHARE_TYPE_USER,
+					share_with: 'user1',
+					share_with_displayname: 'User One'
+				}]
+			});
+			OC.Share.showDropDown(
+				'file',
+				123,
+				$container,
+				true,
+				31,
+				'shared_file_name.txt'
+			);
+			$('#dropdown').on('sharesChanged', handler);
+			autocompleteOptions = autocompleteStub.getCall(0).args[0];
+		});
+		afterEach(function() {
+			autocompleteOptions = null;
+			handler = null;
+		});
+		it('triggers "sharesChanged" event when adding shares', function() {
+			// simulate autocomplete selection
+			autocompleteOptions.select(new $.Event('select'), {
+				item: {
+					label: 'User Two',
+					value: {
+						shareType: OC.Share.SHARE_TYPE_USER,
+						shareWith: 'user2'
+					}
+				}
+			});
+			fakeServer.requests[0].respond(
+				200,
+				{ 'Content-Type': 'application/json' },
+				JSON.stringify({status: 'success'})
+			);
+			expect(handler.calledOnce).toEqual(true);
+			var shares = handler.getCall(0).args[0].shares;
+			expect(shares).toBeDefined();
+			expect(shares[OC.Share.SHARE_TYPE_USER][0].share_with_displayname).toEqual('User One');
+			expect(shares[OC.Share.SHARE_TYPE_USER][1].share_with_displayname).toEqual('User Two');
+			expect(shares[OC.Share.SHARE_TYPE_GROUP]).not.toBeDefined();
+		});
+		it('triggers "sharesChanged" event when deleting shares', function() {
+			dialog.$el.find('.unshare:eq(0)').click();
+			fakeServer.requests[0].respond(
+				200,
+				{ 'Content-Type': 'application/json' },
+				JSON.stringify({status: 'success'})
+			);
+			expect(handler.calledOnce).toEqual(true);
+			var shares = handler.getCall(0).args[0].shares;
+			expect(shares).toBeDefined();
+			expect(shares[OC.Share.SHARE_TYPE_USER]).toEqual([]);
+			expect(shares[OC.Share.SHARE_TYPE_GROUP]).not.toBeDefined();
+		});
+		it('triggers "sharesChanged" event when toggling link share', function() {
+			// simulate autocomplete selection
+			dialog.$el.find('#linkCheckbox').click();
+			fakeServer.requests[0].respond(
+				200,
+				{ 'Content-Type': 'application/json' },
+				JSON.stringify({status: 'success', data: { token: 'abc' }})
+			);
+			expect(handler.calledOnce).toEqual(true);
+			var shares = handler.getCall(0).args[0].shares;
+			expect(shares).toBeDefined();
+			expect(shares[OC.Share.SHARE_TYPE_USER][0].share_with_displayname).toEqual('User One');
+			expect(shares[OC.Share.SHARE_TYPE_GROUP]).not.toBeDefined();
+
+			handler.reset();
+
+			// uncheck checkbox
+			dialog.$el.find('#linkCheckbox').click();
+			fakeServer.requests[1].respond(
+				200,
+				{ 'Content-Type': 'application/json' },
+				JSON.stringify({status: 'success'})
+			);
+
+			expect(handler.calledOnce).toEqual(true);
+			shares = handler.getCall(0).args[0].shares;
+			expect(shares).toBeDefined();
+			expect(shares[OC.Share.SHARE_TYPE_USER][0].share_with_displayname).toEqual('User One');
+			expect(shares[OC.Share.SHARE_TYPE_GROUP]).not.toBeDefined();
+		});
+	});
+	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) {
+			OC.Share.showDropDown(
+				'file',
+				123,
+				$container,
+				true,
+				possiblePermissions,
+				'shared_file_name.txt'
+			);
+			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() {
+				loadItemStub.returns({
+					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() {
+				oc_appconfig.core.resharingAllowed = false;
+				loadItemStub.returns({
+					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() {
+				oc_appconfig.core.resharingAllowed = false;
+				loadItemStub.returns({
+					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() {
+				loadItemStub.returns({
+					reshare: [],
+					shares: []
+				});
+				OC.Share.showDropDown(
+					'file',
+					123,
+					$container,
+					true,
+					OC.PERMISSION_READ,
+					'shared_file_name.txt'
+				);
+				expect(dialog.$el.find('#shareWithList').length).toEqual(0);
+			});
+		});
+		describe('resharing', function() {
+			it('shares with given permissions when original share had all permissions', function() {
+				loadItemStub.returns({
+					reshare: {
+						permissions: OC.PERMISSION_ALL
+					},
+					shares: []
+				});
+				expect(
+					testWithPermissions(OC.PERMISSION_READ | OC.PERMISSION_UPDATE | OC.PERMISSION_SHARE)
+				).toEqual(OC.PERMISSION_READ | OC.PERMISSION_UPDATE | OC.PERMISSION_SHARE);
+			});
+			it('reduces reshare permissions to the ones from the original share', function() {
+				loadItemStub.returns({
+					reshare: {
+						permissions: OC.PERMISSION_READ,
+						uid_owner: 'user1'
+					},
+					shares: []
+				});
+				OC.Share.showDropDown(
+					'file',
+					123,
+					$container,
+					true,
+					OC.PERMISSION_ALL,
+					'shared_file_name.txt'
+				);
+				// no resharing allowed
+				expect(dialog.$el.find('#shareWithList').length).toEqual(0);
+			});
+			it('reduces reshare permissions to possible permissions', function() {
+				loadItemStub.returns({
+					reshare: {
+						permissions: OC.PERMISSION_ALL,
+						uid_owner: 'user1'
+					},
+					shares: []
+				});
+				OC.Share.showDropDown(
+					'file',
+					123,
+					$container,
+					true,
+					OC.PERMISSION_READ,
+					'shared_file_name.txt'
+				);
+				// no resharing allowed
+				expect(dialog.$el.find('#shareWithList').length).toEqual(0);
+			});
+			it('does not show sharing options when resharing not allowed', function() {
+				loadItemStub.returns({
+					reshare: {
+						permissions: OC.PERMISSION_READ | OC.PERMISSION_UPDATE | OC.PERMISSION_DELETE,
+						uid_owner: 'user1'
+					},
+					shares: []
+				});
+				OC.Share.showDropDown(
+					'file',
+					123,
+					$container,
+					true,
+					OC.PERMISSION_ALL,
+					'shared_file_name.txt'
+				);
+				expect(dialog.$el.find('#shareWithList').length).toEqual(0);
+			});
+			it('allows owner to share their own share when they are also the recipient', function() {
+				OC.currentUser = 'user1';
+				loadItemStub.returns({
+					reshare: {
+						permissions: OC.PERMISSION_READ,
+						uid_owner: 'user1'
+					},
+					shares: []
+				});
+				OC.Share.showDropDown(
+					'file',
+					123,
+					$container,
+					true,
+					OC.PERMISSION_ALL,
+					'shared_file_name.txt'
+				);
+				// sharing still allowed
+				expect(dialog.$el.find('#shareWithList').length).toEqual(1);
+			});
+		});
+	});
+});
+
diff --git a/core/js/tests/specs/shareitemmodelSpec.js b/core/js/tests/specs/shareitemmodelSpec.js
new file mode 100644
index 0000000000000000000000000000000000000000..d3c4b7d4004ba1774701618d05a0f218117c7db5
--- /dev/null
+++ b/core/js/tests/specs/shareitemmodelSpec.js
@@ -0,0 +1,234 @@
+/**
+* ownCloud
+*
+* @author Vincent Petry
+* @copyright 2015 Vincent Petry <pvince81@owncloud.com>
+*
+* This library is free software; you can redistribute it and/or
+* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+* License as published by the Free Software Foundation; either
+* version 3 of the License, or any later version.
+*
+* This library is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+*
+* You should have received a copy of the GNU Affero General Public
+* License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*
+*/
+
+/* global oc_appconfig */
+describe('OC.Share.ShareItemModel', function() {
+	var loadItemStub;
+	var fileInfoModel, configModel, model;
+
+	beforeEach(function() {
+		loadItemStub = sinon.stub(OC.Share, 'loadItem');
+		loadItemStub.returns({
+			reshare: [],
+			shares: []
+		});
+
+		fileInfoModel = new OCA.Files.FileInfoModel({
+			id: 123,
+			name: 'shared_file_name.txt',
+			path: '/subdir',
+			size: 100,
+			mimetype: 'text/plain',
+			permissions: 31,
+			sharePermissions: 31
+		});
+
+		var attributes = {
+			itemType: fileInfoModel.isDirectory() ? 'folder' : 'file',
+			itemSource: fileInfoModel.get('id'),
+			possiblePermissions: fileInfoModel.get('sharePermissions')
+		};
+		configModel = new OC.Share.ShareConfigModel();
+		model = new OC.Share.ShareItemModel(attributes, {
+			configModel: configModel,
+			fileInfoModel: fileInfoModel
+		});
+	});
+	afterEach(function() {
+		loadItemStub.restore();
+	});
+
+	describe('Fetching and parsing', function() {
+		it('fetching calls loadItem with the correct arguments', function() {
+			model.fetch();
+
+			expect(loadItemStub.calledOnce).toEqual(true);
+			expect(loadItemStub.calledWith('file', 123)).toEqual(true);
+		});
+		it('populates attributes with parsed response', function() {
+			loadItemStub.returns({
+				/* jshint camelcase: false */
+				reshare: {
+					share_type: OC.Share.SHARE_TYPE_USER,
+					uid_owner: 'owner',
+					displayname_owner: 'Owner',
+					permissions: 31
+				},
+				shares: [{
+					id: 100,
+					item_source: 123,
+					permissions: 31,
+					share_type: OC.Share.SHARE_TYPE_USER,
+					share_with: 'user1',
+					share_with_displayname: 'User One'
+				}, {
+					id: 101,
+					item_source: 123,
+					permissions: 31,
+					share_type: OC.Share.SHARE_TYPE_GROUP,
+					share_with: 'group',
+					share_with_displayname: 'group'
+				}, {
+					id: 102,
+					item_source: 123,
+					permissions: 31,
+					share_type: OC.Share.SHARE_TYPE_REMOTE,
+					share_with: 'foo@bar.com/baz',
+					share_with_displayname: 'foo@bar.com/baz'
+
+				}, {
+					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 shares = model.get('shares');
+			expect(shares.length).toEqual(3);
+			expect(shares.file_source).toEqual(123);
+
+			var linkShare = model.get('linkShare');
+			expect(linkShare.isLinkShare).toEqual(true);
+
+			// TODO: check more attributes
+		});
+		it('does not parse link share when for a different file', function() {
+			loadItemStub.returns({
+				reshare: [],
+				/* jshint camelcase: false */
+				shares: [{
+					displayname_owner: 'root',
+					expiration: null,
+					file_source: 456,
+					file_target: '/folder',
+					id: 20,
+					item_source: '456',
+					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 shares = model.get('shares');
+			expect(shares.length).toEqual(0);
+
+			var linkShare = model.get('linkShare');
+			expect(linkShare.isLinkShare).toEqual(false);
+		});
+		it('parsess correct link share when a nested link share exists along with parent one', function() {
+			loadItemStub.returns({
+				reshare: [],
+				/* jshint camelcase: false */
+				shares: [{
+					displayname_owner: 'root',
+					expiration: 1111,
+					file_source: 123,
+					file_target: '/folder',
+					id: 20,
+					item_source: '123',
+					item_type: 'file',
+					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'
+				}, {
+					displayname_owner: 'root',
+					expiration: 2222,
+					file_source: 456,
+					file_target: '/file_in_folder.txt',
+					id: 21,
+					item_source: '456',
+					item_type: 'file',
+					mail_send: '0',
+					parent: null,
+					path: '/folder/file_in_folder.txt',
+					permissions: OC.PERMISSION_READ,
+					share_type: OC.Share.SHARE_TYPE_LINK,
+					share_with: null,
+					stime: 1403884509,
+					storage: 1,
+					token: 'anothertoken',
+					uid_owner: 'root'
+				}]
+			});
+
+			model.fetch();
+
+			var shares = model.get('shares');
+			expect(shares.length).toEqual(0);
+
+			var linkShare = model.get('linkShare');
+			expect(linkShare.isLinkShare).toEqual(false);
+			expect(linkShare.token).toEqual('tehtoken');
+
+			// TODO: check child too
+		});
+	});
+
+	describe('Util', function() {
+		it('parseTime should properly parse strings', function() {
+
+			_.each([
+				[ '123456', 123456],
+				[  123456 , 123456],
+				['0123456', 123456],
+				['abcdefg',   null],
+				['0x12345',   null],
+				[       '',   null],
+			], function(value) {
+				expect(OC.Share._parseTime(value[0])).toEqual(value[1]);
+			});
+
+		});
+	});
+});
+
diff --git a/tests/karma.config.js b/tests/karma.config.js
index d3280b2939acce6e9c4b9fadd8c872084115499c..64a94ef230bcd83014dd85e841dd0d8e107129e8 100644
--- a/tests/karma.config.js
+++ b/tests/karma.config.js
@@ -55,7 +55,8 @@ module.exports = function(config) {
 					'apps/files_sharing/js/sharedfilelist.js',
 					'apps/files_sharing/js/share.js',
 					'apps/files_sharing/js/external.js',
-					'apps/files_sharing/js/public.js'
+					'apps/files_sharing/js/public.js',
+					'apps/files_sharing/js/sharetabview.js'
 				],
 				testFiles: ['apps/files_sharing/tests/js/*.js']
 			},