Share dialog files sidebartags/v8.2beta1
@@ -367,6 +367,21 @@ | |||
return model; | |||
}, | |||
/** | |||
* 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 | |||
* |
@@ -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( |
@@ -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; | |||
} |
@@ -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; | |||
}, | |||
/** |
@@ -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? | |||
} | |||
} |
@@ -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'); | |||
}); |
@@ -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 { |
@@ -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", |
@@ -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')); | |||
} | |||
}); | |||
}); | |||
}); |
@@ -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; | |||
})(); |
@@ -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; | |||
})(); |
@@ -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; | |||
})(); |
@@ -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; | |||
})(); |
@@ -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; | |||
})(); |
@@ -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; | |||
})(); |
@@ -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; | |||
})(); |
@@ -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); | |||
}); | |||
}); | |||
}); | |||
}); | |||
@@ -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]); | |||
}); | |||
}); | |||
}); | |||
}); | |||
@@ -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'); | |||
} |
@@ -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'] | |||
}, |