summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Müller <thomas.mueller@tmit.eu>2015-09-16 09:40:53 +0200
committerThomas Müller <thomas.mueller@tmit.eu>2015-09-16 09:40:53 +0200
commitafc7eeacaffac3034f34c880597f6655ce1bfc25 (patch)
treeb73fe9b5427453814a6244b85201fc84b3e0879a
parentaf517103b11b05e57fcdb35706c63783e3f475f9 (diff)
parente7e0cfe93d1e7050f35a4b78c9fc70dbe32ec397 (diff)
downloadnextcloud-server-afc7eeacaffac3034f34c880597f6655ce1bfc25.tar.gz
nextcloud-server-afc7eeacaffac3034f34c880597f6655ce1bfc25.zip
Merge pull request #18185 from owncloud/share-dialog-files-sidebar
Share dialog files sidebar
-rw-r--r--apps/files/js/filelist.js15
-rw-r--r--apps/files_sharing/appinfo/app.php15
-rw-r--r--apps/files_sharing/css/sharetabview.css72
-rw-r--r--apps/files_sharing/js/share.js96
-rw-r--r--apps/files_sharing/js/sharetabview.js52
-rw-r--r--apps/files_sharing/tests/js/shareSpec.js125
-rw-r--r--core/css/share.css38
-rw-r--r--core/js/core.json7
-rw-r--r--core/js/share.js934
-rw-r--r--core/js/shareconfigmodel.js83
-rw-r--r--core/js/sharedialogexpirationview.js195
-rw-r--r--core/js/sharedialoglinkshareview.js298
-rw-r--r--core/js/sharedialogresharerinfoview.js124
-rw-r--r--core/js/sharedialogshareelistview.js302
-rw-r--r--core/js/sharedialogview.js260
-rw-r--r--core/js/shareitemmodel.js750
-rw-r--r--core/js/tests/specs/shareSpec.js1100
-rw-r--r--core/js/tests/specs/sharedialogviewSpec.js582
-rw-r--r--core/js/tests/specs/shareitemmodelSpec.js283
-rw-r--r--lib/private/share/share.php7
-rw-r--r--tests/karma.config.js3
21 files changed, 3191 insertions, 2150 deletions
diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js
index 194c658e3a1..23834fa44de 100644
--- a/apps/files/js/filelist.js
+++ b/apps/files/js/filelist.js
@@ -368,6 +368,21 @@
},
/**
+ * Displays the details view for the given file and
+ * selects the given tab
+ *
+ * @param {string} fileName file name for which to show details
+ * @param {string} [tabId] optional tab id to select
+ */
+ showDetailsView: function(fileName, tabId) {
+ this._updateDetailsView(fileName);
+ if (tabId) {
+ this._detailsView.selectTab(tabId);
+ }
+ OC.Apps.showAppSidebar();
+ },
+
+ /**
* Update the details view to display the given file
*
* @param {string} fileName file name from the current list
diff --git a/apps/files_sharing/appinfo/app.php b/apps/files_sharing/appinfo/app.php
index 20f1b046d35..8d919d1466f 100644
--- a/apps/files_sharing/appinfo/app.php
+++ b/apps/files_sharing/appinfo/app.php
@@ -54,9 +54,18 @@ $application->setupPropagation();
\OCP\Share::registerBackend('file', 'OC_Share_Backend_File');
\OCP\Share::registerBackend('folder', 'OC_Share_Backend_Folder', 'file');
-\OCP\Util::addScript('files_sharing', 'share');
-\OCP\Util::addScript('files_sharing', 'external');
-\OCP\Util::addStyle('files_sharing', 'sharetabview');
+$eventDispatcher = \OC::$server->getEventDispatcher();
+$eventDispatcher->addListener(
+ 'OCA\Files::loadAdditionalScripts',
+ function() {
+ \OCP\Util::addScript('files_sharing', 'share');
+ \OCP\Util::addScript('files_sharing', 'sharetabview');
+ \OCP\Util::addScript('files_sharing', 'external');
+ \OCP\Util::addStyle('files_sharing', 'sharetabview');
+ }
+);
+
+// \OCP\Util::addStyle('files_sharing', 'sharetabview');
\OC::$server->getActivityManager()->registerExtension(function() {
return new \OCA\Files_Sharing\Activity(
diff --git a/apps/files_sharing/css/sharetabview.css b/apps/files_sharing/css/sharetabview.css
index 42c9bee7173..0cc812e917c 100644
--- a/apps/files_sharing/css/sharetabview.css
+++ b/apps/files_sharing/css/sharetabview.css
@@ -1,3 +1,75 @@
.app-files .shareTabView {
min-height: 100px;
}
+
+.shareTabView .oneline { white-space: nowrap; }
+
+.shareTabView .shareWithLoading {
+ padding-left: 10px;
+ position: relative;
+ right: 30px;
+ top: 2px;
+}
+
+.shareTabView .shareWithRemoteInfo {
+ padding: 11px 0 11px 10px
+}
+
+.shareTabView label {
+ font-weight:400;
+ white-space: nowrap;
+}
+
+.shareTabView input[type="checkbox"] {
+ margin:0 3px 0 8px;
+ vertical-align: middle;
+}
+
+.shareTabView input[type="text"], .shareTabView input[type="password"] {
+ width: 91%;
+ margin-left: 7px;
+}
+
+.shareTabView form {
+ font-size: 100%;
+ margin-left: 0;
+ margin-right: 0;
+}
+
+#shareWithList {
+ list-style-type:none;
+ padding:8px;
+}
+
+#shareWithList li {
+ padding-top: 10px;
+ padding-bottom: 10px;
+ font-weight: bold;
+ line-height: 21px;
+ white-space: normal;
+}
+
+#shareWithList .unshare img, #shareWithList .showCruds img {
+ vertical-align:text-bottom; /* properly align icons */
+}
+
+#shareWithList label input[type=checkbox]{
+ margin-left: 0;
+ position: relative;
+}
+#shareWithList .username{
+ padding-right: 8px;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ max-width: 254px;
+ display: inline-block;
+ overflow: hidden;
+ vertical-align: middle;
+}
+#shareWithList li label{
+ margin-right: 8px;
+}
+
+.shareTabView .icon-loading-small {
+ margin-left: -30px;
+}
diff --git a/apps/files_sharing/js/share.js b/apps/files_sharing/js/share.js
index c124d390d04..5290dfbb7d1 100644
--- a/apps/files_sharing/js/share.js
+++ b/apps/files_sharing/js/share.js
@@ -79,7 +79,9 @@
$files = fileList.$fileList.find('tr');
}
_.each($files, function(file) {
- OCA.Sharing.Util.updateFileActionIcon($(file));
+ var $tr = $(file);
+ var shareStatus = OC.Share.statuses[$tr.data('id')];
+ OCA.Sharing.Util._updateFileActionIcon($tr, !!shareStatus, shareStatus && shareStatus.link);
});
}
@@ -104,71 +106,59 @@
permissions: OC.PERMISSION_SHARE,
icon: OC.imagePath('core', 'actions/share'),
type: OCA.Files.FileActions.TYPE_INLINE,
- actionHandler: function(filename, context) {
- var $tr = context.$file;
- var itemType = 'file';
- if ($tr.data('type') === 'dir') {
- itemType = 'folder';
- }
- var possiblePermissions = $tr.data('share-permissions');
- if (_.isUndefined(possiblePermissions)) {
- possiblePermissions = $tr.data('permissions');
- }
-
- var appendTo = $tr.find('td.filename');
- // Check if drop down is already visible for a different file
- if (OC.Share.droppedDown) {
- if ($tr.attr('data-id') !== $('#dropdown').attr('data-item-source')) {
- OC.Share.hideDropDown(function () {
- $tr.addClass('mouseOver');
- OC.Share.showDropDown(itemType, $tr.data('id'), appendTo, true, possiblePermissions, filename);
- });
- } else {
- OC.Share.hideDropDown();
- }
- } else {
- $tr.addClass('mouseOver');
- OC.Share.showDropDown(itemType, $tr.data('id'), appendTo, true, possiblePermissions, filename);
- }
- $('#dropdown').on('sharesChanged', function(ev) {
- // files app current cannot show recipients on load, so we don't update the
- // icon when changed for consistency
- if (context.fileList.$el.closest('#app-content-files').length) {
- return;
- }
- var recipients = _.pluck(ev.shares[OC.Share.SHARE_TYPE_USER], 'share_with_displayname');
- var groupRecipients = _.pluck(ev.shares[OC.Share.SHARE_TYPE_GROUP], 'share_with_displayname');
- recipients = recipients.concat(groupRecipients);
- // note: we only update the data attribute because updateIcon()
- // is called automatically after this event
- if (recipients.length) {
- $tr.attr('data-share-recipients', OCA.Sharing.Util.formatRecipients(recipients));
- }
- else {
- $tr.removeAttr('data-share-recipients');
- }
- });
+ actionHandler: function(fileName) {
+ fileList.showDetailsView(fileName, 'shareTabView');
}
});
- OC.addScript('files_sharing', 'sharetabview').done(function() {
- fileList.registerTabView(new OCA.Sharing.ShareTabView('shareTabView'));
+ var shareTab = new OCA.Sharing.ShareTabView('shareTabView');
+ // detect changes and change the matching list entry
+ shareTab.on('sharesChanged', function(shareModel) {
+ var fileInfoModel = shareModel.fileInfoModel;
+ var $tr = fileList.findFileEl(fileInfoModel.get('name'));
+ OCA.Sharing.Util._updateFileListDataAttributes(fileList, $tr, shareModel);
+ if (!OCA.Sharing.Util._updateFileActionIcon($tr, shareModel.hasUserShares(), shareModel.hasLinkShare())) {
+ // remove icon, if applicable
+ OC.Share.markFileAsShared($tr, false, false);
+ }
});
+ fileList.registerTabView(shareTab);
+ },
+
+ /**
+ * Update file list data attributes
+ */
+ _updateFileListDataAttributes: function(fileList, $tr, shareModel) {
+ // files app current cannot show recipients on load, so we don't update the
+ // icon when changed for consistency
+ if (fileList.id === 'files') {
+ return;
+ }
+ var recipients = _.pluck(shareModel.get('shares'), 'share_with_displayname');
+ // note: we only update the data attribute because updateIcon()
+ if (recipients.length) {
+ $tr.attr('data-share-recipients', OCA.Sharing.Util.formatRecipients(recipients));
+ }
+ else {
+ $tr.removeAttr('data-share-recipients');
+ }
},
/**
* Update the file action share icon for the given file
*
* @param $tr file element of the file to update
+ * @param {bool} hasUserShares true if a user share exists
+ * @param {bool} hasLinkShare true if a link share exists
+ *
+ * @return {bool} true if the icon was set, false otherwise
*/
- updateFileActionIcon: function($tr) {
+ _updateFileActionIcon: function($tr, hasUserShares, hasLinkShare) {
// if the statuses are loaded already, use them for the icon
// (needed when scrolling to the next page)
- var shareStatus = OC.Share.statuses[$tr.data('id')];
- if (shareStatus || $tr.attr('data-share-recipients') || $tr.attr('data-share-owner')) {
+ if (hasUserShares || hasLinkShare || $tr.attr('data-share-recipients') || $tr.attr('data-share-owner')) {
var permissions = $tr.data('permissions');
- var hasLink = !!(shareStatus && shareStatus.link);
- OC.Share.markFileAsShared($tr, true, hasLink);
+ OC.Share.markFileAsShared($tr, true, hasLinkShare);
if ((permissions & OC.PERMISSION_SHARE) === 0 && $tr.attr('data-share-owner')) {
// if no share action exists because the admin disabled sharing for this user
// we create a share notification action to inform the user about files
@@ -187,7 +177,9 @@
return $result;
});
}
+ return true;
}
+ return false;
},
/**
diff --git a/apps/files_sharing/js/sharetabview.js b/apps/files_sharing/js/sharetabview.js
index ee572b747ea..e24320604fb 100644
--- a/apps/files_sharing/js/sharetabview.js
+++ b/apps/files_sharing/js/sharetabview.js
@@ -10,7 +10,9 @@
(function() {
var TEMPLATE =
- '<div><ul>{{#if owner}}<li>Owner: {{owner}}</li>{{/if}}</ul></div>';
+ '<div>' +
+ '<div class="dialogContainer"></div>' +
+ '</div>';
/**
* @memberof OCA.Sharing
@@ -20,7 +22,12 @@
id: 'shareTabView',
className: 'tab shareTabView',
- _template: null,
+ template: function(params) {
+ if (!this._template) {
+ this._template = Handlebars.compile(TEMPLATE);
+ }
+ return this._template(params);
+ },
getLabel: function() {
return t('files_sharing', 'Sharing');
@@ -30,23 +37,40 @@
* Renders this details view
*/
render: function() {
- this.$el.empty();
-
- if (!this._template) {
- this._template = Handlebars.compile(TEMPLATE);
+ var self = this;
+ if (this._dialog) {
+ // remove/destroy older instance
+ this._dialog.model.off();
+ this._dialog.remove();
+ this._dialog = null;
}
if (this.model) {
- console.log(this.model);
- var owner = this.model.get('shareOwner');
- if (owner === OC.currentUser) {
- owner = null;
- }
- this.$el.append(this._template({
- owner: owner
- }));
+ this.$el.html(this.template());
+ // TODO: the model should read these directly off the passed fileInfoModel
+ var attributes = {
+ itemType: this.model.isDirectory() ? 'folder' : 'file',
+ itemSource: this.model.get('id'),
+ possiblePermissions: this.model.get('sharePermissions')
+ };
+ var configModel = new OC.Share.ShareConfigModel();
+ var shareModel = new OC.Share.ShareItemModel(attributes, {
+ configModel: configModel,
+ fileInfoModel: this.model
+ });
+ this._dialog = new OC.Share.ShareDialogView({
+ configModel: configModel,
+ model: shareModel
+ });
+ this.$el.find('.dialogContainer').append(this._dialog.$el);
+ this._dialog.render();
+ this._dialog.model.fetch();
+ this._dialog.model.on('change', function() {
+ self.trigger('sharesChanged', shareModel);
+ });
} else {
+ this.$el.empty();
// TODO: render placeholder text?
}
}
diff --git a/apps/files_sharing/tests/js/shareSpec.js b/apps/files_sharing/tests/js/shareSpec.js
index b6368b901ee..96a96f1b814 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/css/share.css b/core/css/share.css
index 0d687cb76da..6467cc0239c 100644
--- a/core/css/share.css
+++ b/core/css/share.css
@@ -23,29 +23,29 @@
}
}
-#dropdown.shareDropDown .unshare.icon-loading-small {
+.shareTabView .unshare.icon-loading-small {
margin-top: 1px;
}
-#dropdown.shareDropDown .shareWithLoading,
-#dropdown.shareDropDown .linkShare .icon-loading-small {
+.shareTabView .shareWithLoading,
+.shareTabView .linkShare .icon-loading-small {
display: inline-block !important;
padding-left: 10px;
}
-#dropdown.shareDropDown .shareWithLoading {
+.shareTabView .shareWithLoading {
position: relative;
right: 70px;
top: 2px;
}
-#dropdown.shareDropDown .icon-loading-small.hidden {
+.shareTabView .icon-loading-small.hidden {
display: none !important;
}
-#dropdown .shareWithRemoteInfo {
+.shareTabView .shareWithRemoteInfo {
padding: 11px 20px;
}
-#dropdown .avatar {
+.shareTabView .avatar {
margin-right: 8px;
display: inline-block;
overflow: hidden;
@@ -87,12 +87,12 @@
#shareWithList li label{
margin-right: 8px;
}
-#dropdown label {
+.shareTabView label {
font-weight:400;
white-space: nowrap;
}
-#dropdown input[type="checkbox"] {
+.shareTabView input[type="checkbox"] {
margin:0 3px 0 8px;
vertical-align: middle;
}
@@ -115,19 +115,29 @@ a.unshare {
padding-top:8px;
}
-#dropdown input[type="text"],#dropdown input[type="password"] {
- width: 86%;
+.shareTabView input[type="text"],
+.shareTabView input[type="password"],
+.shareTabView input[type="submit"] {
margin-left: 7px;
}
-#dropdown form {
+.shareTabView input[type="text"],
+.shareTabView input[type="password"] {
+ width: 86%;
+}
+
+.shareTabView form {
font-size: 100%;
margin-left: 0;
margin-right: 0;
}
-#linkText,#linkPass,#expiration {
- display:none;
+.shareTabView .error {
+ color: #e9322d;
+ border-color: #e9322d;
+ -webkit-box-shadow: 0 0 6px #f8b9b7;
+ -moz-box-shadow: 0 0 6px #f8b9b7;
+ box-shadow: 0 0 6px #f8b9b7;
}
#link #showPassword img {
diff --git a/core/js/core.json b/core/js/core.json
index a67491c4a35..a80636e8463 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 cd4a614e9d1..a2e6e6af0fc 100644
--- a/core/js/share.js
+++ b/core/js/share.js
@@ -3,7 +3,7 @@
/**
* @namespace
*/
-OC.Share={
+OC.Share = _.extend(OC.Share || {}, {
SHARE_TYPE_USER:0,
SHARE_TYPE_GROUP:1,
SHARE_TYPE_LINK:3,
@@ -289,21 +289,34 @@ OC.Share={
}
img.attr('src', image);
},
- loadItem:function(itemType, itemSource) {
+ /**
+ *
+ * @param itemType
+ * @param itemSource
+ * @param callback - optional. If a callback is given this method works
+ * asynchronous and the callback will be provided with data when the request
+ * is done.
+ * @returns {OC.Share.Types.ShareInfo}
+ */
+ loadItem:function(itemType, itemSource, callback) {
var data = '';
var checkReshare = true;
+ var async = !_.isUndefined(callback);
if (typeof OC.Share.statuses[itemSource] === 'undefined') {
// NOTE: Check does not always work and misses some shares, fix later
var checkShares = true;
} else {
var checkShares = true;
}
- $.ajax({type: 'GET', url: OC.filePath('core', 'ajax', 'share.php'), data: { fetch: 'getItem', itemType: itemType, itemSource: itemSource, checkReshare: checkReshare, checkShares: checkShares }, async: false, success: function(result) {
+ $.ajax({type: 'GET', url: OC.filePath('core', 'ajax', 'share.php'), data: { fetch: 'getItem', itemType: itemType, itemSource: itemSource, checkReshare: checkReshare, checkShares: checkShares }, async: async, success: function(result) {
if (result && result.status === 'success') {
data = result.data;
} else {
data = false;
}
+ if(async) {
+ callback(data);
+ }
}});
return data;
@@ -371,269 +384,27 @@ OC.Share={
});
},
showDropDown:function(itemType, itemSource, appendTo, link, possiblePermissions, filename) {
- var data = OC.Share.loadItem(itemType, itemSource);
- var dropDownEl;
- var html = '<div id="dropdown" class="drop shareDropDown" data-item-type="'+itemType+'" data-item-source="'+itemSource+'">';
- if (data !== false && data.reshare !== false && data.reshare.uid_owner !== undefined && data.reshare.uid_owner !== OC.currentUser) {
- html += '<span class="reshare">';
- if (oc_config.enable_avatars === true) {
- html += '<div class="avatar"></div> ';
- }
-
- if (data.reshare.share_type == OC.Share.SHARE_TYPE_GROUP) {
- html += t('core', 'Shared with you and the group {group} by {owner}', {group: data.reshare.share_with, owner: data.reshare.displayname_owner});
- } else {
- html += t('core', 'Shared with you by {owner}', {owner: data.reshare.displayname_owner});
- }
- html += '</span><br />';
- // reduce possible permissions to what the original share allowed
- possiblePermissions = possiblePermissions & data.reshare.permissions;
- }
-
- if (possiblePermissions & OC.PERMISSION_SHARE) {
- // Determine the Allow Public Upload status.
- // Used later on to determine if the
- // respective checkbox should be checked or
- // not.
-
- var publicUploadEnabled = $('#filestable').data('allow-public-upload');
- if (typeof publicUploadEnabled == 'undefined') {
- publicUploadEnabled = 'no';
- }
- var allowPublicUploadStatus = false;
-
- $.each(data.shares, function(key, value) {
- if (value.share_type === OC.Share.SHARE_TYPE_LINK) {
- allowPublicUploadStatus = (value.permissions & OC.PERMISSION_CREATE) ? true : false;
- return true;
- }
- });
-
- var sharePlaceholder = t('core', 'Share with users or groups …');
- if(oc_appconfig.core.remoteShareAllowed) {
- sharePlaceholder = t('core', 'Share with users, groups or remote users …');
+ var configModel = new OC.Share.ShareConfigModel();
+ var attributes = {itemType: itemType, itemSource: itemSource, possiblePermissions: possiblePermissions};
+ var itemModel = new OC.Share.ShareItemModel(attributes, {configModel: configModel});
+ var dialogView = new OC.Share.ShareDialogView({
+ id: 'dropdown',
+ model: itemModel,
+ configModel: configModel,
+ className: 'drop shareDropDown',
+ attributes: {
+ 'data-item-source-name': filename,
+ 'data-item-type': itemType,
+ 'data-item-soruce': itemSource
}
-
- html += '<label for="shareWith" class="hidden-visually">'+t('core', 'Share')+'</label>';
- html += '<input id="shareWith" type="text" placeholder="' + sharePlaceholder + '" />';
- if(oc_appconfig.core.remoteShareAllowed) {
- var federatedCloudSharingDoc = '<a target="_blank" class="icon-info svg shareWithRemoteInfo" href="{docLink}" '
- + 'title="' + t('core', 'Share with people on other ownClouds using the syntax username@example.com/owncloud') + '"></a>';
- html += federatedCloudSharingDoc.replace('{docLink}', oc_appconfig.core.federatedCloudShareDoc);
- }
- html += '<span class="shareWithLoading icon-loading-small hidden"></span>';
- html += '<ul id="shareWithList">';
- html += '</ul>';
- var linksAllowed = $('#allowShareWithLink').val() === 'yes';
- if (link && linksAllowed) {
- html += '<div id="link" class="linkShare">';
- html += '<span class="icon-loading-small hidden"></span>';
- html += '<input type="checkbox" name="linkCheckbox" id="linkCheckbox" value="1" /><label for="linkCheckbox">'+t('core', 'Share link')+'</label>';
- html += '<br />';
-
- var defaultExpireMessage = '';
- if ((itemType === 'folder' || itemType === 'file') && oc_appconfig.core.defaultExpireDateEnforced) {
- defaultExpireMessage = t('core', 'The public link will expire no later than {days} days after it is created', {'days': oc_appconfig.core.defaultExpireDate}) + '<br/>';
- }
-
- html += '<label for="linkText" class="hidden-visually">'+t('core', 'Link')+'</label>';
- html += '<input id="linkText" type="text" readonly="readonly" />';
- html += '<input type="checkbox" name="showPassword" id="showPassword" value="1" style="display:none;" /><label for="showPassword" style="display:none;">'+t('core', 'Password protect')+'</label>';
- html += '<div id="linkPass">';
- html += '<label for="linkPassText" class="hidden-visually">'+t('core', 'Password')+'</label>';
- html += '<input id="linkPassText" type="password" placeholder="'+t('core', 'Choose a password for the public link')+'" />';
- html += '<span class="icon-loading-small hidden"></span>';
- html += '</div>';
-
- if (itemType === 'folder' && (possiblePermissions & OC.PERMISSION_CREATE) && publicUploadEnabled === 'yes') {
- html += '<div id="allowPublicUploadWrapper" style="display:none;">';
- html += '<span class="icon-loading-small hidden"></span>';
- html += '<input type="checkbox" value="1" name="allowPublicUpload" id="sharingDialogAllowPublicUpload"' + ((allowPublicUploadStatus) ? 'checked="checked"' : '') + ' />';
- html += '<label for="sharingDialogAllowPublicUpload">' + t('core', 'Allow editing') + '</label>';
- html += '</div>';
- }
- html += '</div>';
- var mailPublicNotificationEnabled = $('input:hidden[name=mailPublicNotificationEnabled]').val();
- if (mailPublicNotificationEnabled === 'yes') {
- html += '<form id="emailPrivateLink">';
- html += '<input id="email" style="display:none; width:62%;" value="" placeholder="'+t('core', 'Email link to person')+'" type="text" />';
- html += '<input id="emailButton" style="display:none;" type="submit" value="'+t('core', 'Send')+'" />';
- html += '</form>';
- }
- }
-
- html += '<div id="expiration">';
- html += '<input type="checkbox" name="expirationCheckbox" id="expirationCheckbox" value="1" /><label for="expirationCheckbox">'+t('core', 'Set expiration date')+'</label>';
- html += '<label for="expirationDate" class="hidden-visually">'+t('core', 'Expiration')+'</label>';
- html += '<input id="expirationDate" type="text" placeholder="'+t('core', 'Expiration date')+'" style="display:none; width:90%;" />';
- html += '<em id="defaultExpireMessage">'+defaultExpireMessage+'</em>';
- html += '</div>';
- dropDownEl = $(html);
- dropDownEl = dropDownEl.appendTo(appendTo);
-
- // trigger remote share info tooltip
- if(oc_appconfig.core.remoteShareAllowed) {
- $('.shareWithRemoteInfo').tipsy({gravity: 'e'});
- }
-
- //Get owner avatars
- if (oc_config.enable_avatars === true && data !== false && data.reshare !== false && data.reshare.uid_owner !== undefined) {
- dropDownEl.find(".avatar").avatar(data.reshare.uid_owner, 32);
- }
-
- // Reset item shares
- OC.Share.itemShares = [];
- OC.Share.currentShares = {};
- if (data.shares) {
- $.each(data.shares, function(index, share) {
- if (share.share_type == OC.Share.SHARE_TYPE_LINK) {
- if (itemSource === share.file_source || itemSource === share.item_source) {
- OC.Share.showLink(share.token, share.share_with, itemSource);
- }
- } else {
- if (share.collection) {
- OC.Share.addShareWith(share.share_type, share.share_with, share.share_with_displayname, share.permissions, possiblePermissions, share.mail_send, share.collection);
- } else {
- if (share.share_type === OC.Share.SHARE_TYPE_REMOTE) {
- OC.Share.addShareWith(share.share_type, share.share_with, share.share_with_displayname, share.permissions, OC.PERMISSION_READ | OC.PERMISSION_UPDATE | OC.PERMISSION_CREATE, share.mail_send, false);
- } else {
- OC.Share.addShareWith(share.share_type, share.share_with, share.share_with_displayname, share.permissions, possiblePermissions, share.mail_send, false);
- }
- }
- }
- if (share.expiration != null) {
- OC.Share.showExpirationDate(share.expiration, share.stime);
- }
- });
- }
- $('#shareWith').autocomplete({minLength: 2, delay: 750, source: function(search, response) {
- var $loading = $('#dropdown .shareWithLoading');
- $loading.removeClass('hidden');
- $.get(OC.filePath('core', 'ajax', 'share.php'), { fetch: 'getShareWith', search: search.term.trim(), limit: 200, itemShares: OC.Share.itemShares, itemType: itemType }, function(result) {
- $loading.addClass('hidden');
- if (result.status == 'success' && result.data.length > 0) {
- $( "#shareWith" ).autocomplete( "option", "autoFocus", true );
- response(result.data);
- } else {
- response();
- }
- }).fail(function(){
- $('#dropdown').find('.shareWithLoading').addClass('hidden');
- OC.Notification.show(t('core', 'An error occured. Please try again'));
- window.setTimeout(OC.Notification.hide, 5000);
- });
- },
- focus: function(event, focused) {
- event.preventDefault();
- },
- select: function(event, selected) {
- event.stopPropagation();
- var $dropDown = $('#dropdown');
- var itemType = $dropDown.data('item-type');
- var itemSource = $dropDown.data('item-source');
- var itemSourceName = $dropDown.data('item-source-name');
- var expirationDate = '';
- if ( $('#expirationCheckbox').is(':checked') === true ) {
- expirationDate = $( "#expirationDate" ).val();
- }
- var shareType = selected.item.value.shareType;
- var shareWith = selected.item.value.shareWith;
- $(this).val(shareWith);
- // Default permissions are Edit (CRUD) and Share
- // Check if these permissions are possible
- var permissions = OC.PERMISSION_READ;
- if (shareType === OC.Share.SHARE_TYPE_REMOTE) {
- permissions = OC.PERMISSION_CREATE | OC.PERMISSION_UPDATE | OC.PERMISSION_READ;
- } else {
- if (possiblePermissions & OC.PERMISSION_UPDATE) {
- permissions = permissions | OC.PERMISSION_UPDATE;
- }
- if (possiblePermissions & OC.PERMISSION_CREATE) {
- permissions = permissions | OC.PERMISSION_CREATE;
- }
- if (possiblePermissions & OC.PERMISSION_DELETE) {
- permissions = permissions | OC.PERMISSION_DELETE;
- }
- if (oc_appconfig.core.resharingAllowed && (possiblePermissions & OC.PERMISSION_SHARE)) {
- permissions = permissions | OC.PERMISSION_SHARE;
- }
- }
-
- var $input = $(this);
- var $loading = $dropDown.find('.shareWithLoading');
- $loading.removeClass('hidden');
- $input.val(t('core', 'Adding user...'));
- $input.prop('disabled', true);
-
- OC.Share.share(itemType, itemSource, shareType, shareWith, permissions, itemSourceName, expirationDate, function() {
- $input.prop('disabled', false);
- $loading.addClass('hidden');
- var posPermissions = possiblePermissions;
- if (shareType === OC.Share.SHARE_TYPE_REMOTE) {
- posPermissions = permissions;
- }
- OC.Share.addShareWith(shareType, shareWith, selected.item.label, permissions, posPermissions);
- $('#shareWith').val('');
- $('#dropdown').trigger(new $.Event('sharesChanged', {shares: OC.Share.currentShares}));
- OC.Share.updateIcon(itemType, itemSource);
- });
- return false;
- }
- })
- // customize internal _renderItem function to display groups and users differently
- .data("ui-autocomplete")._renderItem = function( ul, item ) {
- var insert = $( "<a>" );
- var text = item.label;
- if (item.value.shareType === OC.Share.SHARE_TYPE_GROUP) {
- text = text + ' ('+t('core', 'group')+')';
- } else if (item.value.shareType === OC.Share.SHARE_TYPE_REMOTE) {
- text = text + ' ('+t('core', 'remote')+')';
- }
- insert.text( text );
- if(item.value.shareType === OC.Share.SHARE_TYPE_GROUP) {
- insert = insert.wrapInner('<strong></strong>');
- }
- return $( "<li>" )
- .addClass((item.value.shareType === OC.Share.SHARE_TYPE_GROUP)?'group':'user')
- .append( insert )
- .appendTo( ul );
- };
- if (link && linksAllowed && $('#email').length != 0) {
- $('#email').autocomplete({
- minLength: 1,
- source: function (search, response) {
- $.get(OC.filePath('core', 'ajax', 'share.php'), { fetch: 'getShareWithEmail', search: search.term }, function(result) {
- if (result.status == 'success' && result.data.length > 0) {
- response(result.data);
- }
- });
- },
- select: function( event, item ) {
- $('#email').val(item.item.email);
- return false;
- }
- })
- .data("ui-autocomplete")._renderItem = function( ul, item ) {
- return $('<li>')
- .append('<a>' + escapeHTML(item.displayname) + "<br>" + escapeHTML(item.email) + '</a>' )
- .appendTo( ul );
- };
- }
-
- } else {
- html += '<input id="shareWith" type="text" placeholder="'+t('core', 'Resharing is not allowed')+'" style="width:90%;" disabled="disabled"/>';
- html += '</div>';
- dropDownEl = $(html);
- dropDownEl.appendTo(appendTo);
- }
- dropDownEl.attr('data-item-source-name', filename);
- $('#dropdown').slideDown(OC.menuSpeed, function() {
+ });
+ dialogView.setShowLink(link);
+ var $dialog = dialogView.render().$el;
+ $dialog.appendTo(appendTo);
+ $dialog.slideDown(OC.menuSpeed, function() {
OC.Share.droppedDown = true;
});
- if ($('html').hasClass('lte9')){
- $('#dropdown input[placeholder]').placeholder();
- }
- $('#shareWith').focus();
+ itemModel.fetch();
},
hideDropDown:function(callback) {
OC.Share.currentShares = null;
@@ -648,256 +419,10 @@ OC.Share={
}
});
},
- addShareWith:function(shareType, shareWith, shareWithDisplayName, permissions, possiblePermissions, mailSend, collection) {
- var shareItem = {
- share_type: shareType,
- share_with: shareWith,
- share_with_displayname: shareWithDisplayName,
- permissions: permissions
- };
- if (shareType === OC.Share.SHARE_TYPE_GROUP) {
- shareWithDisplayName = shareWithDisplayName + " (" + t('core', 'group') + ')';
- }
- if (shareType === OC.Share.SHARE_TYPE_REMOTE) {
- shareWithDisplayName = shareWithDisplayName + " (" + t('core', 'remote') + ')';
- }
- if (!OC.Share.itemShares[shareType]) {
- OC.Share.itemShares[shareType] = [];
- }
- OC.Share.itemShares[shareType].push(shareWith);
- if (collection) {
- if (collection.item_type == 'file' || collection.item_type == 'folder') {
- var item = collection.path;
- } else {
- var item = collection.item_source;
- }
- var collectionList = $('#shareWithList li').filterAttr('data-collection', item);
- if (collectionList.length > 0) {
- $(collectionList).append(', '+shareWithDisplayName);
- } else {
- var html = '<li style="clear: both;" data-collection="'+item+'">'+t('core', 'Shared in {item} with {user}', {'item': item, user: shareWithDisplayName})+'</li>';
- $('#shareWithList').prepend(html);
- }
- } else {
- var editChecked = createChecked = updateChecked = deleteChecked = shareChecked = '';
- if (permissions & OC.PERMISSION_CREATE) {
- createChecked = 'checked="checked"';
- editChecked = 'checked="checked"';
- }
- if (permissions & OC.PERMISSION_UPDATE) {
- updateChecked = 'checked="checked"';
- editChecked = 'checked="checked"';
- }
- if (permissions & OC.PERMISSION_DELETE) {
- deleteChecked = 'checked="checked"';
- editChecked = 'checked="checked"';
- }
- if (permissions & OC.PERMISSION_SHARE) {
- shareChecked = 'checked="checked"';
- }
- var html = '<li style="clear: both;" data-share-type="'+escapeHTML(shareType)+'" data-share-with="'+escapeHTML(shareWith)+'" title="' + escapeHTML(shareWith) + '">';
- var showCrudsButton;
- html += '<a href="#" class="unshare"><img class="svg" alt="'+t('core', 'Unshare')+'" title="'+t('core', 'Unshare')+'" src="'+OC.imagePath('core', 'actions/delete')+'"/></a>';
- if (oc_config.enable_avatars === true) {
- html += '<div class="avatar"></div>';
- }
- html += '<span class="username">' + escapeHTML(shareWithDisplayName) + '</span>';
- var mailNotificationEnabled = $('input:hidden[name=mailNotificationEnabled]').val();
- if (mailNotificationEnabled === 'yes' && shareType !== OC.Share.SHARE_TYPE_REMOTE) {
- var checked = '';
- if (mailSend === '1') {
- checked = 'checked';
- }
- html += '<label><input type="checkbox" name="mailNotification" class="mailNotification" ' + checked + ' />'+t('core', 'notify by email')+'</label> ';
- }
- if (oc_appconfig.core.resharingAllowed && (possiblePermissions & OC.PERMISSION_SHARE)) {
- html += '<label><input id="canShare-'+escapeHTML(shareWith)+'" type="checkbox" name="share" class="permissions" '+shareChecked+' data-permissions="'+OC.PERMISSION_SHARE+'" />'+t('core', 'can share')+'</label>';
- }
- if (possiblePermissions & OC.PERMISSION_CREATE || possiblePermissions & OC.PERMISSION_UPDATE || possiblePermissions & OC.PERMISSION_DELETE) {
- html += '<label><input id="canEdit-'+escapeHTML(shareWith)+'" type="checkbox" name="edit" class="permissions" '+editChecked+' />'+t('core', 'can edit')+'</label>';
- }
- if (shareType !== OC.Share.SHARE_TYPE_REMOTE) {
- showCrudsButton = '<a href="#" class="showCruds"><img class="svg" alt="'+t('core', 'access control')+'" src="'+OC.imagePath('core', 'actions/triangle-s')+'"/></a>';
- }
- html += '<div class="cruds" style="display:none;">';
- if (possiblePermissions & OC.PERMISSION_CREATE) {
- html += '<label><input id="canCreate-' + escapeHTML(shareWith) + '" type="checkbox" name="create" class="permissions" ' + createChecked + ' data-permissions="' + OC.PERMISSION_CREATE + '"/>' + t('core', 'create') + '</label>';
- }
- if (possiblePermissions & OC.PERMISSION_UPDATE) {
- html += '<label><input id="canUpdate-' + escapeHTML(shareWith) + '" type="checkbox" name="update" class="permissions" ' + updateChecked + ' data-permissions="' + OC.PERMISSION_UPDATE + '"/>' + t('core', 'change') + '</label>';
- }
- if (possiblePermissions & OC.PERMISSION_DELETE) {
- html += '<label><input id="canDelete-' + escapeHTML(shareWith) + '" type="checkbox" name="delete" class="permissions" ' + deleteChecked + ' data-permissions="' + OC.PERMISSION_DELETE + '"/>' + t('core', 'delete') + '</label>';
- }
- html += '</div>';
- html += '</li>';
- html = $(html).appendTo('#shareWithList');
- if (oc_config.enable_avatars === true) {
- if (shareType === OC.Share.SHARE_TYPE_USER) {
- html.find('.avatar').avatar(escapeHTML(shareWith), 32);
- } else {
- //Add sharetype to generate different seed if there is a group and use with the same name
- html.find('.avatar').imageplaceholder(escapeHTML(shareWith) + ' ' + shareType);
- }
- }
- // insert cruds button into last label element
- var lastLabel = html.find('>label:last');
- if (lastLabel.exists()){
- lastLabel.append(showCrudsButton);
- }
- else{
- html.find('.cruds').before(showCrudsButton);
- }
- if (!OC.Share.currentShares[shareType]) {
- OC.Share.currentShares[shareType] = [];
- }
- OC.Share.currentShares[shareType].push(shareItem);
- }
- },
- showLink:function(token, password, itemSource) {
- OC.Share.itemShares[OC.Share.SHARE_TYPE_LINK] = true;
- $('#linkCheckbox').attr('checked', true);
-
- //check itemType
- var linkSharetype=$('#dropdown').data('item-type');
-
- if (! token) {
- //fallback to pre token link
- var filename = $('tr').filterAttr('data-id', String(itemSource)).data('file');
- var type = $('tr').filterAttr('data-id', String(itemSource)).data('type');
- if ($('#dir').val() == '/') {
- var file = $('#dir').val() + filename;
- } else {
- var file = $('#dir').val() + '/' + filename;
- }
- file = '/'+OC.currentUser+'/files'+file;
- // TODO: use oc webroot ?
- var link = parent.location.protocol+'//'+location.host+OC.linkTo('', 'public.php')+'?service=files&'+type+'='+encodeURIComponent(file);
- } else {
- //TODO add path param when showing a link to file in a subfolder of a public link share
- var service='';
- if(linkSharetype === 'folder' || linkSharetype === 'file'){
- service='files';
- }else{
- service=linkSharetype;
- }
-
- // TODO: use oc webroot ?
- if (service !== 'files') {
- var link = parent.location.protocol+'//'+location.host+OC.linkTo('', 'public.php')+'?service='+service+'&t='+token;
- } else {
- var link = parent.location.protocol+'//'+location.host+OC.generateUrl('/s/')+token;
- }
- }
- $('#linkText').val(link);
- $('#linkText').slideDown(OC.menuSpeed);
- $('#linkText').css('display','block');
- if (oc_appconfig.core.enforcePasswordForPublicLink === false || password === null) {
- $('#showPassword').show();
- $('#showPassword+label').show();
- }
- if (password != null) {
- $('#linkPass').slideDown(OC.menuSpeed);
- $('#showPassword').attr('checked', true);
- $('#linkPassText').attr('placeholder', '**********');
- }
- $('#expiration').show();
- $('#emailPrivateLink #email').show();
- $('#emailPrivateLink #emailButton').show();
- $('#allowPublicUploadWrapper').show();
- },
- hideLink:function() {
- $('#linkText').slideUp(OC.menuSpeed);
- $('#defaultExpireMessage').hide();
- $('#showPassword').hide();
- $('#showPassword+label').hide();
- $('#linkPass').slideUp(OC.menuSpeed);
- $('#emailPrivateLink #email').hide();
- $('#emailPrivateLink #emailButton').hide();
- $('#allowPublicUploadWrapper').hide();
- },
dirname:function(path) {
return path.replace(/\\/g,'/').replace(/\/[^\/]*$/, '');
- },
- /**
- * Parses a string to an valid integer (unix timestamp)
- * @param time
- * @returns {*}
- * @internal Only used to work around a bug in the backend
- */
- _parseTime: function(time) {
- if (_.isString(time)) {
- // skip empty strings and hex values
- if (time === '' || (time.length > 1 && time[0] === '0' && time[1] === 'x')) {
- return null;
- }
- time = parseInt(time, 10);
- if(isNaN(time)) {
- time = null;
- }
- }
- return time;
- },
- /**
- * Displays the expiration date field
- *
- * @param {Date} date current expiration date
- * @param {int} [shareTime] share timestamp in seconds, defaults to now
- */
- showExpirationDate:function(date, shareTime) {
- var now = new Date();
- // min date should always be the next day
- var minDate = new Date();
- minDate.setDate(minDate.getDate()+1);
- var datePickerOptions = {
- minDate: minDate,
- maxDate: null
- };
- // TODO: hack: backend returns string instead of integer
- shareTime = OC.Share._parseTime(shareTime);
- if (_.isNumber(shareTime)) {
- shareTime = new Date(shareTime * 1000);
- }
- if (!shareTime) {
- shareTime = now;
- }
- $('#expirationCheckbox').attr('checked', true);
- $('#expirationDate').val(date);
- $('#expirationDate').slideDown(OC.menuSpeed);
- $('#expirationDate').css('display','block');
- $('#expirationDate').datepicker({
- dateFormat : 'dd-mm-yy'
- });
- if (oc_appconfig.core.defaultExpireDateEnforced) {
- $('#expirationCheckbox').attr('disabled', true);
- shareTime = OC.Util.stripTime(shareTime).getTime();
- // max date is share date + X days
- datePickerOptions.maxDate = new Date(shareTime + oc_appconfig.core.defaultExpireDate * 24 * 3600 * 1000);
- }
- if(oc_appconfig.core.defaultExpireDateEnabled) {
- $('#defaultExpireMessage').slideDown(OC.menuSpeed);
- }
- $.datepicker.setDefaults(datePickerOptions);
- },
- /**
- * Get the default Expire date
- *
- * @return {String} The expire date
- */
- getDefaultExpirationDate:function() {
- var expireDateString = '';
- if (oc_appconfig.core.defaultExpireDateEnabled) {
- var date = new Date().getTime();
- var expireAfterMs = oc_appconfig.core.defaultExpireDate * 24 * 60 * 60 * 1000;
- var expireDate = new Date(date + expireAfterMs);
- var month = expireDate.getMonth() + 1;
- var year = expireDate.getFullYear();
- var day = expireDate.getDate();
- expireDateString = year + "-" + month + '-' + day + ' 00:00:00';
- }
- return expireDateString;
}
-};
+});
$(document).ready(function() {
@@ -915,403 +440,16 @@ $(document).ready(function() {
minDate : minDate
});
}
- $(document).on('click', 'a.share', function(event) {
- event.stopPropagation();
- if ($(this).data('item-type') !== undefined && $(this).data('item') !== undefined) {
- var itemType = $(this).data('item-type');
- var itemSource = $(this).data('item');
- var appendTo = $(this).parent().parent();
- var link = false;
- var possiblePermissions = $(this).data('possible-permissions');
- if ($(this).data('link') !== undefined && $(this).data('link') == true) {
- link = true;
- }
- if (OC.Share.droppedDown) {
- if (itemSource != $('#dropdown').data('item')) {
- OC.Share.hideDropDown(function () {
- OC.Share.showDropDown(itemType, itemSource, appendTo, link, possiblePermissions);
- });
- } else {
- OC.Share.hideDropDown();
- }
- } else {
- OC.Share.showDropDown(itemType, itemSource, appendTo, link, possiblePermissions);
- }
- }
- });
$(this).click(function(event) {
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();
}
});
- $(document).on('click', '#dropdown .showCruds', function() {
- $(this).closest('li').find('.cruds').toggle();
- return false;
- });
-
- $(document).on('click', '#dropdown .unshare', function() {
- var $li = $(this).closest('li');
- var itemType = $('#dropdown').data('item-type');
- var itemSource = $('#dropdown').data('item-source');
- var shareType = $li.data('share-type');
- var shareWith = $li.attr('data-share-with');
- var $button = $(this);
-
- if (!$button.is('a')) {
- $button = $button.closest('a');
- }
-
- if ($button.hasClass('icon-loading-small')) {
- // deletion in progress
- return false;
- }
- $button.empty().addClass('icon-loading-small');
-
- OC.Share.unshare(itemType, itemSource, shareType, shareWith, function() {
- $li.remove();
- var index = OC.Share.itemShares[shareType].indexOf(shareWith);
- OC.Share.itemShares[shareType].splice(index, 1);
- // updated list of shares
- OC.Share.currentShares[shareType].splice(index, 1);
- $('#dropdown').trigger(new $.Event('sharesChanged', {shares: OC.Share.currentShares}));
- OC.Share.updateIcon(itemType, itemSource);
- if (typeof OC.Share.statuses[itemSource] === 'undefined') {
- $('#expiration').slideUp(OC.menuSpeed);
- }
- });
-
- return false;
- });
-
- $(document).on('change', '#dropdown .permissions', function() {
- var li = $(this).closest('li');
- if ($(this).attr('name') == 'edit') {
- var checkboxes = $('.permissions', li);
- var checked = $(this).is(':checked');
- // Check/uncheck Create, Update, and Delete checkboxes if Edit is checked/unck
- $(checkboxes).filter('input[name="create"]').attr('checked', checked);
- $(checkboxes).filter('input[name="update"]').attr('checked', checked);
- $(checkboxes).filter('input[name="delete"]').attr('checked', checked);
- } else {
- var checkboxes = $('.permissions', li);
- // Uncheck Edit if Create, Update, and Delete are not checked
- if (!$(this).is(':checked')
- && !$(checkboxes).filter('input[name="create"]').is(':checked')
- && !$(checkboxes).filter('input[name="update"]').is(':checked')
- && !$(checkboxes).filter('input[name="delete"]').is(':checked'))
- {
- $(checkboxes).filter('input[name="edit"]').attr('checked', false);
- // Check Edit if Create, Update, or Delete is checked
- } else if (($(this).attr('name') == 'create'
- || $(this).attr('name') == 'update'
- || $(this).attr('name') == 'delete'))
- {
- $(checkboxes).filter('input[name="edit"]').attr('checked', true);
- }
- }
- var permissions = OC.PERMISSION_READ;
- $(checkboxes).filter(':not(input[name="edit"])').filter(':checked').each(function(index, checkbox) {
- permissions |= $(checkbox).data('permissions');
- });
- OC.Share.setPermissions($('#dropdown').data('item-type'),
- $('#dropdown').data('item-source'),
- li.data('share-type'),
- li.attr('data-share-with'),
- permissions);
- });
-
- $(document).on('change', '#dropdown #linkCheckbox', function() {
- var $dropDown = $('#dropdown');
- var itemType = $dropDown.data('item-type');
- var itemSource = $dropDown.data('item-source');
- var itemSourceName = $dropDown.data('item-source-name');
- var $loading = $dropDown.find('#link .icon-loading-small');
- var $button = $(this);
-
- if (!$loading.hasClass('hidden')) {
- // already in progress
- return false;
- }
-
- if (this.checked) {
- // Reset password placeholder
- $('#linkPassText').attr('placeholder', t('core', 'Choose a password for the public link'));
- // Reset link
- $('#linkText').val('');
- $('#showPassword').prop('checked', false);
- $('#linkPass').hide();
- $('#sharingDialogAllowPublicUpload').prop('checked', false);
- $('#expirationCheckbox').prop('checked', false);
- $('#expirationDate').hide();
- var expireDateString = '';
- // Create a link
- if (oc_appconfig.core.enforcePasswordForPublicLink === false) {
- expireDateString = OC.Share.getDefaultExpirationDate();
- $loading.removeClass('hidden');
- $button.addClass('hidden');
- $button.prop('disabled', true);
-
- OC.Share.share(itemType, itemSource, OC.Share.SHARE_TYPE_LINK, '', OC.PERMISSION_READ, itemSourceName, expireDateString, function(data) {
- $loading.addClass('hidden');
- $button.removeClass('hidden');
- $button.prop('disabled', false);
- OC.Share.showLink(data.token, null, itemSource);
- $('#dropdown').trigger(new $.Event('sharesChanged', {shares: OC.Share.currentShares}));
- OC.Share.updateIcon(itemType, itemSource);
- });
- } else {
- $('#linkPass').slideToggle(OC.menuSpeed);
- // TODO drop with IE8 drop
- if($('html').hasClass('ie8')) {
- $('#linkPassText').attr('placeholder', null);
- $('#linkPassText').val('');
- }
- $('#linkPassText').focus();
- }
- if (expireDateString !== '') {
- OC.Share.showExpirationDate(expireDateString);
- }
- } else {
- // Delete private link
- OC.Share.hideLink();
- $('#expiration').slideUp(OC.menuSpeed);
- if ($('#linkText').val() !== '') {
- $loading.removeClass('hidden');
- $button.addClass('hidden');
- $button.prop('disabled', true);
- OC.Share.unshare(itemType, itemSource, OC.Share.SHARE_TYPE_LINK, '', function() {
- $loading.addClass('hidden');
- $button.removeClass('hidden');
- $button.prop('disabled', false);
- OC.Share.itemShares[OC.Share.SHARE_TYPE_LINK] = false;
- $('#dropdown').trigger(new $.Event('sharesChanged', {shares: OC.Share.currentShares}));
- OC.Share.updateIcon(itemType, itemSource);
- if (typeof OC.Share.statuses[itemSource] === 'undefined') {
- $('#expiration').slideUp(OC.menuSpeed);
- }
- });
- }
- }
- });
-
- $(document).on('click', '#dropdown #linkText', function() {
- $(this).focus();
- $(this).select();
- });
-
- // Handle the Allow Public Upload Checkbox
- $(document).on('click', '#sharingDialogAllowPublicUpload', function() {
-
- // Gather data
- var $dropDown = $('#dropdown');
- var allowPublicUpload = $(this).is(':checked');
- var itemType = $dropDown.data('item-type');
- var itemSource = $dropDown.data('item-source');
- var itemSourceName = $dropDown.data('item-source-name');
- var expirationDate = '';
- if ($('#expirationCheckbox').is(':checked') === true) {
- expirationDate = $( "#expirationDate" ).val();
- }
- var permissions = 0;
- var $button = $(this);
- var $loading = $dropDown.find('#allowPublicUploadWrapper .icon-loading-small');
-
- if (!$loading.hasClass('hidden')) {
- // already in progress
- return false;
- }
-
- // Calculate permissions
- if (allowPublicUpload) {
- permissions = OC.PERMISSION_UPDATE + OC.PERMISSION_CREATE + OC.PERMISSION_READ;
- } else {
- permissions = OC.PERMISSION_READ;
- }
-
- // Update the share information
- $button.addClass('hidden');
- $button.prop('disabled', true);
- $loading.removeClass('hidden');
- OC.Share.share(itemType, itemSource, OC.Share.SHARE_TYPE_LINK, '', permissions, itemSourceName, expirationDate, function(data) {
- $loading.addClass('hidden');
- $button.removeClass('hidden');
- $button.prop('disabled', false);
- });
- });
-
- $(document).on('click', '#dropdown #showPassword', function() {
- $('#linkPass').slideToggle(OC.menuSpeed);
- if (!$('#showPassword').is(':checked') ) {
- var itemType = $('#dropdown').data('item-type');
- var itemSource = $('#dropdown').data('item-source');
- var itemSourceName = $('#dropdown').data('item-source-name');
- var allowPublicUpload = $('#sharingDialogAllowPublicUpload').is(':checked');
- var permissions = 0;
- var $loading = $('#showPassword .icon-loading-small');
-
- // Calculate permissions
- if (allowPublicUpload) {
- permissions = OC.PERMISSION_UPDATE + OC.PERMISSION_CREATE + OC.PERMISSION_READ;
- } else {
- permissions = OC.PERMISSION_READ;
- }
-
- $loading.removeClass('hidden');
- OC.Share.share(itemType, itemSource, OC.Share.SHARE_TYPE_LINK, '', permissions, itemSourceName).then(function() {
- $loading.addClass('hidden');
- $('#linkPassText').attr('placeholder', t('core', 'Choose a password for the public link'));
- });
- } else {
- $('#linkPassText').focus();
- }
- });
-
- $(document).on('focusout keyup', '#dropdown #linkPassText', function(event) {
- var linkPassText = $('#linkPassText');
- if ( linkPassText.val() != '' && (event.type == 'focusout' || event.keyCode == 13) ) {
- var allowPublicUpload = $('#sharingDialogAllowPublicUpload').is(':checked');
- var dropDown = $('#dropdown');
- var itemType = dropDown.data('item-type');
- var itemSource = dropDown.data('item-source');
- var itemSourceName = $('#dropdown').data('item-source-name');
- var permissions = 0;
- var $loading = dropDown.find('#linkPass .icon-loading-small');
-
- // Calculate permissions
- if (allowPublicUpload) {
- permissions = OC.PERMISSION_UPDATE + OC.PERMISSION_CREATE + OC.PERMISSION_READ;
- } else {
- permissions = OC.PERMISSION_READ;
- }
-
- var expireDateString = OC.Share.getDefaultExpirationDate();
-
- $loading.removeClass('hidden');
- OC.Share.share(itemType, itemSource, OC.Share.SHARE_TYPE_LINK, $('#linkPassText').val(), permissions, itemSourceName, expireDateString, function(data) {
- $loading.addClass('hidden');
- linkPassText.val('');
- linkPassText.attr('placeholder', t('core', 'Password protected'));
-
- if (oc_appconfig.core.enforcePasswordForPublicLink) {
- OC.Share.showLink(data.token, "password set", itemSource);
- OC.Share.updateIcon(itemType, itemSource);
- }
- $('#dropdown').trigger(new $.Event('sharesChanged', {shares: OC.Share.currentShares}));
- }, function(result) {
- $loading.addClass('hidden');
- linkPassText.val('');
- linkPassText.attr('placeholder', result.data.message);
- });
-
- if (expireDateString !== '') {
- OC.Share.showExpirationDate(expireDateString);
- }
- }
- });
-
- $(document).on('click', '#dropdown #expirationCheckbox', function() {
- if (this.checked) {
- OC.Share.showExpirationDate('');
- } else {
- var itemType = $('#dropdown').data('item-type');
- var itemSource = $('#dropdown').data('item-source');
- $.post(OC.filePath('core', 'ajax', 'share.php'), { action: 'setExpirationDate', itemType: itemType, itemSource: itemSource, date: '' }, function(result) {
- if (!result || result.status !== 'success') {
- OC.dialogs.alert(t('core', 'Error unsetting expiration date'), t('core', 'Error'));
- }
- $('#expirationDate').slideUp(OC.menuSpeed);
- if (oc_appconfig.core.defaultExpireDateEnforced === false) {
- $('#defaultExpireMessage').slideDown(OC.menuSpeed);
- }
- });
- }
- });
-
- $(document).on('change', '#dropdown #expirationDate', function() {
- var itemType = $('#dropdown').data('item-type');
- var itemSource = $('#dropdown').data('item-source');
-
- $(this).tipsy('hide');
- $(this).removeClass('error');
-
- $.post(OC.filePath('core', 'ajax', 'share.php'), { action: 'setExpirationDate', itemType: itemType, itemSource: itemSource, date: $(this).val() }, function(result) {
- if (!result || result.status !== 'success') {
- var expirationDateField = $('#dropdown #expirationDate');
- if (!result.data.message) {
- expirationDateField.attr('original-title', t('core', 'Error setting expiration date'));
- } else {
- expirationDateField.attr('original-title', result.data.message);
- }
- expirationDateField.tipsy({gravity: 'n'});
- expirationDateField.tipsy('show');
- expirationDateField.addClass('error');
- } else {
- if (oc_appconfig.core.defaultExpireDateEnforced === 'no') {
- $('#defaultExpireMessage').slideUp(OC.menuSpeed);
- }
- }
- });
- });
-
-
- $(document).on('submit', '#dropdown #emailPrivateLink', function(event) {
- event.preventDefault();
- var link = $('#linkText').val();
- var itemType = $('#dropdown').data('item-type');
- var itemSource = $('#dropdown').data('item-source');
- var file = $('tr').filterAttr('data-id', String(itemSource)).data('file');
- var email = $('#email').val();
- var expirationDate = '';
- if ( $('#expirationCheckbox').is(':checked') === true ) {
- expirationDate = $( "#expirationDate" ).val();
- }
- if (email != '') {
- $('#email').prop('disabled', true);
- $('#email').val(t('core', 'Sending ...'));
- $('#emailButton').prop('disabled', true);
-
- $.post(OC.filePath('core', 'ajax', 'share.php'), { action: 'email', toaddress: email, link: link, itemType: itemType, itemSource: itemSource, file: file, expiration: expirationDate},
- function(result) {
- $('#email').prop('disabled', false);
- $('#emailButton').prop('disabled', false);
- if (result && result.status == 'success') {
- $('#email').css('font-weight', 'bold').val(t('core','Email sent'));
- setTimeout(function() {
- $('#email').css('font-weight', 'normal').val('');
- }, 2000);
- } else {
- OC.dialogs.alert(result.data.message, t('core', 'Error while sharing'));
- }
- });
- }
- });
-
- $(document).on('click', '#dropdown input[name=mailNotification]', function() {
- var $li = $(this).closest('li');
- var itemType = $('#dropdown').data('item-type');
- var itemSource = $('#dropdown').data('item-source');
- var action = '';
- if (this.checked) {
- action = 'informRecipients';
- } else {
- action = 'informRecipientsDisabled';
- }
-
- var shareType = $li.data('share-type');
- var shareWith = $li.attr('data-share-with');
-
- $.post(OC.filePath('core', 'ajax', 'share.php'), {action: action, recipient: shareWith, shareType: shareType, itemSource: itemSource, itemType: itemType}, function(result) {
- if (result.status !== 'success') {
- OC.dialogs.alert(t('core', result.data.message), t('core', 'Warning'));
- }
- });
-
-});
});
diff --git a/core/js/shareconfigmodel.js b/core/js/shareconfigmodel.js
new file mode 100644
index 00000000000..8729698d136
--- /dev/null
+++ b/core/js/shareconfigmodel.js
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2015
+ *
+ * This file is licensed under the Affero General Public License version 3
+ * or later.
+ *
+ * See the COPYING-README file.
+ *
+ */
+
+(function() {
+ if (!OC.Share) {
+ OC.Share = {};
+ 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,
+ enforcePasswordForPublicLink: oc_appconfig.core.enforcePasswordForPublicLink,
+ isDefaultExpireDateEnforced: oc_appconfig.core.defaultExpireDateEnforced === true,
+ isDefaultExpireDateEnabled: oc_appconfig.core.defaultExpireDateEnabled === true,
+ isRemoteShareAllowed: oc_appconfig.core.remoteShareAllowed,
+ defaultExpireDate: oc_appconfig.core.defaultExpireDate,
+ isResharingAllowed: oc_appconfig.core.resharingAllowed
+ },
+
+ /**
+ * @returns {boolean}
+ */
+ areAvatarsEnabled: function() {
+ return oc_config.enable_avatars === true;
+ },
+
+ /**
+ * @returns {boolean}
+ */
+ isPublicUploadEnabled: function() {
+ var publicUploadEnabled = $('#filestable').data('allow-public-upload');
+ return publicUploadEnabled === 'yes';
+ },
+
+ /**
+ * @returns {boolean}
+ */
+ isMailPublicNotificationEnabled: function() {
+ return $('input:hidden[name=mailPublicNotificationEnabled]').val() === 'yes';
+ },
+
+ /**
+ * @returns {boolean}
+ */
+ isShareWithLinkAllowed: function() {
+ return $('#allowShareWithLink').val() === 'yes';
+ },
+
+ /**
+ * @returns {string}
+ */
+ getFederatedShareDocLink: function() {
+ return oc_appconfig.core.federatedCloudShareDoc;
+ },
+
+ getDefaultExpirationDateString: function () {
+ var expireDateString = '';
+ if (this.get('isDefaultExpireDateEnabled')) {
+ var date = new Date().getTime();
+ var expireAfterMs = this.get('defaultExpireDate') * 24 * 60 * 60 * 1000;
+ var expireDate = new Date(date + expireAfterMs);
+ var month = expireDate.getMonth() + 1;
+ var year = expireDate.getFullYear();
+ var day = expireDate.getDate();
+ expireDateString = year + "-" + month + '-' + day + ' 00:00:00';
+ }
+ return expireDateString;
+ }
+ });
+
+
+ OC.Share.ShareConfigModel = ShareConfigModel;
+})();
diff --git a/core/js/sharedialogexpirationview.js b/core/js/sharedialogexpirationview.js
new file mode 100644
index 00000000000..3fba4b135d9
--- /dev/null
+++ b/core/js/sharedialogexpirationview.js
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2015
+ *
+ * This file is licensed under the Affero General Public License version 3
+ * or later.
+ *
+ * See the COPYING-README file.
+ *
+ */
+
+(function() {
+ if (!OC.Share) {
+ OC.Share = {};
+ }
+
+ var TEMPLATE =
+ // currently expiration is only effective for link share.
+ // this is about to change in future. Therefore this is not included
+ // in the LinkShareView to ease reusing it in future. Then,
+ // modifications (getting rid of IDs) are still necessary.
+ '{{#if isLinkShare}}' +
+ '<input type="checkbox" name="expirationCheckbox" class="expirationCheckbox" id="expirationCheckbox" value="1" ' +
+ '{{#if isExpirationSet}}checked="checked"{{/if}} {{#if disableCheckbox}}disabled="disabled"{{/if}} />' +
+ '<label for="expirationCheckbox">{{setExpirationLabel}}</label>' +
+ '<div class="expirationDateContainer {{#unless isExpirationSet}}hidden{{/unless}}">' +
+ ' <label for="expirationDate" class="hidden-visually" value="{{expirationDate}}">{{expirationLabel}}</label>' +
+ ' <input id="expirationDate" class="datepicker" type="text" placeholder="{{expirationDatePlaceholder}}" value="{{expirationValue}}" />' +
+ '</div>' +
+ ' {{#if isExpirationEnforced}}' +
+ // originally the expire message was shown when a default date was set, however it never had text
+ '<em id="defaultExpireMessage">{{defaultExpireMessage}}</em>' +
+ ' {{/if}}' +
+ '{{/if}}'
+ ;
+
+ /**
+ * @class OCA.Share.ShareDialogExpirationView
+ * @member {OC.Share.ShareItemModel} model
+ * @member {jQuery} $el
+ * @memberof OCA.Sharing
+ * @classdesc
+ *
+ * Represents the expiration part in the GUI of the share dialogue
+ *
+ */
+ var ShareDialogExpirationView = OC.Backbone.View.extend({
+ /** @type {string} **/
+ id: 'shareDialogLinkShare',
+
+ /** @type {OC.Share.ShareConfigModel} **/
+ configModel: undefined,
+
+ /** @type {Function} **/
+ _template: undefined,
+
+ /** @type {boolean} **/
+ showLink: true,
+
+ className: 'hidden',
+
+ events: {
+ 'change .expirationCheckbox': '_onToggleExpiration',
+ 'change .datepicker': '_onChangeExpirationDate'
+ },
+
+ initialize: function(options) {
+ if(!_.isUndefined(options.configModel)) {
+ this.configModel = options.configModel;
+ } else {
+ throw 'missing OC.Share.ShareConfigModel';
+ }
+
+ var view = this;
+ this.configModel.on('change:isDefaultExpireDateEnforced', function() {
+ view.render();
+ });
+
+ this.model.on('change:itemType', function() {
+ view.render();
+ });
+
+ this.model.on('change:linkShare', function() {
+ view.render();
+ });
+ },
+
+ _onToggleExpiration: function(event) {
+ var $checkbox = $(event.target);
+ var state = $checkbox.prop('checked');
+ // TODO: slide animation
+ this.$el.find('.expirationDateContainer').toggleClass('hidden', !state);
+ if (!state) {
+ // discard expiration date
+ this.model.setExpirationDate('');
+ this.model.saveLinkShare();
+ }
+ },
+
+ _onChangeExpirationDate: function(event) {
+ var $target = $(event.target);
+ $target.tooltip('hide');
+ $target.removeClass('error');
+
+ this.model.setExpirationDate($target.val());
+ this.model.saveLinkShare(null, {
+ error: function(model, message) {
+ if (!message) {
+ $target.attr('title', t('core', 'Error setting expiration date'));
+ } else {
+ $target.attr('title', message);
+ }
+ $target.tooltip({gravity: 'n'});
+ $target.tooltip('show');
+ $target.addClass('error');
+ }
+ });
+ },
+
+ render: function() {
+ var defaultExpireMessage = '';
+ var defaultExpireDays = this.configModel.get('defaultExpireDate');
+ var isExpirationEnforced = this.configModel.get('isDefaultExpireDateEnforced');
+
+ if( (this.model.isFolder() || this.model.isFile())
+ && isExpirationEnforced) {
+ defaultExpireMessage = t(
+ 'core',
+ 'The public link will expire no later than {days} days after it is created',
+ {'days': defaultExpireDays }
+ );
+ }
+
+ var isExpirationSet = !!this.model.get('linkShare').expiration || isExpirationEnforced;
+
+ var expirationTemplate = this.template();
+ this.$el.html(expirationTemplate({
+ setExpirationLabel: t('core', 'Set expiration date'),
+ expirationLabel: t('core', 'Expiration'),
+ expirationDatePlaceholder: t('core', 'Expiration date'),
+ defaultExpireMessage: defaultExpireMessage,
+ isLinkShare: this.model.get('linkShare').isLinkShare,
+ isExpirationSet: isExpirationSet,
+ isExpirationEnforced: isExpirationEnforced,
+ disableCheckbox: isExpirationEnforced && isExpirationSet,
+ expirationValue: this.model.get('linkShare').expiration
+ }));
+
+ // what if there is another date picker on that page?
+ var minDate = new Date();
+ var maxDate = null;
+ // min date should always be the next day
+ minDate.setDate(minDate.getDate()+1);
+
+ if(isExpirationSet) {
+ if(isExpirationEnforced) {
+ // TODO: hack: backend returns string instead of integer
+ var shareTime = this.model.get('linkShare').stime;
+ if (_.isNumber(shareTime)) {
+ shareTime = new Date(shareTime * 1000);
+ }
+ if (!shareTime) {
+ shareTime = new Date(); // now
+ }
+ shareTime = OC.Util.stripTime(shareTime).getTime();
+ maxDate = new Date(shareTime + defaultExpireDays * 24 * 3600 * 1000);
+ }
+ }
+ $.datepicker.setDefaults({
+ minDate: minDate,
+ maxDate: maxDate
+ });
+
+ this.$el.find('.datepicker').datepicker({dateFormat : 'dd-mm-yy'});
+
+ this.delegateEvents();
+
+ return this;
+ },
+
+ /**
+ * @returns {Function} from Handlebars
+ * @private
+ */
+ template: function () {
+ if (!this._template) {
+ this._template = Handlebars.compile(TEMPLATE);
+ }
+ return this._template;
+ }
+
+ });
+
+ OC.Share.ShareDialogExpirationView = ShareDialogExpirationView;
+
+})();
diff --git a/core/js/sharedialoglinkshareview.js b/core/js/sharedialoglinkshareview.js
new file mode 100644
index 00000000000..ab591b9c6e8
--- /dev/null
+++ b/core/js/sharedialoglinkshareview.js
@@ -0,0 +1,298 @@
+/*
+ * Copyright (c) 2015
+ *
+ * This file is licensed under the Affero General Public License version 3
+ * or later.
+ *
+ * See the COPYING-README file.
+ *
+ */
+
+(function() {
+ if (!OC.Share) {
+ OC.Share = {};
+ }
+
+ var TEMPLATE =
+ '{{#if shareAllowed}}' +
+ '<span class="icon-loading-small hidden"></span>' +
+ '<input type="checkbox" name="linkCheckbox" id="linkCheckbox" value="1" {{#if isLinkShare}}checked="checked"{{/if}} /><label for="linkCheckbox">{{linkShareLabel}}</label>' +
+ '<br />' +
+ '<label for="linkText" class="hidden-visually">{{urlLabel}}</label>' +
+ '<input id="linkText" {{#unless isLinkShare}}class="hidden"{{/unless}} type="text" readonly="readonly" value="{{shareLinkURL}}" />' +
+ ' {{#if showPasswordCheckBox}}' +
+ '<input type="checkbox" name="showPassword" id="showPassword" {{#if isPasswordSet}}checked="checked"{{/if}} value="1" /><label for="showPassword">{{enablePasswordLabel}}</label>' +
+ ' {{/if}}' +
+ '<div id="linkPass" {{#unless isPasswordSet}}class="hidden"{{/unless}}>' +
+ ' <label for="linkPassText" class="hidden-visually">{{passwordLabel}}</label>' +
+ ' <input id="linkPassText" type="password" placeholder="{{passwordPlaceholder}}" />' +
+ ' <span class="icon-loading-small hidden"></span>' +
+ '</div>' +
+ ' {{#if publicUpload}}' +
+ '<div id="allowPublicUploadWrapper">' +
+ ' <span class="icon-loading-small hidden"></span>' +
+ ' <input type="checkbox" value="1" name="allowPublicUpload" id="sharingDialogAllowPublicUpload" {{{publicUploadChecked}}} />' +
+ '<label for="sharingDialogAllowPublicUpload">{{publicUploadLabel}}</label>' +
+ '</div>' +
+ ' {{/if}}' +
+ ' {{#if mailPublicNotificationEnabled}}' +
+ '<form id="emailPrivateLink" class="emailPrivateLinkForm">' +
+ ' <input id="email" value="" placeholder="{{mailPrivatePlaceholder}}" type="text" />' +
+ ' <input id="emailButton" type="submit" value="{{mailButtonText}}" />' +
+ '</form>' +
+ ' {{/if}}' +
+ '{{else}}' +
+ '<input id="shareWith" type="text" placeholder="{{noSharingPlaceholder}}" disabled="disabled"/>' +
+ '{{/if}}'
+ ;
+
+ /**
+ * @class OCA.Share.ShareDialogLinkShareView
+ * @member {OC.Share.ShareItemModel} model
+ * @member {jQuery} $el
+ * @memberof OCA.Sharing
+ * @classdesc
+ *
+ * Represents the GUI of the share dialogue
+ *
+ */
+ var ShareDialogLinkShareView = OC.Backbone.View.extend({
+ /** @type {string} **/
+ id: 'shareDialogLinkShare',
+
+ /** @type {OC.Share.ShareConfigModel} **/
+ configModel: undefined,
+
+ /** @type {Function} **/
+ _template: undefined,
+
+ /** @type {boolean} **/
+ showLink: true,
+
+ events: {
+ 'submit .emailPrivateLinkForm': '_onEmailPrivateLink'
+ },
+
+ initialize: function(options) {
+ var view = this;
+
+ this.model.on('change:permissions', function() {
+ view.render();
+ });
+
+ this.model.on('change:itemType', function() {
+ view.render();
+ });
+
+ this.model.on('change:allowPublicUploadStatus', function() {
+ view.render();
+ });
+
+ this.model.on('change:linkShare', function() {
+ view.render();
+ });
+
+ if(!_.isUndefined(options.configModel)) {
+ this.configModel = options.configModel;
+ } else {
+ throw 'missing OC.Share.ShareConfigModel';
+ }
+
+ _.bindAll(this, 'onLinkCheckBoxChange', 'onPasswordEntered',
+ 'onShowPasswordClick', 'onAllowPublicUploadChange');
+ },
+
+ onLinkCheckBoxChange: function() {
+ var $checkBox = this.$el.find('#linkCheckbox');
+ var $loading = $checkBox.siblings('.icon-loading-small');
+ if(!$loading.hasClass('hidden')) {
+ return false;
+ }
+
+ if($checkBox.is(':checked')) {
+ if(this.configModel.get('enforcePasswordForPublicLink') === false) {
+ $loading.removeClass('hidden');
+ // this will create it
+ this.model.saveLinkShare();
+ } else {
+ this.$el.find('#linkPass').slideToggle(OC.menuSpeed);
+ // TODO drop with IE8 drop
+ if($('html').hasClass('ie8')) {
+ this.$el.find('#linkPassText').attr('placeholder', null);
+ this.$el.find('#linkPassText').val('');
+ }
+ this.$el.find('#linkPassText').focus();
+ }
+ } else {
+ this.model.removeLinkShare();
+ }
+ },
+
+ onLinkTextClick: function() {
+ this.focus();
+ this.select();
+ },
+
+ onShowPasswordClick: function() {
+ this.$el.find('#linkPass').slideToggle(OC.menuSpeed);
+ if(!this.$el.find('#showPassword').is(':checked')) {
+ this.model.setPassword('');
+ this.model.saveLinkShare();
+ } else {
+ this.$el.find('#linkPassText').focus();
+ }
+ },
+
+ onPasswordEntered: function() {
+ var password = this.$el.find('#linkPassText').val();
+ if(password === '') {
+ return;
+ }
+
+ this.$el.find('#linkPass .icon-loading-small')
+ .removeClass('hidden')
+ .addClass('inlineblock');
+
+ this.model.setPassword(password);
+ this.model.saveLinkShare();
+ },
+
+ onAllowPublicUploadChange: function() {
+ this.$el.find('#sharingDialogAllowPublicUpload')
+ .siblings('.icon-loading-small').removeClass('hidden').addClass('inlineblock');
+ this.model.setPublicUpload(this.$el.find('#sharingDialogAllowPublicUpload').is(':checked'));
+ this.model.saveLinkShare();
+ },
+
+ _onEmailPrivateLink: function(event) {
+ event.preventDefault();
+
+ var $emailField = this.$el.find('#email');
+ var $emailButton = this.$el.find('#emailButton');
+ var email = this.$el.find('#email').val();
+ if (email !== '') {
+ $emailField.prop('disabled', true);
+ $emailButton.prop('disabled', true);
+ $emailField.val(t('core', 'Sending ...'));
+ this.model.sendEmailPrivateLink(email).then(function() {
+ $emailField.css('font-weight', 'bold').val(t('core','Email sent'));
+ setTimeout(function() {
+ $emailField.css('font-weight', 'normal').val('');
+ $emailField.prop('disabled', false);
+ $emailButton.prop('disabled', false);
+ }, 2000);
+ });
+ }
+ return false;
+ },
+
+ render: function() {
+ var linkShareTemplate = this.template();
+
+ if( !this.model.sharePermissionPossible()
+ || !this.showLink
+ || !this.configModel.isShareWithLinkAllowed())
+ {
+ this.$el.html(linkShareTemplate({
+ shareAllowed: false,
+ noSharingPlaceholder: t('core', 'Resharing is not allowed')
+ }));
+ return this;
+ }
+
+ var publicUpload =
+ this.model.isFolder()
+ && this.model.createPermissionPossible()
+ && this.configModel.isPublicUploadEnabled();
+
+ var publicUploadChecked = '';
+ if(this.model.isPublicUploadAllowed()) {
+ publicUploadChecked = 'checked="checked"';
+ }
+
+ var isLinkShare = this.model.get('linkShare').isLinkShare;
+ var isPasswordSet = !!this.model.get('linkShare').password;
+ var showPasswordCheckBox = isLinkShare
+ && ( !this.configModel.get('enforcePasswordForPublicLink')
+ || !this.model.get('linkShare').password);
+
+ this.$el.html(linkShareTemplate({
+ shareAllowed: true,
+ isLinkShare: isLinkShare,
+ shareLinkURL: this.model.get('linkShare').link,
+ linkShareLabel: t('core', 'Share link'),
+ urlLabel: t('core', 'Link'),
+ enablePasswordLabel: t('core', 'Password protect'),
+ passwordLabel: t('core', 'Password'),
+ passwordPlaceholder: isPasswordSet ? '**********' : t('core', 'Choose a password for the public link'),
+ isPasswordSet: isPasswordSet,
+ showPasswordCheckBox: showPasswordCheckBox,
+ publicUpload: publicUpload && isLinkShare,
+ publicUploadChecked: publicUploadChecked,
+ publicUploadLabel: t('core', 'Allow editing'),
+ mailPublicNotificationEnabled: isLinkShare && this.configModel.isMailPublicNotificationEnabled(),
+ mailPrivatePlaceholder: t('core', 'Email link to person'),
+ mailButtonText: t('core', 'Send')
+ }));
+
+ // TODO: move this to delegate events instead
+ 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);
+ this.$el.find('#linkPassText').focusout(this.onPasswordEntered);
+ var view = this;
+ this.$el.find('#linkPassText').keyup(function(event) {
+ if(event.keyCode == 13) {
+ view.onPasswordEntered();
+ }
+ });
+
+ var $emailField = this.$el.find('#email');
+ if (isLinkShare && $emailField.length !== 0) {
+ $emailField.autocomplete({
+ minLength: 1,
+ source: function (search, response) {
+ $.get(
+ OC.generateUrl('core/ajax/share.php'), {
+ fetch: 'getShareWithEmail',
+ search: search.term
+ }, function(result) {
+ if (result.status == 'success' && result.data.length > 0) {
+ response(result.data);
+ }
+ });
+ },
+ select: function( event, item ) {
+ $emailField.val(item.item.email);
+ return false;
+ }
+ })
+ .data("ui-autocomplete")._renderItem = function( ul, item ) {
+ return $('<li>')
+ .append('<a>' + escapeHTML(item.displayname) + "<br>" + escapeHTML(item.email) + '</a>' )
+ .appendTo( ul );
+ };
+ }
+
+ this.delegateEvents();
+
+ return this;
+ },
+
+ /**
+ * @returns {Function} from Handlebars
+ * @private
+ */
+ template: function () {
+ if (!this._template) {
+ this._template = Handlebars.compile(TEMPLATE);
+ }
+ return this._template;
+ }
+
+ });
+
+ OC.Share.ShareDialogLinkShareView = ShareDialogLinkShareView;
+
+})();
diff --git a/core/js/sharedialogresharerinfoview.js b/core/js/sharedialogresharerinfoview.js
new file mode 100644
index 00000000000..600e2ecbf56
--- /dev/null
+++ b/core/js/sharedialogresharerinfoview.js
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2015
+ *
+ * This file is licensed under the Affero General Public License version 3
+ * or later.
+ *
+ * See the COPYING-README file.
+ *
+ */
+
+(function() {
+ if (!OC.Share) {
+ OC.Share = {};
+ }
+
+ var TEMPLATE =
+ '<span class="reshare">' +
+ ' {{#if avatarEnabled}}' +
+ ' <div class="avatar" data-userName="{{reshareOwner}}"></div>' +
+ ' {{/if}}' +
+ ' {{sharedByText}}' +
+ '</span><br/>'
+ ;
+
+ /**
+ * @class OCA.Share.ShareDialogView
+ * @member {OC.Share.ShareItemModel} model
+ * @member {jQuery} $el
+ * @memberof OCA.Sharing
+ * @classdesc
+ *
+ * Represents the GUI of the share dialogue
+ *
+ */
+ var ShareDialogResharerInfoView = OC.Backbone.View.extend({
+ /** @type {string} **/
+ id: 'shareDialogResharerInfo',
+
+ /** @type {string} **/
+ tagName: 'div',
+
+ /** @type {string} **/
+ className: 'reshare',
+
+ /** @type {OC.Share.ShareConfigModel} **/
+ configModel: undefined,
+
+ /** @type {Function} **/
+ _template: undefined,
+
+ initialize: function(options) {
+ var view = this;
+
+ this.model.on('change:reshare', function() {
+ view.render();
+ });
+
+ if(!_.isUndefined(options.configModel)) {
+ this.configModel = options.configModel;
+ } else {
+ throw 'missing OC.Share.ShareConfigModel';
+ }
+ },
+
+ render: function() {
+ if (!this.model.hasReshare()
+ || this.model.getReshareOwner() === OC.currentUser)
+ {
+ this.$el.empty();
+ return this;
+ }
+
+ var reshareTemplate = this.template();
+ var ownerDisplayName = this.model.getReshareOwnerDisplayname();
+ var sharedByText = '';
+ if (this.model.getReshareType() === OC.Share.SHARE_TYPE_GROUP) {
+ sharedByText = t(
+ 'core',
+ 'Shared with you and the group {group} by {owner}',
+ {
+ group: this.model.getReshareWith(),
+ owner: ownerDisplayName
+ }
+ );
+ } else {
+ sharedByText = t(
+ 'core',
+ 'Shared with you by {owner}',
+ { owner: ownerDisplayName }
+ );
+ }
+
+ this.$el.html(reshareTemplate({
+ avatarEnabled: this.configModel.areAvatarsEnabled(),
+ reshareOwner: this.model.getReshareOwner(),
+ sharedByText: sharedByText
+ }));
+
+ if(this.configModel.areAvatarsEnabled()) {
+ this.$el.find('.avatar').each(function() {
+ var $this = $(this);
+ $this.avatar($this.data('username'), 32);
+ });
+ }
+
+ return this;
+ },
+
+ /**
+ * @returns {Function} from Handlebars
+ * @private
+ */
+ template: function () {
+ if (!this._template) {
+ this._template = Handlebars.compile(TEMPLATE);
+ }
+ return this._template;
+ }
+
+ });
+
+ OC.Share.ShareDialogResharerInfoView = ShareDialogResharerInfoView;
+
+})();
diff --git a/core/js/sharedialogshareelistview.js b/core/js/sharedialogshareelistview.js
new file mode 100644
index 00000000000..41be49dc241
--- /dev/null
+++ b/core/js/sharedialogshareelistview.js
@@ -0,0 +1,302 @@
+/*
+ * Copyright (c) 2015
+ *
+ * This file is licensed under the Affero General Public License version 3
+ * or later.
+ *
+ * See the COPYING-README file.
+ *
+ */
+
+(function() {
+ if (!OC.Share) {
+ OC.Share = {};
+ }
+
+ var TEMPLATE =
+ '<ul id="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}}">' +
+ ' <a href="#" class="unshare"><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>' +
+ ' {{/if}}' +
+ ' <span class="username">{{shareWithDisplayName}}</span>' +
+ ' {{#if mailPublicNotificationEnabled}} {{#unless isRemoteShare}}' +
+ ' <label><input type="checkbox" name="mailNotification" class="mailNotification" {{#if wasMailSent}}checked="checked"{{/if}} />{{notifyByMailLabel}}</label>' +
+ ' {{/unless}} {{/if}}' +
+ ' {{#if isResharingAllowed}} {{#if sharePermissionPossible}} {{#unless isRemoteShare}}' +
+ ' <label><input id="canShare-{{shareWith}}" type="checkbox" name="share" class="permissions" {{#if hasSharePermission}}checked="checked"{{/if}} data-permissions="{{sharePermission}}" />{{canShareLabel}}</label>' +
+ ' {{/unless}} {{/if}} {{/if}}' +
+ ' {{#if editPermissionPossible}}' +
+ ' <label><input id="canEdit-{{shareWith}}" type="checkbox" name="edit" class="permissions" {{#if hasEditPermission}}checked="checked"{{/if}} />{{canEditLabel}}</label>' +
+ ' {{/if}}' +
+ ' {{#unless isRemoteShare}}' +
+ ' <a href="#" class="showCruds"><img class="svg" alt="{{crudsLabel}}" src="{{triangleSImage}}"/></a>' +
+ ' <div class="cruds hidden">' +
+ ' {{#if createPermissionPossible}}' +
+ ' <label><input id="canCreate-{{shareWith}}" type="checkbox" name="create" class="permissions" {{#if hasCreatePermission}}checked="checked"{{/if}} data-permissions="{{createPermission}}"/>{{createPermissionLabel}}</label>' +
+ ' {{/if}}' +
+ ' {{#if updatePermissionPossible}}' +
+ ' <label><input id="canUpdate-{{shareWith}}" type="checkbox" name="update" class="permissions" {{#if hasUpdatePermission}}checked="checked"{{/if}} data-permissions="{{updatePermission}}"/>{{updatePermissionLabel}}</label>' +
+ ' {{/if}}' +
+ ' {{#if deletePermissionPossible}} {{#unless isRemoteShare}}' +
+ ' <label><input id="canDelete-{{shareWith}}" type="checkbox" name="delete" class="permissions" {{#if hasDeletePermission}}checked="checked"{{/if}} data-permissions="{{deletePermission}}"/>{{deletePermissionLabel}}</label>' +
+ ' {{/unless}} {{/if}}' +
+ ' </div>' +
+ ' {{/unless}}' +
+ ' </li>' +
+ ' {{/unless}}' +
+ '{{/each}}' +
+ '</ul>'
+ ;
+
+ /**
+ * @class OCA.Share.ShareDialogShareeListView
+ * @member {OC.Share.ShareItemModel} model
+ * @member {jQuery} $el
+ * @memberof OCA.Sharing
+ * @classdesc
+ *
+ * Represents the sharee list part in the GUI of the share dialogue
+ *
+ */
+ var ShareDialogShareeListView = OC.Backbone.View.extend({
+ /** @type {string} **/
+ id: 'shareDialogLinkShare',
+
+ /** @type {OC.Share.ShareConfigModel} **/
+ configModel: undefined,
+
+ /** @type {Function} **/
+ _template: undefined,
+
+ /** @type {boolean} **/
+ showLink: true,
+
+ /** @type {object} **/
+ _collections: {},
+
+ events: {
+ 'click .unshare': 'onUnshare',
+ 'click .permissions': 'onPermissionChange',
+ 'click .showCruds': 'onCrudsToggle',
+ 'click .mailNotification': 'onSendMailNotification'
+ },
+
+ initialize: function(options) {
+ if(!_.isUndefined(options.configModel)) {
+ this.configModel = options.configModel;
+ } else {
+ throw 'missing OC.Share.ShareConfigModel';
+ }
+
+ var view = this;
+ this.model.on('change:shares', function() {
+ view.render();
+ });
+ },
+
+ 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
+ * @returns {object}
+ */
+ getShareeObject: function(shareIndex) {
+ var shareWith = this.model.getShareWith(shareIndex);
+ var shareWithDisplayName = this.model.getShareWithDisplayName(shareIndex);
+ var shareType = this.model.getShareType(shareIndex);
+
+ var hasPermissionOverride = {};
+ if (shareType === OC.Share.SHARE_TYPE_GROUP) {
+ shareWithDisplayName = shareWithDisplayName + " (" + t('core', 'group') + ')';
+ } else if (shareType === OC.Share.SHARE_TYPE_REMOTE) {
+ shareWithDisplayName = shareWithDisplayName + " (" + t('core', 'remote') + ')';
+ hasPermissionOverride = {
+ createPermissionPossible: true,
+ updatePermissionPossible: true
+ };
+ }
+
+ return _.extend(hasPermissionOverride, {
+ hasSharePermission: this.model.hasSharePermission(shareIndex),
+ hasEditPermission: this.model.hasEditPermission(shareIndex),
+ hasCreatePermission: this.model.hasCreatePermission(shareIndex),
+ hasUpdatePermission: this.model.hasUpdatePermission(shareIndex),
+ hasDeletePermission: this.model.hasDeletePermission(shareIndex),
+ wasMailSent: this.model.notificationMailWasSent(shareIndex),
+ shareWith: shareWith,
+ shareWithDisplayName: shareWithDisplayName,
+ shareType: shareType,
+ modSeed: shareType !== OC.Share.SHARE_TYPE_USER,
+ isRemoteShare: shareType === OC.Share.SHARE_TYPE_REMOTE
+ });
+ },
+
+ getShareeList: function() {
+ var universal = {
+ avatarEnabled: this.configModel.areAvatarsEnabled(),
+ mailPublicNotificationEnabled: this.configModel.isMailPublicNotificationEnabled(),
+ notifyByMailLabel: t('core', 'notify by email'),
+ unshareLabel: t('core', 'Unshare'),
+ unshareImage: OC.imagePath('core', 'actions/delete'),
+ canShareLabel: t('core', 'can share'),
+ canEditLabel: t('core', 'can edit'),
+ createPermissionLabel: t('core', 'create'),
+ updatePermissionLabel: t('core', 'change'),
+ deletePermissionLabel: t('core', 'delete'),
+ crudsLabel: t('core', 'access control'),
+ triangleSImage: OC.imagePath('core', 'actions/triangle-s'),
+ isResharingAllowed: this.configModel.get('isResharingAllowed'),
+ sharePermissionPossible: this.model.sharePermissionPossible(),
+ editPermissionPossible: this.model.editPermissionPossible(),
+ createPermissionPossible: this.model.createPermissionPossible(),
+ updatePermissionPossible: this.model.updatePermissionPossible(),
+ deletePermissionPossible: this.model.deletePermissionPossible(),
+ sharePermission: OC.PERMISSION_SHARE,
+ createPermission: OC.PERMISSION_CREATE,
+ updatePermission: OC.PERMISSION_UPDATE,
+ deletePermission: OC.PERMISSION_DELETE
+ };
+
+ this._collections = {};
+
+ if(!this.model.hasUserShares()) {
+ return [];
+ }
+
+ 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)));
+ }
+ }
+ list = _.union(_.values(this._collections), list);
+
+ return list;
+ },
+
+ render: function() {
+ var shareeListTemplate = this.template();
+ this.$el.html(shareeListTemplate({
+ sharees: this.getShareeList()
+ }));
+
+ if(this.configModel.areAvatarsEnabled()) {
+ this.$el.find('.avatar').each(function() {
+ var $this = $(this);
+ if ($this.hasClass('imageplaceholderseed')) {
+ $this.css({width: 32, height: 32});
+ $this.imageplaceholder($this.data('seed'));
+ } else {
+ $this.avatar($this.data('username'), 32);
+ }
+ });
+ }
+
+ this.delegateEvents();
+
+ return this;
+ },
+
+ /**
+ * @returns {Function} from Handlebars
+ * @private
+ */
+ template: function () {
+ if (!this._template) {
+ this._template = Handlebars.compile(TEMPLATE);
+ }
+ return this._template;
+ },
+
+ onUnshare: function(event) {
+ var $element = $(event.target);
+
+ if($element.hasClass('icon-loading-small')) {
+ // in process
+ return;
+ }
+ $element.empty().addClass('icon-loading-small');
+
+ var $li = $element.closest('li');
+ var shareType = $li.data('share-type');
+ var shareWith = $li.attr('data-share-with');
+
+ this.model.removeShare(shareType, shareWith);
+
+ return false;
+ },
+
+ onPermissionChange: function(event) {
+ var $element = $(event.target);
+ var $li = $element.closest('li');
+ var shareType = $li.data('share-type');
+ var shareWith = $li.attr('data-share-with');
+
+ // adjust checkbox states
+ var $checkboxes = $('.permissions', $li).not('input[name="edit"]').not('input[name="share"]');
+ var checked;
+ if ($element.attr('name') === 'edit') {
+ checked = $element.is(':checked');
+ // Check/uncheck Create, Update, and Delete checkboxes if Edit is checked/unck
+ $($checkboxes).attr('checked', checked);
+ } else {
+ var numberChecked = $checkboxes.filter(':checked').length;
+ checked = numberChecked > 0;
+ $('input[name="edit"]', $li).attr('checked', checked);
+ }
+
+ var permissions = OC.PERMISSION_READ;
+ $('.permissions', $li).not('input[name="edit"]').filter(':checked').each(function(index, checkbox) {
+ permissions |= $(checkbox).data('permissions');
+ });
+
+ this.model.setPermissions(shareType, shareWith, permissions);
+ },
+
+ onCrudsToggle: function(event) {
+ var $target = $(event.target);
+ $target.closest('li').find('.cruds').toggleClass('hidden');
+ return false;
+ },
+
+ onSendMailNotification: function(event) {
+ var $target = $(event.target);
+ var $li = $(event.target).closest('li');
+ var shareType = $li.data('share-type');
+ var shareWith = $li.attr('data-share-with');
+
+ this.model.sendNotificationForShare(shareType, shareWith, $target.is(':checked'));
+ }
+ });
+
+ OC.Share.ShareDialogShareeListView = ShareDialogShareeListView;
+
+})();
diff --git a/core/js/sharedialogview.js b/core/js/sharedialogview.js
new file mode 100644
index 00000000000..2b61dab3ceb
--- /dev/null
+++ b/core/js/sharedialogview.js
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 2015
+ *
+ * This file is licensed under the Affero General Public License version 3
+ * or later.
+ *
+ * See the COPYING-README file.
+ *
+ */
+
+(function() {
+ if(!OC.Share) {
+ OC.Share = {};
+ }
+
+ var TEMPLATE_BASE =
+ '<div class="resharerInfoView"></div>' +
+ '{{#if isSharingAllowed}}' +
+ '<label for="shareWith" class="hidden-visually">{{shareLabel}}</label>' +
+ '<div class="oneline">' +
+ ' <input id="shareWith" type="text" placeholder="{{sharePlaceholder}}" />' +
+ ' <span class="shareWithLoading icon-loading-small hidden"></span>'+
+ '{{{remoteShareInfo}}}' +
+ '</div>' +
+ '{{/if}}' +
+ '<div class="shareeListView"></div>' +
+ '<div class="linkShareView"></div>' +
+ '<div class="expirationView"></div>'
+ ;
+
+ var TEMPLATE_REMOTE_SHARE_INFO =
+ '<a target="_blank" class="icon-info svg shareWithRemoteInfo hasTooltip" href="{{docLink}}" ' +
+ 'title="{{tooltip}}"></a>';
+
+ /**
+ * @class OCA.Share.ShareDialogView
+ * @member {OC.Share.ShareItemModel} model
+ * @member {jQuery} $el
+ * @memberof OCA.Sharing
+ * @classdesc
+ *
+ * Represents the GUI of the share dialogue
+ *
+ */
+ var ShareDialogView = OC.Backbone.View.extend({
+ /** @type {Object} **/
+ _templates: {},
+
+ /** @type {boolean} **/
+ _showLink: true,
+
+ /** @type {string} **/
+ tagName: 'div',
+
+ /** @type {OC.Share.ShareConfigModel} **/
+ configModel: undefined,
+
+ /** @type {object} **/
+ resharerInfoView: undefined,
+
+ /** @type {object} **/
+ linkShareView: undefined,
+
+ /** @type {object} **/
+ expirationView: undefined,
+
+ /** @type {object} **/
+ shareeListView: undefined,
+
+ initialize: function(options) {
+ var view = this;
+
+ this.model.on('fetchError', function() {
+ OC.Notification.showTemporary(t('core', 'Share details could not be loaded for this item.'));
+ });
+
+ if(!_.isUndefined(options.configModel)) {
+ this.configModel = options.configModel;
+ } else {
+ throw 'missing OC.Share.ShareConfigModel';
+ }
+
+ this.configModel.on('change:isRemoteShareAllowed', function() {
+ view.render();
+ });
+ this.model.on('change:permissions', function() {
+ view.render();
+ });
+
+ var subViewOptions = {
+ model: this.model,
+ configModel: this.configModel
+ };
+
+ var subViews = {
+ resharerInfoView: 'ShareDialogResharerInfoView',
+ linkShareView: 'ShareDialogLinkShareView',
+ expirationView: 'ShareDialogExpirationView',
+ shareeListView: 'ShareDialogShareeListView'
+ };
+
+ for(var name in subViews) {
+ var className = subViews[name];
+ this[name] = _.isUndefined(options[name])
+ ? new OC.Share[className](subViewOptions)
+ : options[name];
+ }
+
+ _.bindAll(this, 'autocompleteHandler', '_onSelectRecipient');
+ },
+
+ autocompleteHandler: function (search, response) {
+ var view = this;
+ var $loading = this.$el.find('.shareWithLoading');
+ $loading.removeClass('hidden');
+ $loading.addClass('inlineblock');
+ $.get(OC.filePath('core', 'ajax', 'share.php'), {
+ fetch: 'getShareWith',
+ search: search.term.trim(),
+ limit: 200,
+ itemShares: OC.Share.itemShares,
+ itemType: view.model.get('itemType')
+ }, function (result) {
+ $loading.addClass('hidden');
+ $loading.removeClass('inlineblock');
+ if (result.status == 'success' && result.data.length > 0) {
+ $("#shareWith").autocomplete("option", "autoFocus", true);
+ response(result.data);
+ } else {
+ response();
+ }
+ }).fail(function () {
+ $loading.addClass('hidden');
+ $loading.removeClass('inlineblock');
+ OC.Notification.show(t('core', 'An error occured. Please try again'));
+ window.setTimeout(OC.Notification.hide, 5000);
+ });
+ },
+
+ autocompleteRenderItem: function(ul, item) {
+ var insert = $("<a>");
+ var text = item.label;
+ if (item.value.shareType === OC.Share.SHARE_TYPE_GROUP) {
+ text = text + ' ('+t('core', 'group')+')';
+ } else if (item.value.shareType === OC.Share.SHARE_TYPE_REMOTE) {
+ text = text + ' ('+t('core', 'remote')+')';
+ }
+ insert.text(text);
+ if(item.value.shareType === OC.Share.SHARE_TYPE_GROUP) {
+ insert = insert.wrapInner('<strong></strong>');
+ }
+ return $("<li>")
+ .addClass((item.value.shareType === OC.Share.SHARE_TYPE_GROUP) ? 'group' : 'user')
+ .append(insert)
+ .appendTo(ul);
+ },
+
+ _onSelectRecipient: function(e, s) {
+ e.preventDefault();
+ $(e.target).val('');
+ this.model.addShare(s.item.value);
+ },
+
+ render: function() {
+ var baseTemplate = this._getTemplate('base', TEMPLATE_BASE);
+
+ this.$el.html(baseTemplate({
+ shareLabel: t('core', 'Share'),
+ sharePlaceholder: this._renderSharePlaceholderPart(),
+ remoteShareInfo: this._renderRemoteShareInfoPart(),
+ isSharingAllowed: this.model.sharePermissionPossible()
+ }));
+
+ var $shareField = this.$el.find('#shareWith');
+ if ($shareField.length) {
+ $shareField.autocomplete({
+ minLength: 2,
+ delay: 750,
+ source: this.autocompleteHandler,
+ select: this._onSelectRecipient
+ }).data('ui-autocomplete')._renderItem = this.autocompleteRenderItem;
+ }
+
+ this.resharerInfoView.$el = this.$el.find('.resharerInfoView');
+ this.resharerInfoView.render();
+
+ this.linkShareView.$el = this.$el.find('.linkShareView');
+ this.linkShareView.render();
+
+ this.expirationView.$el = this.$el.find('.expirationView');
+ this.expirationView.render();
+
+ this.shareeListView.$el = this.$el.find('.shareeListView');
+ this.shareeListView.render();
+
+ this.$el.find('.hasTooltip').tooltip();
+
+ return this;
+ },
+
+ /**
+ * sets whether share by link should be displayed or not. Default is
+ * true.
+ *
+ * @param {bool} showLink
+ */
+ setShowLink: function(showLink) {
+ this._showLink = (typeof showLink === 'boolean') ? showLink : true;
+ this.linkShareView.showLink = this._showLink;
+ },
+
+ _renderRemoteShareInfoPart: function() {
+ var remoteShareInfo = '';
+ if(this.configModel.get('isRemoteShareAllowed')) {
+ var infoTemplate = this._getRemoteShareInfoTemplate();
+ remoteShareInfo = infoTemplate({
+ docLink: this.configModel.getFederatedShareDocLink(),
+ tooltip: t('core', 'Share with people on other ownClouds using the syntax username@example.com/owncloud')
+ });
+ }
+
+ return remoteShareInfo;
+ },
+
+ _renderSharePlaceholderPart: function () {
+ var sharePlaceholder = t('core', 'Share with users or groups …');
+ if (this.configModel.get('isRemoteShareAllowed')) {
+ sharePlaceholder = t('core', 'Share with users, groups or remote users …');
+ }
+ return sharePlaceholder;
+ },
+
+ /**
+ *
+ * @param {string} key - an identifier for the template
+ * @param {string} template - the HTML to be compiled by Handlebars
+ * @returns {Function} from Handlebars
+ * @private
+ */
+ _getTemplate: function (key, template) {
+ if (!this._templates[key]) {
+ this._templates[key] = Handlebars.compile(template);
+ }
+ return this._templates[key];
+ },
+
+ /**
+ * returns the info template for remote sharing
+ *
+ * @returns {Function}
+ * @private
+ */
+ _getRemoteShareInfoTemplate: function() {
+ return this._getTemplate('remoteShareInfo', TEMPLATE_REMOTE_SHARE_INFO);
+ }
+ });
+
+ OC.Share.ShareDialogView = ShareDialogView;
+
+})();
diff --git a/core/js/shareitemmodel.js b/core/js/shareitemmodel.js
new file mode 100644
index 00000000000..ff0d3a6d800
--- /dev/null
+++ b/core/js/shareitemmodel.js
@@ -0,0 +1,750 @@
+/*
+ * Copyright (c) 2015
+ *
+ * This file is licensed under the Affero General Public License version 3
+ * or later.
+ *
+ * See the COPYING-README file.
+ *
+ */
+
+(function() {
+ if(!OC.Share) {
+ OC.Share = {};
+ OC.Share.Types = {};
+ }
+
+ /**
+ * @typedef {object} OC.Share.Types.LinkShareInfo
+ * @property {bool} isLinkShare
+ * @property {string} token
+ * @property {string|null} password
+ * @property {string} link
+ * @property {number} permissions
+ * @property {Date} expiration
+ * @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
+ * @property {number} share_type
+ * @property {string} share_with
+ * @property {string} displayname_owner
+ * @property {number} permissions
+ */
+
+ /**
+ * @typedef {object} OC.Share.Types.ShareInfo
+ * @property {number} share_type
+ * @property {number} permissions
+ * @property {number} file_source optional
+ * @property {number} item_source
+ * @property {string} token
+ * @property {string} share_with
+ * @property {string} share_with_displayname
+ * @property {string} share_mail_send
+ * @property {OC.Share.Types.Collection|undefined} collection
+ * @property {Date} expiration optional?
+ * @property {number} stime optional?
+ */
+
+ /**
+ * @typedef {object} OC.Share.Types.ShareItemInfo
+ * @property {OC.Share.Types.Reshare} reshare
+ * @property {OC.Share.Types.ShareInfo[]} shares
+ * @property {OC.Share.Types.LinkShareInfo|undefined} linkShare
+ */
+
+ /**
+ * @class OCA.Share.ShareItemModel
+ * @classdesc
+ *
+ * Represents the GUI of the share dialogue
+ *
+ * // FIXME: use OC Share API once #17143 is done
+ *
+ * // TODO: this really should be a collection of share item models instead,
+ * where the link share is one of them
+ */
+ var ShareItemModel = OC.Backbone.Model.extend({
+ initialize: function(attributes, options) {
+ if(!_.isUndefined(options.configModel)) {
+ this.configModel = options.configModel;
+ }
+ if(!_.isUndefined(options.fileInfoModel)) {
+ /** @type {OC.Files.FileInfo} **/
+ this.fileInfoModel = options.fileInfoModel;
+ }
+
+ _.bindAll(this, 'addShare');
+ },
+
+ defaults: {
+ allowPublicUploadStatus: false,
+ permissions: 0,
+ linkShare: {}
+ },
+
+ /**
+ * Saves the current link share information.
+ *
+ * This will trigger an ajax call and refetch the model afterwards.
+ *
+ * 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: '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;
+ }
+ }
+ });
+
+ OC.Share.share(
+ itemType,
+ itemSource,
+ OC.Share.SHARE_TYPE_LINK,
+ attributes.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;
+ }
+
+ if (options && _.isFunction(options.error)) {
+ options.error(model, msg);
+ } else {
+ OC.dialogs.alert(msg, t('core', 'Error while sharing'));
+ }
+ }
+ );
+ },
+
+ removeLinkShare: function() {
+ this.removeShare(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;
+ }
+
+ this.get('linkShare').permissions = permissions;
+ },
+
+ /**
+ * 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;
+ },
+
+ addShare: function(attributes, options) {
+ var shareType = attributes.shareType;
+ var shareWith = attributes.shareWith;
+ var fileName = this.fileInfoModel.get('name');
+ options = options || {};
+
+ // Default permissions are Edit (CRUD) and Share
+ // Check if these permissions are possible
+ var permissions = OC.PERMISSION_READ;
+ if (shareType === OC.Share.SHARE_TYPE_REMOTE) {
+ permissions = OC.PERMISSION_CREATE | OC.PERMISSION_UPDATE | OC.PERMISSION_READ;
+ } else {
+ if (this.updatePermissionPossible()) {
+ permissions = permissions | OC.PERMISSION_UPDATE;
+ }
+ if (this.createPermissionPossible()) {
+ permissions = permissions | OC.PERMISSION_CREATE;
+ }
+ if (this.deletePermissionPossible()) {
+ permissions = permissions | OC.PERMISSION_DELETE;
+ }
+ if (this.configModel.get('isResharingAllowed') && (this.sharePermissionPossible())) {
+ permissions = permissions | OC.PERMISSION_SHARE;
+ }
+ }
+
+ 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();
+ });
+ },
+
+ setPermissions: function(shareType, shareWith, permissions) {
+ var itemType = this.get('itemType');
+ var itemSource = this.get('itemSource');
+
+ // TODO: in the future, only set the permissions on the model but don't save directly
+ OC.Share.setPermissions(itemType, itemSource, shareType, shareWith, permissions);
+ },
+
+ removeShare: function(shareType, shareWith) {
+ var model = this;
+ var itemType = this.get('itemType');
+ var itemSource = this.get('itemSource');
+
+ OC.Share.unshare(itemType, itemSource, shareType, shareWith, function() {
+ model.fetch();
+ });
+ },
+
+ /**
+ * @returns {boolean}
+ */
+ isPublicUploadAllowed: function() {
+ return this.get('allowPublicUploadStatus');
+ },
+
+ /**
+ * @returns {boolean}
+ */
+ isFolder: function() {
+ return this.get('itemType') === 'folder';
+ },
+
+ /**
+ * @returns {boolean}
+ */
+ isFile: function() {
+ return this.get('itemType') === 'file';
+ },
+
+ /**
+ * whether this item has reshare information
+ * @returns {boolean}
+ */
+ hasReshare: function() {
+ var reshare = this.get('reshare');
+ return _.isObject(reshare) && !_.isUndefined(reshare.uid_owner);
+ },
+
+ /**
+ * whether this item has user share information
+ * @returns {boolean}
+ */
+ hasUserShares: function() {
+ var shares = this.get('shares');
+ return _.isArray(shares) && shares.length > 0;
+ },
+
+ /**
+ * Returns whether this item has a link share
+ *
+ * @return {bool} true if a link share exists, false otherwise
+ */
+ hasLinkShare: function() {
+ var linkShare = this.get('linkShare');
+ if (linkShare && linkShare.isLinkShare) {
+ return true;
+ }
+ 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}
+ */
+ getReshareOwner: function() {
+ return this.get('reshare').uid_owner;
+ },
+
+ /**
+ * @returns {string}
+ */
+ getReshareOwnerDisplayname: function() {
+ return this.get('reshare').displayname_owner;
+ },
+
+ /**
+ * @returns {string}
+ */
+ getReshareWith: function() {
+ return this.get('reshare').share_with;
+ },
+
+ /**
+ * @returns {number}
+ */
+ getReshareType: function() {
+ return this.get('reshare').share_type;
+ },
+
+ /**
+ * @param shareIndex
+ * @returns {string}
+ */
+ getShareWith: function(shareIndex) {
+ /** @type OC.Share.Types.ShareInfo **/
+ var share = this.get('shares')[shareIndex];
+ if(!_.isObject(share)) {
+ throw "Unknown Share";
+ }
+ return share.share_with;
+ },
+
+ /**
+ * @param shareIndex
+ * @returns {string}
+ */
+ getShareWithDisplayName: function(shareIndex) {
+ /** @type OC.Share.Types.ShareInfo **/
+ var share = this.get('shares')[shareIndex];
+ if(!_.isObject(share)) {
+ throw "Unknown Share";
+ }
+ return share.share_with_displayname;
+ },
+
+ getShareType: function(shareIndex) {
+ /** @type OC.Share.Types.ShareInfo **/
+ var share = this.get('shares')[shareIndex];
+ if(!_.isObject(share)) {
+ throw "Unknown Share";
+ }
+ return share.share_type;
+ },
+
+ /**
+ * whether a share from shares has the requested permission
+ *
+ * @param {number} shareIndex
+ * @param {number} permission
+ * @returns {boolean}
+ * @private
+ */
+ _shareHasPermission: function(shareIndex, permission) {
+ /** @type OC.Share.Types.ShareInfo **/
+ var share = this.get('shares')[shareIndex];
+ if(!_.isObject(share)) {
+ throw "Unknown Share";
+ }
+ if( share.share_type === OC.Share.SHARE_TYPE_REMOTE
+ && ( permission === OC.PERMISSION_SHARE
+ || permission === OC.PERMISSION_DELETE))
+ {
+ return false;
+ }
+ return (share.permissions & permission) === permission;
+ },
+
+ notificationMailWasSent: function(shareIndex) {
+ /** @type OC.Share.Types.ShareInfo **/
+ var share = this.get('shares')[shareIndex];
+ if(!_.isObject(share)) {
+ throw "Unknown Share";
+ }
+ return share.share_mail_send === '1';
+ },
+
+ /**
+ * Sends an email notification for the given share
+ *
+ * @param {int} shareType share type
+ * @param {string} shareWith recipient
+ * @param {bool} state whether to set the notification flag or remove it
+ */
+ sendNotificationForShare: function(shareType, shareWith, state) {
+ var itemType = this.get('itemType');
+ var itemSource = this.get('itemSource');
+
+ return $.post(
+ OC.generateUrl('core/ajax/share.php'),
+ {
+ action: state ? 'informRecipients' : 'informRecipientsDisabled',
+ recipient: shareWith,
+ shareType: shareType,
+ itemSource: itemSource,
+ itemType: itemType
+ },
+ function(result) {
+ if (result.status !== 'success') {
+ // FIXME: a model should not show dialogs
+ OC.dialogs.alert(t('core', result.data.message), t('core', 'Warning'));
+ }
+ }
+ );
+ },
+
+ /**
+ * Send the link share information by email
+ *
+ * @param {string} recipientEmail recipient email address
+ */
+ sendEmailPrivateLink: function(recipientEmail) {
+ var itemType = this.get('itemType');
+ var itemSource = this.get('itemSource');
+ var linkShare = this.get('linkShare');
+
+ return $.post(
+ OC.generateUrl('core/ajax/share.php'), {
+ action: 'email',
+ toaddress: recipientEmail,
+ link: linkShare.link,
+ itemType: itemType,
+ itemSource: itemSource,
+ file: this.fileInfoModel.get('name'),
+ expiration: linkShare.expiration || ''
+ },
+ function(result) {
+ if (!result || result.status !== 'success') {
+ // FIXME: a model should not show dialogs
+ OC.dialogs.alert(result.data.message, t('core', 'Error while sending notification'));
+ }
+ });
+ },
+
+ /**
+ * @returns {boolean}
+ */
+ sharePermissionPossible: function() {
+ return (this.get('permissions') & OC.PERMISSION_SHARE) === OC.PERMISSION_SHARE;
+ },
+
+ /**
+ * @param {number} shareIndex
+ * @returns {boolean}
+ */
+ hasSharePermission: function(shareIndex) {
+ return this._shareHasPermission(shareIndex, OC.PERMISSION_SHARE);
+ },
+
+ /**
+ * @returns {boolean}
+ */
+ createPermissionPossible: function() {
+ return (this.get('permissions') & OC.PERMISSION_CREATE) === OC.PERMISSION_CREATE;
+ },
+
+ /**
+ * @param {number} shareIndex
+ * @returns {boolean}
+ */
+ hasCreatePermission: function(shareIndex) {
+ return this._shareHasPermission(shareIndex, OC.PERMISSION_CREATE);
+ },
+
+ /**
+ * @returns {boolean}
+ */
+ updatePermissionPossible: function() {
+ return (this.get('permissions') & OC.PERMISSION_UPDATE) === OC.PERMISSION_UPDATE;
+ },
+
+ /**
+ * @param {number} shareIndex
+ * @returns {boolean}
+ */
+ hasUpdatePermission: function(shareIndex) {
+ return this._shareHasPermission(shareIndex, OC.PERMISSION_UPDATE);
+ },
+
+ /**
+ * @returns {boolean}
+ */
+ deletePermissionPossible: function() {
+ return (this.get('permissions') & OC.PERMISSION_DELETE) === OC.PERMISSION_DELETE;
+ },
+
+ /**
+ * @param {number} shareIndex
+ * @returns {boolean}
+ */
+ hasDeletePermission: function(shareIndex) {
+ return this._shareHasPermission(shareIndex, OC.PERMISSION_DELETE);
+ },
+
+ /**
+ * @returns {boolean}
+ */
+ editPermissionPossible: function() {
+ return this.createPermissionPossible()
+ || this.updatePermissionPossible()
+ || this.deletePermissionPossible();
+ },
+
+ /**
+ * @returns {boolean}
+ */
+ hasEditPermission: function(shareIndex) {
+ return this.hasCreatePermission(shareIndex)
+ || this.hasUpdatePermission(shareIndex)
+ || this.hasDeletePermission(shareIndex);
+ },
+
+ fetch: function() {
+ var model = this;
+ OC.Share.loadItem(this.get('itemType'), this.get('itemSource'), function(data) {
+ model.set(model.parse(data));
+ });
+ },
+
+ /**
+ * Updates OC.Share.itemShares and OC.Share.statuses.
+ *
+ * This is required in case the user navigates away and comes back,
+ * the share statuses from the old arrays are still used to fill in the icons
+ * in the file list.
+ */
+ _legacyFillCurrentShares: function(shares) {
+ var fileId = this.fileInfoModel.get('id');
+ if (!shares || !shares.length) {
+ delete OC.Share.statuses[fileId];
+ return;
+ }
+
+ var currentShareStatus = OC.Share.statuses[fileId];
+ if (!currentShareStatus) {
+ currentShareStatus = {link: false};
+ OC.Share.statuses[fileId] = currentShareStatus;
+ }
+ currentShareStatus.link = false;
+
+ OC.Share.currentShares = {};
+ OC.Share.itemShares = [];
+ _.each(shares,
+ /**
+ * @param {OC.Share.Types.ShareInfo} share
+ */
+ function(share) {
+ if (share.share_type === OC.Share.SHARE_TYPE_LINK) {
+ OC.Share.itemShares[share.share_type] = true;
+ currentShareStatus.link = true;
+ } else {
+ if (!OC.Share.itemShares[share.share_type]) {
+ OC.Share.itemShares[share.share_type] = [];
+ }
+ OC.Share.itemShares[share.share_type].push(share.share_with);
+ }
+ }
+ );
+ },
+
+ parse: function(data) {
+ if(data === false) {
+ console.warn('no data was returned');
+ trigger('fetchError');
+ return {};
+ }
+
+ var permissions = this.get('possiblePermissions');
+ if(!_.isUndefined(data.reshare) && !_.isUndefined(data.reshare.permissions) && data.reshare.uid_owner !== OC.currentUser) {
+ permissions = permissions & data.reshare.permissions;
+ }
+
+ var allowPublicUploadStatus = false;
+ if(!_.isUndefined(data.shares)) {
+ $.each(data.shares, function (key, value) {
+ if (value.share_type === OC.Share.SHARE_TYPE_LINK) {
+ allowPublicUploadStatus = (value.permissions & OC.PERMISSION_CREATE) ? true : false;
+ return true;
+ }
+ });
+ }
+
+ /** @type {OC.Share.Types.ShareInfo[]} **/
+ var shares = _.toArray(data.shares);
+ this._legacyFillCurrentShares(shares);
+
+ var linkShare = { isLinkShare: false };
+ // filter out the share by link
+ shares = _.reject(shares,
+ /**
+ * @param {OC.Share.Types.ShareInfo} share
+ */
+ function(share) {
+ var isShareLink =
+ share.share_type === OC.Share.SHARE_TYPE_LINK
+ && ( share.file_source === this.get('itemSource')
+ || share.item_source === this.get('itemSource'));
+
+ if (isShareLink) {
+ var link = window.location.protocol + '//' + window.location.host;
+ if (!share.token) {
+ // pre-token link
+ var fullPath = this.fileInfoModel.get('path') + '/' +
+ this.fileInfoModel.get('name');
+ var location = '/' + OC.currentUser + '/files' + fullPath;
+ var type = this.fileInfoModel.isDirectory() ? 'folder' : 'file';
+ link += OC.linkTo('', 'public.php') + '?service=files&' +
+ type + '=' + encodeURIComponent(location);
+ } else {
+ link += OC.generateUrl('/s/') + share.token;
+ }
+ linkShare = {
+ isLinkShare: true,
+ token: share.token,
+ password: share.share_with,
+ link: link,
+ permissions: share.permissions,
+ // currently expiration is only effective for link shares.
+ expiration: share.expiration,
+ stime: share.stime
+ };
+
+ return share;
+ }
+ },
+ this
+ );
+
+ return {
+ reshare: data.reshare,
+ shares: shares,
+ linkShare: linkShare,
+ permissions: permissions,
+ allowPublicUploadStatus: allowPublicUploadStatus
+ };
+ },
+
+ /**
+ * Parses a string to an valid integer (unix timestamp)
+ * @param time
+ * @returns {*}
+ * @internal Only used to work around a bug in the backend
+ */
+ _parseTime: function(time) {
+ if (_.isString(time)) {
+ // skip empty strings and hex values
+ if (time === '' || (time.length > 1 && time[0] === '0' && time[1] === 'x')) {
+ return null;
+ }
+ time = parseInt(time, 10);
+ if(isNaN(time)) {
+ time = null;
+ }
+ }
+ return time;
+ }
+ });
+
+ OC.Share.ShareItemModel = ShareItemModel;
+})();
diff --git a/core/js/tests/specs/shareSpec.js b/core/js/tests/specs/shareSpec.js
index 5a59a117d77..9e80f4fe19d 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 00000000000..de6f9944094
--- /dev/null
+++ b/core/js/tests/specs/sharedialogviewSpec.js
@@ -0,0 +1,582 @@
+/**
+* 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({
+ enforcePasswordForPublicLink: false,
+ isResharingAllowed: true,
+ enforcePasswordForPublicLink: false,
+ isDefaultExpireDateEnabled: false,
+ isDefaultExpireDateEnforced: false,
+ defaultExpireDate: 7
+ });
+ 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() {
+ // 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');
+
+ 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('**********');
+ });
+ 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');
+
+ 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('**********');
+ });
+ 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('shows populated link share when a link share exists', function() {
+ // this is how the OC.Share class does it...
+ var link = parent.location.protocol + '//' + location.host +
+ OC.generateUrl('/s/') + 'tehtoken';
+ shareModel.set('linkShare', {
+ isLinkShare: true,
+ token: 'tehtoken',
+ link: link,
+ expiration: '',
+ permissions: OC.PERMISSION_READ,
+ stime: 1403884258,
+ });
+
+ dialog.render();
+
+ expect(dialog.$el.find('#linkCheckbox').prop('checked')).toEqual(true);
+ 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);
+
+ configModel.set({
+ enforcePasswordForPublicLink: false,
+ isDefaultExpireDateEnabled: false,
+ isDefaultExpireDateEnforced: false,
+ defaultExpireDate: 7
+ });
+
+ 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() {
+ configModel.set('isDefaultExpireDateEnabled', true);
+ dialog.render();
+ dialog.$el.find('[name=linkCheckbox]').click();
+ // here fetch would be called and the server returns the expiration date
+ shareModel.get('linkShare').expiration = '2014-1-27 00:00:00';
+ dialog.render();
+
+ // 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() {
+ configModel.set({
+ isDefaultExpireDateEnabled: true,
+ isDefaultExpireDateEnforced: true
+ });
+ dialog.render();
+ dialog.$el.find('[name=linkCheckbox]').click();
+ // here fetch would be called and the server returns the expiration date
+ shareModel.get('linkShare').expiration = '2014-1-27 00:00:00';
+ dialog.render();
+
+ 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() {
+ configModel.set({
+ enforcePasswordForPublicLink: true,
+ isDefaultExpireDateEnabled: true,
+ isDefaultExpireDateEnforced: true
+ });
+ dialog.render();
+ dialog.$el.find('[name=linkCheckbox]').click();
+ // here fetch would be called and the server returns the expiration date
+ shareModel.get('linkShare').expiration = '2014-1-27 00:00:00';
+ dialog.render();
+
+ //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(dialog.$('#emailPrivateLink').length).toEqual(1);
+ });
+ it('not renders email form when sending emails is disabled', function() {
+ $('input[name=mailPublicNotificationEnabled]').val('no');
+ dialog.render();
+ expect(dialog.$('#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() {
+ configModel.set({
+ isDefaultExpireDateEnabled: true,
+ isDefaultExpireDateEnforced: 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
+ var shareItem = shareModel.get('linkShare');
+ 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;
+ configModel.set({
+ isDefaultExpireDateEnabled: true,
+ isDefaultExpireDateEnforced: 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() {
+ shareModel.set({
+ 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;
+ avatarStub.reset();
+ dialog.render();
+ });
+
+ afterEach(function() {
+ oc_config.enable_avatars = false;
+ });
+
+ it('test correct function calls', function() {
+ expect(avatarStub.calledTwice).toEqual(true);
+ expect(placeholderStub.calledTwice).toEqual(true);
+ expect(dialog.$('#shareWithList').children().length).toEqual(3);
+ expect(dialog.$('.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() {
+ dialog.render();
+ });
+
+ it('no avatar classes', function() {
+ expect($('.avatar').length).toEqual(0);
+ expect(avatarStub.callCount).toEqual(0);
+ expect(placeholderStub.callCount).toEqual(0);
+ });
+ });
+ });
+ 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('#shareWith').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
new file mode 100644
index 00000000000..c1d820052e2
--- /dev/null
+++ b/core/js/tests/specs/shareitemmodelSpec.js
@@ -0,0 +1,283 @@
+/**
+* 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;
+ var oldCurrentUser;
+
+ beforeEach(function() {
+ oldCurrentUser = OC.currentUser;
+
+ loadItemStub = sinon.stub(OC.Share, 'loadItem');
+
+ 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();
+ OC.currentUser = oldCurrentUser;
+ });
+
+ 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.yields({
+ /* 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[0].id).toEqual(100);
+ expect(shares[0].permissions).toEqual(31);
+ expect(shares[0].share_type).toEqual(OC.Share.SHARE_TYPE_USER);
+ expect(shares[0].share_with).toEqual('user1');
+ expect(shares[0].share_with_displayname).toEqual('User One');
+
+ 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.yields({
+ 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');
+ // remaining share appears in this list
+ expect(shares.length).toEqual(1);
+
+ var linkShare = model.get('linkShare');
+ 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: [{
+ 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');
+ // the parent share remains in the list
+ expect(shares.length).toEqual(1);
+
+ var linkShare = model.get('linkShare');
+ expect(linkShare.isLinkShare).toEqual(true);
+ expect(linkShare.token).toEqual('tehtoken');
+
+ // 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: []
+ });
+ 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: []
+ });
+
+ model.set('possiblePermissions', OC.PERMISSION_READ);
+ model.fetch();
+
+ // no resharing allowed
+ expect(model.get('permissions')).toEqual(OC.PERMISSION_READ);
+ });
+ it('allows owner to share their own share when they are also the recipient', function() {
+ OC.currentUser = 'user1';
+ loadItemStub.yields({
+ reshare: {
+ permissions: OC.PERMISSION_READ,
+ uid_owner: 'user1'
+ },
+ shares: []
+ });
+
+ model.fetch();
+
+ // sharing still allowed
+ expect(model.get('permissions') & OC.PERMISSION_SHARE).toEqual(OC.PERMISSION_SHARE);
+ });
+ });
+
+ 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.ShareItemModel.prototype._parseTime(value[0])).toEqual(value[1]);
+ });
+
+ });
+ });
+});
+
diff --git a/lib/private/share/share.php b/lib/private/share/share.php
index 6ad36d60fe8..802b146cfb6 100644
--- a/lib/private/share/share.php
+++ b/lib/private/share/share.php
@@ -83,6 +83,13 @@ class Share extends Constants {
'supportedFileExtensions' => $supportedFileExtensions
);
if(count(self::$backendTypes) === 1) {
+ \OC_Util::addScript('core', 'shareconfigmodel');
+ \OC_Util::addScript('core', 'shareitemmodel');
+ \OC_Util::addScript('core', 'sharedialogresharerinfoview');
+ \OC_Util::addScript('core', 'sharedialoglinkshareview');
+ \OC_Util::addScript('core', 'sharedialogexpirationview');
+ \OC_Util::addScript('core', 'sharedialogshareelistview');
+ \OC_Util::addScript('core', 'sharedialogview');
\OC_Util::addScript('core', 'share');
\OC_Util::addStyle('core', 'share');
}
diff --git a/tests/karma.config.js b/tests/karma.config.js
index d3280b2939a..64a94ef230b 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']
},