Browse Source
Merge pull request #18185 from owncloud/share-dialog-files-sidebar
Merge pull request #18185 from owncloud/share-dialog-files-sidebar
Share dialog files sidebarremotes/origin/db-empty-migrate
21 changed files with 3191 additions and 2150 deletions
-
15apps/files/js/filelist.js
-
15apps/files_sharing/appinfo/app.php
-
72apps/files_sharing/css/sharetabview.css
-
96apps/files_sharing/js/share.js
-
52apps/files_sharing/js/sharetabview.js
-
125apps/files_sharing/tests/js/shareSpec.js
-
38core/css/share.css
-
7core/js/core.json
-
934core/js/share.js
-
83core/js/shareconfigmodel.js
-
195core/js/sharedialogexpirationview.js
-
298core/js/sharedialoglinkshareview.js
-
124core/js/sharedialogresharerinfoview.js
-
302core/js/sharedialogshareelistview.js
-
260core/js/sharedialogview.js
-
750core/js/shareitemmodel.js
-
1100core/js/tests/specs/shareSpec.js
-
582core/js/tests/specs/sharedialogviewSpec.js
-
283core/js/tests/specs/shareitemmodelSpec.js
-
7lib/private/share/share.php
-
3tests/karma.config.js
@ -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; |
|||
} |
@ -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; |
|||
})(); |
1100
core/js/tests/specs/shareSpec.js
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -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]); |
|||
}); |
|||
|
|||
}); |
|||
}); |
|||
}); |
|||
|
Write
Preview
Loading…
Cancel
Save
Reference in new issue