From edd163a6113664921a2fda730037dcf68bfe08ae Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Fri, 31 Jul 2015 00:07:41 +0200 Subject: [PATCH 01/59] refactor share dialog for multi-purpose use (dropdown, sidebar) and better maintainability --- apps/files_sharing/appinfo/app.php | 3 +- apps/files_sharing/css/sharetabview.css | 69 +++++++ core/js/share.js | 25 ++- core/js/sharedialogview.js | 253 ++++++++++++++++++++++++ core/js/shareitemmodel.js | 113 +++++++++++ lib/private/share/share.php | 2 + 6 files changed, 462 insertions(+), 3 deletions(-) create mode 100644 core/js/sharedialogview.js create mode 100644 core/js/shareitemmodel.js diff --git a/apps/files_sharing/appinfo/app.php b/apps/files_sharing/appinfo/app.php index 20f1b046d35b..3049dc6a2637 100644 --- a/apps/files_sharing/appinfo/app.php +++ b/apps/files_sharing/appinfo/app.php @@ -55,8 +55,9 @@ \OCP\Share::registerBackend('folder', 'OC_Share_Backend_Folder', 'file'); \OCP\Util::addScript('files_sharing', 'share'); +\OCP\Util::addScript('files_sharing', 'sharetabview'); \OCP\Util::addScript('files_sharing', 'external'); -\OCP\Util::addStyle('files_sharing', 'sharetabview'); +// \OCP\Util::addStyle('files_sharing', 'sharetabview'); \OC::$server->getActivityManager()->registerExtension(function() { return new \OCA\Files_Sharing\Activity( diff --git a/apps/files_sharing/css/sharetabview.css b/apps/files_sharing/css/sharetabview.css index 42c9bee71731..8b9af852531c 100644 --- a/apps/files_sharing/css/sharetabview.css +++ b/apps/files_sharing/css/sharetabview.css @@ -1,3 +1,72 @@ .app-files .shareTabView { min-height: 100px; } + +.shareTabView .oneline { white-space: nowrap; } + +.shareTabView .shareWithLoading { + display: inline-block !important; + 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; +} diff --git a/core/js/share.js b/core/js/share.js index cd4a614e9d15..5d3253e6d5cf 100644 --- a/core/js/share.js +++ b/core/js/share.js @@ -3,7 +3,7 @@ /** * @namespace */ -OC.Share={ +OC.Share = _.extend(OC.Share, { SHARE_TYPE_USER:0, SHARE_TYPE_GROUP:1, SHARE_TYPE_LINK:3, @@ -289,6 +289,12 @@ OC.Share={ } img.attr('src', image); }, + /** + * + * @param itemType + * @param itemSource + * @returns {OC.Share.Types.ShareInfo} + */ loadItem:function(itemType, itemSource) { var data = ''; var checkReshare = true; @@ -371,6 +377,21 @@ OC.Share={ }); }, showDropDown:function(itemType, itemSource, appendTo, link, possiblePermissions, filename) { + var itemModel = new OC.Share.ShareItemModel(itemType, itemSource); + var dialogView = new OC.Share.ShareDialogView('dropdown'); + dialogView.setContainerClasses('drop shareDropDown'); + dialogView.setShowLink(link); + dialogView.setPossiblePermissions(possiblePermissions); + dialogView.setItemModel(itemModel); + var $dialog = dialogView.render(); + $dialog.appendTo(appendTo); + $dialog.attr('data-item-source-name', filename); + $dialog.slideDown(OC.menuSpeed, function() { + OC.Share.droppedDown = true; + }); + return; + + var data = OC.Share.loadItem(itemType, itemSource); var dropDownEl; var html = '' + + ' {{#if publicUpload}}' + + ' ' + + ' {{/if}}' + + ' {{#if mailPublicNotificationEnabled}}' + + ' ' + + ' {{/if}}' + + '' ; + var TEMPLATE_NO_SHARING = + '' + ; + + var TEMPLATE_EXPIRATION = + '
' + + ' ' + + ' ' + + ' ' + + ' ' + + ' {{defaultExpireMessage}}' + + '
' + ; + /** * @class OCA.Share.ShareDialogView * @member {OC.Share.ShareItemModel} model @@ -73,7 +107,10 @@ /** @type {string} **/ tagName: 'div', - initialize: function() { + /** @type {OC.Share.ShareConfigModel} **/ + configModel: undefined, + + initialize: function(options) { var view = this; this.model.on('change', function() { view.render(); @@ -82,6 +119,12 @@ 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 { + console.warn('missing OC.Share.ShareConfigModel'); + } }, render: function() { @@ -92,7 +135,10 @@ resharerInfo: this._renderResharerInfo(), sharePlaceholder: this._renderSharePlaceholderPart(), remoteShareInfo: this._renderRemoteShareInfoPart(), - linkShare: this._renderLinkSharePart() + linkShare: this._renderLinkSharePart(), + shareAllowed: this.model.hasSharePermission(), + noSharing: this._renderNoSharing(), + expiration: this._renderExpirationPart() })); return this; @@ -155,14 +201,34 @@ _renderLinkSharePart: function() { var linkShare = ''; - if(this._showLink && $('#allowShareWithLink').val() === 'yes') { + if( this.model.hasSharePermission() + && this._showLink + && $('#allowShareWithLink').val() === 'yes') + { var linkShareTemplate = this._getLinkShareTemplate(); + + var publicUpload = + this.model.isFolder() + && this.model.hasCreatePermission() + && this.configModel.isPublicUploadEnabled(); + + var publicUploadChecked = ''; + if(this.model.isPublicUploadAllowed) { + publicUploadChecked = 'checked="checked"'; + } + linkShare = linkShareTemplate({ linkShareLabel: t('core', 'Share link'), urlLabel: t('core', 'Link'), enablePasswordLabel: t('core', 'Password protect'), passwordLabel: t('core', 'Password'), - passwordPlaceholder: t('core', 'Choose a password for the public link') + passwordPlaceholder: t('core', 'Choose a password for the public link'), + publicUpload: publicUpload, + publicUploadChecked: publicUploadChecked, + publicUploadLabel: t('core', 'Allow editing'), + mailPublicNotificationEnabled: this.configModel.isMailPublicNotificationEnabled(), + mailPrivatePlaceholder: t('core', 'Email link to person'), + mailButtonText: t('core', 'Send') }); } return linkShare; @@ -176,6 +242,40 @@ return sharePlaceholder; }, + _renderNoSharing: function () { + var noSharing = ''; + if(!this.model.hasSharePermission()) { + var noSharingTemplate = this._getTemplate('noSharing', TEMPLATE_NO_SHARING); + noSharing = noSharingTemplate({ + placeholder: t('core', 'Resharing is not allowed') + }); + } + return noSharing; + }, + + _renderExpirationPart: function() { + var expirationTemplate = this._getTemplate('expiration', TEMPLATE_EXPIRATION); + + var defaultExpireMessage = ''; + if(( this.model.isFolder() || this.model.isFile()) + && this.configModel.isDefaultExpireDateEnforced()) { + defaultExpireMessage = t( + 'core', + 'The public link will expire no later than {days} days after it is created', + {'days': this.configModel.getDefaultExpireDate()} + ); + } + + var expiration = expirationTemplate({ + setExpirationLabel: t('core', 'Set expiration date'), + expirationLabel: t('core', 'Expiration'), + expirationDatePlaceholder: t('core', 'Expiration date'), + defaultExpireMessage: defaultExpireMessage + }); + + return expiration; + }, + /** * * @param {string} key - an identifier for the template diff --git a/core/js/shareitemmodel.js b/core/js/shareitemmodel.js index efce69f0f3ed..eb93b91ce92c 100644 --- a/core/js/shareitemmodel.js +++ b/core/js/shareitemmodel.js @@ -53,7 +53,10 @@ * // FIXME: use OC Share API once #17143 is done */ var ShareItemModel = OC.Backbone.Model.extend({ - initialize: function() { + initialize: function(attributes, options) { + if(!_.isUndefined(options.configModel)) { + this.configModel = options.configModel; + } this.fetch(); }, @@ -62,15 +65,24 @@ }, /** - * @returns {boolean|jQuery} + * @returns {boolean} */ - isPublicUploadEnabled: function() { - // FIXME: this really needs a better place - var publicUploadEnabled = $('#filestable').data('allow-public-upload'); - if (_.isUndefined(publicUploadEnabled)) { - publicUploadEnabled = 'no'; - } - return publicUploadEnabled; + isPublicUploadAllowed: function() { + return this.get('allowPublicUploadStatus'); + }, + + /** + * @returns {boolean} + */ + isFolder: function() { + return this.get('itemType') === 'folder'; + }, + + /** + * @returns {boolean} + */ + isFile: function() { + return this.get('itemType') === 'file'; }, /** @@ -134,7 +146,14 @@ * @returns {boolean} */ hasSharePermission: function() { - return (this.getPermissions & OC.PERMISSION_SHARE) === OC.PERMISSION_SHARE; + return (this.getPermissions() & OC.PERMISSION_SHARE) === OC.PERMISSION_SHARE; + }, + + /** + * @returns {boolean} + */ + hasCreatePermission: function() { + return (this.getPermissions() & OC.PERMISSION_CREATE) === OC.PERMISSION_CREATE; }, fetch: function() { diff --git a/lib/private/share/share.php b/lib/private/share/share.php index 65968f581f5a..cd3f0cbfb341 100644 --- a/lib/private/share/share.php +++ b/lib/private/share/share.php @@ -83,6 +83,7 @@ public static function registerBackend($itemType, $class, $collectionOf = null, 'supportedFileExtensions' => $supportedFileExtensions ); if(count(self::$backendTypes) === 1) { + \OC_Util::addScript('core', 'shareconfigmodel'); \OC_Util::addScript('core', 'shareitemmodel'); \OC_Util::addScript('core', 'sharedialogview'); \OC_Util::addScript('core', 'share'); From a57ff13a23ce06846160c1264369ff4b07619890 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Tue, 18 Aug 2015 12:13:08 +0200 Subject: [PATCH 10/59] move remaining global settings to configModel --- core/js/shareconfigmodel.js | 28 ++++++++++++++++++++++++++++ core/js/sharedialogview.js | 10 +++++----- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/core/js/shareconfigmodel.js b/core/js/shareconfigmodel.js index 371cc96df5ee..857e356a3517 100644 --- a/core/js/shareconfigmodel.js +++ b/core/js/shareconfigmodel.js @@ -19,6 +19,13 @@ publicUploadEnabled: false }, + /** + * @returns {boolean} + */ + areAvatarsEnabled: function() { + return oc_config.enable_avatars === true; + }, + /** * @returns {boolean} */ @@ -41,6 +48,27 @@ return oc_appconfig.core.defaultExpireDateEnforced === true; }, + /** + * @returns {boolean} + */ + isRemoteShareAllowed: function() { + return oc_appconfig.core.remoteShareAllowed; + }, + + /** + * @returns {boolean} + */ + isShareWithLinkAllowed: function() { + return $('#allowShareWithLink').val() === 'yes'; + }, + + /** + * @returns {string} + */ + getFederatedShareDocLink: function() { + return oc_appconfig.core.federatedCloudShareDoc; + }, + /** * @returns {number} */ diff --git a/core/js/sharedialogview.js b/core/js/sharedialogview.js index 7b0af2bc84ce..abf45a6bf45a 100644 --- a/core/js/sharedialogview.js +++ b/core/js/sharedialogview.js @@ -182,17 +182,17 @@ } return reshareTemplate({ - avatarEnabled: oc_config.enable_avatars === true, + avatarEnabled: this.configModel.areAvatarsEnabled(), sharedByText: sharedByText }); }, _renderRemoteShareInfoPart: function() { var remoteShareInfo = ''; - if(oc_appconfig.core.remoteShareAllowed) { + if(this.configModel.isRemoteShareAllowed()) { var infoTemplate = this._getRemoteShareInfoTemplate(); remoteShareInfo = infoTemplate({ - docLink: oc_appconfig.core.federatedCloudShareDoc, + docLink: this.configModel.getFederatedShareDocLink(), tooltip: t('core', 'Share with people on other ownClouds using the syntax username@example.com/owncloud') }); } @@ -203,7 +203,7 @@ var linkShare = ''; if( this.model.hasSharePermission() && this._showLink - && $('#allowShareWithLink').val() === 'yes') + && this.configModel.isShareWithLinkAllowed()) { var linkShareTemplate = this._getLinkShareTemplate(); @@ -236,7 +236,7 @@ _renderSharePlaceholderPart: function () { var sharePlaceholder = t('core', 'Share with users or groups …'); - if (oc_appconfig.core.remoteShareAllowed) { + if (this.configModel.isRemoteShareAllowed()) { sharePlaceholder = t('core', 'Share with users, groups or remote users …'); } return sharePlaceholder; From e3fd96fa8ef2aa5b5fd3881e75b95c7a869895fa Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Tue, 18 Aug 2015 13:04:02 +0200 Subject: [PATCH 11/59] improve doc --- core/js/share.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/js/share.js b/core/js/share.js index 8d25749a1078..84da511497e0 100644 --- a/core/js/share.js +++ b/core/js/share.js @@ -293,7 +293,9 @@ OC.Share = _.extend(OC.Share, { * * @param itemType * @param itemSource - * @param callback + * @param callback - optional. If a callback is given this method works + * asynchronous and the callback will be provided with data when the request + * is done. * @returns {OC.Share.Types.ShareInfo} */ loadItem:function(itemType, itemSource, callback) { From 1bd6942be70da621da9acae56a23a39040ef24bf Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Tue, 18 Aug 2015 17:23:32 +0200 Subject: [PATCH 12/59] show tooltips and load avatar --- core/js/sharedialogview.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/js/sharedialogview.js b/core/js/sharedialogview.js index abf45a6bf45a..5e3fb8b8d976 100644 --- a/core/js/sharedialogview.js +++ b/core/js/sharedialogview.js @@ -41,7 +41,7 @@ '
'; var TEMPLATE_REMOTE_SHARE_INFO = - ''; var TEMPLATE_LINK_SHARE = @@ -141,6 +141,9 @@ expiration: this._renderExpirationPart() })); + this.$el.find('.hasTooltip').tooltip(); + this.$el.find('.avatar').avatar(this.model.getReshareOwner, 32); + return this; }, @@ -196,6 +199,7 @@ tooltip: t('core', 'Share with people on other ownClouds using the syntax username@example.com/owncloud') }); } + return remoteShareInfo; }, From dcb084a617b2ee24031a14bce8b8e9749c821b75 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Wed, 19 Aug 2015 00:04:16 +0200 Subject: [PATCH 13/59] split ShareDialogResharerInfoView from base view --- core/js/sharedialogresharerinfoview.js | 117 +++++++++++++++++++++++++ core/js/sharedialogview.js | 62 ++++--------- core/js/shareitemmodel.js | 1 + lib/private/share/share.php | 1 + 4 files changed, 138 insertions(+), 43 deletions(-) create mode 100644 core/js/sharedialogresharerinfoview.js diff --git a/core/js/sharedialogresharerinfoview.js b/core/js/sharedialogresharerinfoview.js new file mode 100644 index 000000000000..3f996bb6d214 --- /dev/null +++ b/core/js/sharedialogresharerinfoview.js @@ -0,0 +1,117 @@ +/* + * 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 avatarEnabled}}' + + '
' + + ' {{/if}}' + + ' {{sharedByText}}' + + '

' + ; + + /** + * @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; + + //FIXME: specific to reshares stuff + this.model.on('change', function() { + view.render(); + }); + + if(!_.isUndefined(options.configModel)) { + this.configModel = options.configModel; + } else { + console.warn('missing OC.Share.ShareConfigModel'); + } + }, + + render: function() { + if ( !this.model.hasReshare() + || !this.model.getReshareOwner() !== OC.currentUser) + { + this.$el.html(''); + 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(), + sharedByText: sharedByText + })); + + return this; + }, + + /** + * @returns {Function} from Handlebars + * @private + */ + template: function () { + if (!this._template) { + this._template = Handlebars.compile(TEMPLATE); + } + return this._template; + } + + }); + + OC.Share.ShareDialogResharerInfoView = ShareDialogResharerInfoView; + +})(); diff --git a/core/js/sharedialogview.js b/core/js/sharedialogview.js index 5e3fb8b8d976..179d818e90f6 100644 --- a/core/js/sharedialogview.js +++ b/core/js/sharedialogview.js @@ -32,14 +32,6 @@ '{{{expiration}}}' ; - var TEMPLATE_RESHARER_INFO = - '' + - ' {{#if avatarEnabled}}' + - '
' + - ' {{/if}}' + - ' {{sharedByText}}' + - '

'; - var TEMPLATE_REMOTE_SHARE_INFO = ''; @@ -110,6 +102,9 @@ /** @type {OC.Share.ShareConfigModel} **/ configModel: undefined, + /** @type {object} **/ + resharerInfoView: undefined, + initialize: function(options) { var view = this; this.model.on('change', function() { @@ -125,14 +120,26 @@ } else { console.warn('missing OC.Share.ShareConfigModel'); } + + var subViewOptions = { + model: this.model, + configModel: this.configModel + }; + + this.resharerInfoView = _.isUndefined(options.resharerInfoView) + ? new OC.Share.ShareDialogResharerInfoView(subViewOptions) + : options.resharerInfoView; + }, render: function() { var baseTemplate = this._getTemplate('base', TEMPLATE_BASE); + this.resharerInfoView.render(); + this.$el.html(baseTemplate({ shareLabel: t('core', 'Share'), - resharerInfo: this._renderResharerInfo(), + resharerInfo: this.resharerInfoView.el.innerHTML, sharePlaceholder: this._renderSharePlaceholderPart(), remoteShareInfo: this._renderRemoteShareInfoPart(), linkShare: this._renderLinkSharePart(), @@ -142,7 +149,9 @@ })); this.$el.find('.hasTooltip').tooltip(); - this.$el.find('.avatar').avatar(this.model.getReshareOwner, 32); + if(this.configModel.areAvatarsEnabled()) { + this.$el.find('.avatar').avatar(this.model.getReshareOwner, 32); + } return this; }, @@ -157,39 +166,6 @@ this._showLink = (typeof showLink === 'boolean') ? showLink : true; }, - _renderResharerInfo: function() { - var resharerInfo = ''; - if ( !this.model.hasReshare() - || !this.model.getReshareOwner() !== OC.currentUser) - { - return ''; - } - - var reshareTemplate = this._getReshareTemplate(); - 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: this.model.getReshareOwnerDisplayname() - } - ); - } else { - sharedByText = t( - 'core', - 'Shared with you by {owner}', - { owner: this.model.getReshareOwnerDisplayname() } - ); - } - - return reshareTemplate({ - avatarEnabled: this.configModel.areAvatarsEnabled(), - sharedByText: sharedByText - }); - }, - _renderRemoteShareInfoPart: function() { var remoteShareInfo = ''; if(this.configModel.isRemoteShareAllowed()) { diff --git a/core/js/shareitemmodel.js b/core/js/shareitemmodel.js index eb93b91ce92c..0d2bce8b2efe 100644 --- a/core/js/shareitemmodel.js +++ b/core/js/shareitemmodel.js @@ -112,6 +112,7 @@ * @returns {string} */ getReshareOwnerDisplayname: function() { + return 'foo'; return this.get('reshare').displayname_owner; }, diff --git a/lib/private/share/share.php b/lib/private/share/share.php index cd3f0cbfb341..10d0480ab890 100644 --- a/lib/private/share/share.php +++ b/lib/private/share/share.php @@ -85,6 +85,7 @@ public static function registerBackend($itemType, $class, $collectionOf = null, if(count(self::$backendTypes) === 1) { \OC_Util::addScript('core', 'shareconfigmodel'); \OC_Util::addScript('core', 'shareitemmodel'); + \OC_Util::addScript('core', 'sharedialogresharerinfoview'); \OC_Util::addScript('core', 'sharedialogview'); \OC_Util::addScript('core', 'share'); \OC_Util::addStyle('core', 'share'); From 277b786886de7979fea308595e4cf658e85aacf7 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Fri, 21 Aug 2015 15:00:15 +0200 Subject: [PATCH 14/59] ShareDialogResharerInfoView improvements --- core/js/sharedialogresharerinfoview.js | 8 ++++---- core/js/sharedialogview.js | 8 ++++---- core/js/shareitemmodel.js | 3 ++- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/core/js/sharedialogresharerinfoview.js b/core/js/sharedialogresharerinfoview.js index 3f996bb6d214..8970d857fc39 100644 --- a/core/js/sharedialogresharerinfoview.js +++ b/core/js/sharedialogresharerinfoview.js @@ -51,8 +51,7 @@ initialize: function(options) { var view = this; - //FIXME: specific to reshares stuff - this.model.on('change', function() { + this.model.on('change:reshare', function() { view.render(); }); @@ -67,7 +66,7 @@ if ( !this.model.hasReshare() || !this.model.getReshareOwner() !== OC.currentUser) { - this.$el.html(''); + this.$el.empty(); return this; } @@ -91,7 +90,8 @@ ); } - this.$el.html(reshareTemplate({ + this.$el.empty(); + this.$el.append(reshareTemplate({ avatarEnabled: this.configModel.areAvatarsEnabled(), sharedByText: sharedByText })); diff --git a/core/js/sharedialogview.js b/core/js/sharedialogview.js index 179d818e90f6..67017778632b 100644 --- a/core/js/sharedialogview.js +++ b/core/js/sharedialogview.js @@ -14,7 +14,7 @@ } var TEMPLATE_BASE = - '{{{resharerInfo}}}' + + '
' + '' + '
' + ' ' + @@ -135,11 +135,8 @@ render: function() { var baseTemplate = this._getTemplate('base', TEMPLATE_BASE); - this.resharerInfoView.render(); - this.$el.html(baseTemplate({ shareLabel: t('core', 'Share'), - resharerInfo: this.resharerInfoView.el.innerHTML, sharePlaceholder: this._renderSharePlaceholderPart(), remoteShareInfo: this._renderRemoteShareInfoPart(), linkShare: this._renderLinkSharePart(), @@ -148,6 +145,9 @@ expiration: this._renderExpirationPart() })); + this.resharerInfoView.$el = this.$el.find('.resharerInfo'); + this.resharerInfoView.render(); + this.$el.find('.hasTooltip').tooltip(); if(this.configModel.areAvatarsEnabled()) { this.$el.find('.avatar').avatar(this.model.getReshareOwner, 32); diff --git a/core/js/shareitemmodel.js b/core/js/shareitemmodel.js index 0d2bce8b2efe..9c4a8141cda4 100644 --- a/core/js/shareitemmodel.js +++ b/core/js/shareitemmodel.js @@ -90,7 +90,8 @@ * @returns {boolean} */ hasReshare: function() { - return _.isObject(this.get('reshare')) && !_.isUndefined(this.get('reshare').uid_owner); + var reshare = this.get('reshare'); + return _.isObject(reshare) && !_.isUndefined(reshare.uid_owner); }, /** From f9c232c4ce16b101fce233f1f20e0cafc7e4d1fa Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Fri, 21 Aug 2015 15:40:50 +0200 Subject: [PATCH 15/59] split off linkShareView --- core/js/sharedialoglinkshareview.js | 143 ++++++++++++++++++++++++++++ core/js/sharedialogview.js | 94 +++--------------- lib/private/share/share.php | 1 + 3 files changed, 158 insertions(+), 80 deletions(-) create mode 100644 core/js/sharedialoglinkshareview.js diff --git a/core/js/sharedialoglinkshareview.js b/core/js/sharedialoglinkshareview.js new file mode 100644 index 000000000000..ccd705803f6a --- /dev/null +++ b/core/js/sharedialoglinkshareview.js @@ -0,0 +1,143 @@ +/* + * 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 = + '' + ; + + /** + * @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, + + 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(); + }); + + if(!_.isUndefined(options.configModel)) { + this.configModel = options.configModel; + } else { + console.warn('missing OC.Share.ShareConfigModel'); + } + }, + + render: function() { + if( !this.model.hasSharePermission() + || !this.showLink + || !this.configModel.isShareWithLinkAllowed()) + { + this.$el.empty(); + return this; + } + + var publicUpload = + this.model.isFolder() + && this.model.hasCreatePermission() + && this.configModel.isPublicUploadEnabled(); + + var publicUploadChecked = ''; + if(this.model.isPublicUploadAllowed()) { + publicUploadChecked = 'checked="checked"'; + } + + var linkShareTemplate = this.template(); + this.$el.empty(); + this.$el.append(linkShareTemplate({ + linkShareLabel: t('core', 'Share link'), + urlLabel: t('core', 'Link'), + enablePasswordLabel: t('core', 'Password protect'), + passwordLabel: t('core', 'Password'), + passwordPlaceholder: t('core', 'Choose a password for the public link'), + publicUpload: publicUpload, + publicUploadChecked: publicUploadChecked, + publicUploadLabel: t('core', 'Allow editing'), + mailPublicNotificationEnabled: this.configModel.isMailPublicNotificationEnabled(), + mailPrivatePlaceholder: t('core', 'Email link to person'), + mailButtonText: t('core', 'Send') + })); + + return this; + }, + + /** + * @returns {Function} from Handlebars + * @private + */ + template: function () { + if (!this._template) { + this._template = Handlebars.compile(TEMPLATE); + } + return this._template; + } + + }); + + OC.Share.ShareDialogLinkShareView = ShareDialogLinkShareView; + +})(); diff --git a/core/js/sharedialogview.js b/core/js/sharedialogview.js index 67017778632b..cc9590815a78 100644 --- a/core/js/sharedialogview.js +++ b/core/js/sharedialogview.js @@ -25,7 +25,7 @@ '
    ' + '
' + '{{#if shareAllowed}}' + - '{{{linkShare}}}' + + '
' + '{{else}}' + '{{{noSharing}}}' + '{{/if}}' + @@ -36,35 +36,6 @@ ''; - var TEMPLATE_LINK_SHARE = - '' - ; - var TEMPLATE_NO_SHARING = '' ; @@ -105,6 +76,9 @@ /** @type {object} **/ resharerInfoView: undefined, + /** @type {object} **/ + linkShareView: undefined, + initialize: function(options) { var view = this; this.model.on('change', function() { @@ -130,6 +104,10 @@ ? new OC.Share.ShareDialogResharerInfoView(subViewOptions) : options.resharerInfoView; + this.linkShareView = _.isUndefined(options.linkShareView) + ? new OC.Share.ShareDialogLinkShareView(subViewOptions) + : options.linkShareView; + }, render: function() { @@ -139,7 +117,6 @@ shareLabel: t('core', 'Share'), sharePlaceholder: this._renderSharePlaceholderPart(), remoteShareInfo: this._renderRemoteShareInfoPart(), - linkShare: this._renderLinkSharePart(), shareAllowed: this.model.hasSharePermission(), noSharing: this._renderNoSharing(), expiration: this._renderExpirationPart() @@ -148,6 +125,11 @@ this.resharerInfoView.$el = this.$el.find('.resharerInfo'); this.resharerInfoView.render(); + if(this.model.hasSharePermission()) { + this.linkShareView.$el = this.$el.find('.linkShare'); + this.linkShareView.render(); + } + this.$el.find('.hasTooltip').tooltip(); if(this.configModel.areAvatarsEnabled()) { this.$el.find('.avatar').avatar(this.model.getReshareOwner, 32); @@ -164,6 +146,7 @@ */ setShowLink: function(showLink) { this._showLink = (typeof showLink === 'boolean') ? showLink : true; + this.linkShareView.showLink = this._showLink; }, _renderRemoteShareInfoPart: function() { @@ -179,41 +162,6 @@ return remoteShareInfo; }, - _renderLinkSharePart: function() { - var linkShare = ''; - if( this.model.hasSharePermission() - && this._showLink - && this.configModel.isShareWithLinkAllowed()) - { - var linkShareTemplate = this._getLinkShareTemplate(); - - var publicUpload = - this.model.isFolder() - && this.model.hasCreatePermission() - && this.configModel.isPublicUploadEnabled(); - - var publicUploadChecked = ''; - if(this.model.isPublicUploadAllowed) { - publicUploadChecked = 'checked="checked"'; - } - - linkShare = linkShareTemplate({ - linkShareLabel: t('core', 'Share link'), - urlLabel: t('core', 'Link'), - enablePasswordLabel: t('core', 'Password protect'), - passwordLabel: t('core', 'Password'), - passwordPlaceholder: t('core', 'Choose a password for the public link'), - publicUpload: publicUpload, - publicUploadChecked: publicUploadChecked, - publicUploadLabel: t('core', 'Allow editing'), - mailPublicNotificationEnabled: this.configModel.isMailPublicNotificationEnabled(), - mailPrivatePlaceholder: t('core', 'Email link to person'), - mailButtonText: t('core', 'Send') - }); - } - return linkShare; - }, - _renderSharePlaceholderPart: function () { var sharePlaceholder = t('core', 'Share with users or groups …'); if (this.configModel.isRemoteShareAllowed()) { @@ -278,20 +226,6 @@ */ _getRemoteShareInfoTemplate: function() { return this._getTemplate('remoteShareInfo', TEMPLATE_REMOTE_SHARE_INFO); - }, - - /** - * returns the info template for link sharing - * - * @returns {Function} - * @private - */ - _getLinkShareTemplate: function() { - return this._getTemplate('linkShare', TEMPLATE_LINK_SHARE); - }, - - _getReshareTemplate: function() { - return this._getTemplate('reshare', TEMPLATE_RESHARER_INFO); } }); diff --git a/lib/private/share/share.php b/lib/private/share/share.php index 10d0480ab890..ea4f01c0a950 100644 --- a/lib/private/share/share.php +++ b/lib/private/share/share.php @@ -86,6 +86,7 @@ public static function registerBackend($itemType, $class, $collectionOf = null, \OC_Util::addScript('core', 'shareconfigmodel'); \OC_Util::addScript('core', 'shareitemmodel'); \OC_Util::addScript('core', 'sharedialogresharerinfoview'); + \OC_Util::addScript('core', 'sharedialoglinkshareview'); \OC_Util::addScript('core', 'sharedialogview'); \OC_Util::addScript('core', 'share'); \OC_Util::addStyle('core', 'share'); From ffd4e0dc5a1977885b639b3ba1bb87a533e6a75a Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Fri, 21 Aug 2015 19:35:13 +0200 Subject: [PATCH 16/59] split off expirationView --- core/js/sharedialogexpirationview.js | 93 ++++++++++++++++++++++++++++ core/js/sharedialogview.js | 54 +++++----------- lib/private/share/share.php | 1 + 3 files changed, 109 insertions(+), 39 deletions(-) create mode 100644 core/js/sharedialogexpirationview.js diff --git a/core/js/sharedialogexpirationview.js b/core/js/sharedialogexpirationview.js new file mode 100644 index 000000000000..e752c66bf355 --- /dev/null +++ b/core/js/sharedialogexpirationview.js @@ -0,0 +1,93 @@ +/* + * 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 = + '' + + '' + + '' + + '' + + '{{defaultExpireMessage}}' + ; + + /** + * @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, + + initialize: function(options) { + if(!_.isUndefined(options.configModel)) { + this.configModel = options.configModel; + } else { + console.warn('missing OC.Share.ShareConfigModel'); + } + }, + + render: function() { + var defaultExpireMessage = ''; + if( (this.model.isFolder() || this.model.isFile()) + && this.configModel.isDefaultExpireDateEnforced()) { + defaultExpireMessage = t( + 'core', + 'The public link will expire no later than {days} days after it is created', + {'days': this.configModel.getDefaultExpireDate()} + ); + } + + var expirationTemplate = this.template(); + this.$el.empty(); + this.$el.append(expirationTemplate({ + setExpirationLabel: t('core', 'Set expiration date'), + expirationLabel: t('core', 'Expiration'), + expirationDatePlaceholder: t('core', 'Expiration date'), + defaultExpireMessage: defaultExpireMessage + })); + + return this; + }, + + /** + * @returns {Function} from Handlebars + * @private + */ + template: function () { + if (!this._template) { + this._template = Handlebars.compile(TEMPLATE); + } + return this._template; + } + + }); + + OC.Share.ShareDialogExpirationView = ShareDialogExpirationView; + +})(); diff --git a/core/js/sharedialogview.js b/core/js/sharedialogview.js index cc9590815a78..cdf5214fa65b 100644 --- a/core/js/sharedialogview.js +++ b/core/js/sharedialogview.js @@ -14,7 +14,7 @@ } var TEMPLATE_BASE = - '
' + + '
' + '' + '
' + ' ' + @@ -25,11 +25,11 @@ '
    ' + '
' + '{{#if shareAllowed}}' + - '
' + + '
' + '{{else}}' + '{{{noSharing}}}' + '{{/if}}' + - '{{{expiration}}}' + '
' ; var TEMPLATE_REMOTE_SHARE_INFO = @@ -40,16 +40,6 @@ '' ; - var TEMPLATE_EXPIRATION = - '
' + - ' ' + - ' ' + - ' ' + - ' ' + - ' {{defaultExpireMessage}}' + - '
' - ; - /** * @class OCA.Share.ShareDialogView * @member {OC.Share.ShareItemModel} model @@ -79,6 +69,9 @@ /** @type {object} **/ linkShareView: undefined, + /** @type {object} **/ + expirationView: undefined, + initialize: function(options) { var view = this; this.model.on('change', function() { @@ -108,6 +101,10 @@ ? new OC.Share.ShareDialogLinkShareView(subViewOptions) : options.linkShareView; + this.expirationView = _.isUndefined(options.expirationView) + ? new OC.Share.ShareDialogExpirationView(subViewOptions) + : options.expirationView; + }, render: function() { @@ -119,14 +116,16 @@ remoteShareInfo: this._renderRemoteShareInfoPart(), shareAllowed: this.model.hasSharePermission(), noSharing: this._renderNoSharing(), - expiration: this._renderExpirationPart() })); - this.resharerInfoView.$el = this.$el.find('.resharerInfo'); + this.resharerInfoView.$el = this.$el.find('.resharerInfoView'); this.resharerInfoView.render(); + this.expirationView.$el = this.$el.find('.expirationView'); + this.expirationView.render(); + if(this.model.hasSharePermission()) { - this.linkShareView.$el = this.$el.find('.linkShare'); + this.linkShareView.$el = this.$el.find('.linkShareView'); this.linkShareView.render(); } @@ -181,29 +180,6 @@ return noSharing; }, - _renderExpirationPart: function() { - var expirationTemplate = this._getTemplate('expiration', TEMPLATE_EXPIRATION); - - var defaultExpireMessage = ''; - if(( this.model.isFolder() || this.model.isFile()) - && this.configModel.isDefaultExpireDateEnforced()) { - defaultExpireMessage = t( - 'core', - 'The public link will expire no later than {days} days after it is created', - {'days': this.configModel.getDefaultExpireDate()} - ); - } - - var expiration = expirationTemplate({ - setExpirationLabel: t('core', 'Set expiration date'), - expirationLabel: t('core', 'Expiration'), - expirationDatePlaceholder: t('core', 'Expiration date'), - defaultExpireMessage: defaultExpireMessage - }); - - return expiration; - }, - /** * * @param {string} key - an identifier for the template diff --git a/lib/private/share/share.php b/lib/private/share/share.php index ea4f01c0a950..b5d508b30ba7 100644 --- a/lib/private/share/share.php +++ b/lib/private/share/share.php @@ -87,6 +87,7 @@ public static function registerBackend($itemType, $class, $collectionOf = null, \OC_Util::addScript('core', 'shareitemmodel'); \OC_Util::addScript('core', 'sharedialogresharerinfoview'); \OC_Util::addScript('core', 'sharedialoglinkshareview'); + \OC_Util::addScript('core', 'sharedialogexpirationview'); \OC_Util::addScript('core', 'sharedialogview'); \OC_Util::addScript('core', 'share'); \OC_Util::addStyle('core', 'share'); From 8f3884145e3992ec5195d6bdd7c4c97926c0c582 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Fri, 21 Aug 2015 20:05:50 +0200 Subject: [PATCH 17/59] less stupid initalization of subviews --- core/js/sharedialogview.js | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/core/js/sharedialogview.js b/core/js/sharedialogview.js index cdf5214fa65b..b33130a83dc9 100644 --- a/core/js/sharedialogview.js +++ b/core/js/sharedialogview.js @@ -93,18 +93,18 @@ configModel: this.configModel }; - this.resharerInfoView = _.isUndefined(options.resharerInfoView) - ? new OC.Share.ShareDialogResharerInfoView(subViewOptions) - : options.resharerInfoView; - - this.linkShareView = _.isUndefined(options.linkShareView) - ? new OC.Share.ShareDialogLinkShareView(subViewOptions) - : options.linkShareView; - - this.expirationView = _.isUndefined(options.expirationView) - ? new OC.Share.ShareDialogExpirationView(subViewOptions) - : options.expirationView; + var subViews = { + resharerInfoView: 'ShareDialogResharerInfoView', + linkShareView: 'ShareDialogLinkShareView', + expirationView: 'ShareDialogExpirationView' + }; + for(var name in subViews) { + var className = subViews[name]; + this[name] = _.isUndefined(options[name]) + ? new OC.Share[className](subViewOptions) + : options[name]; + } }, render: function() { From 6af6024e172bc4810691f54ccaa2da6912395962 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Fri, 21 Aug 2015 20:29:12 +0200 Subject: [PATCH 18/59] integraton noshare part into ShareDialogLinkShareView --- core/js/sharedialoglinkshareview.js | 52 +++++++++++++++++------------ core/js/sharedialogview.js | 25 ++------------ 2 files changed, 33 insertions(+), 44 deletions(-) diff --git a/core/js/sharedialoglinkshareview.js b/core/js/sharedialoglinkshareview.js index ccd705803f6a..ca6413761b09 100644 --- a/core/js/sharedialoglinkshareview.js +++ b/core/js/sharedialoglinkshareview.js @@ -14,32 +14,34 @@ } var TEMPLATE = - '' + '{{else}}' + + '' + + '{{/if}}' ; /** @@ -88,11 +90,17 @@ }, render: function() { + var linkShareTemplate = this.template(); + if( !this.model.hasSharePermission() || !this.showLink || !this.configModel.isShareWithLinkAllowed()) { this.$el.empty(); + this.$el.append(linkShareTemplate({ + shareAllowed: false, + noSharingPlaceholder: t('core', 'Resharing is not allowed') + })); return this; } @@ -106,9 +114,9 @@ publicUploadChecked = 'checked="checked"'; } - var linkShareTemplate = this.template(); this.$el.empty(); this.$el.append(linkShareTemplate({ + shareAllowed: true, linkShareLabel: t('core', 'Share link'), urlLabel: t('core', 'Link'), enablePasswordLabel: t('core', 'Password protect'), diff --git a/core/js/sharedialogview.js b/core/js/sharedialogview.js index b33130a83dc9..63c3473252f2 100644 --- a/core/js/sharedialogview.js +++ b/core/js/sharedialogview.js @@ -24,11 +24,7 @@ '{{{remoteShareInfo}}}' + '
    ' + '
' + - '{{#if shareAllowed}}' + '
' + - '{{else}}' + - '{{{noSharing}}}' + - '{{/if}}' + '
' ; @@ -114,21 +110,17 @@ shareLabel: t('core', 'Share'), sharePlaceholder: this._renderSharePlaceholderPart(), remoteShareInfo: this._renderRemoteShareInfoPart(), - shareAllowed: this.model.hasSharePermission(), - noSharing: this._renderNoSharing(), })); 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(); - if(this.model.hasSharePermission()) { - this.linkShareView.$el = this.$el.find('.linkShareView'); - this.linkShareView.render(); - } - this.$el.find('.hasTooltip').tooltip(); if(this.configModel.areAvatarsEnabled()) { this.$el.find('.avatar').avatar(this.model.getReshareOwner, 32); @@ -169,17 +161,6 @@ return sharePlaceholder; }, - _renderNoSharing: function () { - var noSharing = ''; - if(!this.model.hasSharePermission()) { - var noSharingTemplate = this._getTemplate('noSharing', TEMPLATE_NO_SHARING); - noSharing = noSharingTemplate({ - placeholder: t('core', 'Resharing is not allowed') - }); - } - return noSharing; - }, - /** * * @param {string} key - an identifier for the template From 018d07b3e55fd7234008c51d0a124b7b681a1a4f Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Mon, 24 Aug 2015 13:00:03 +0200 Subject: [PATCH 19/59] Add share dialog into share tab --- apps/files_sharing/appinfo/app.php | 13 ++++++--- apps/files_sharing/js/share.js | 4 +-- apps/files_sharing/js/sharetabview.js | 38 ++++++++++++++++++++++----- 3 files changed, 42 insertions(+), 13 deletions(-) diff --git a/apps/files_sharing/appinfo/app.php b/apps/files_sharing/appinfo/app.php index 3049dc6a2637..2bb872eaad6a 100644 --- a/apps/files_sharing/appinfo/app.php +++ b/apps/files_sharing/appinfo/app.php @@ -54,9 +54,16 @@ \OCP\Share::registerBackend('file', 'OC_Share_Backend_File'); \OCP\Share::registerBackend('folder', 'OC_Share_Backend_Folder', 'file'); -\OCP\Util::addScript('files_sharing', 'share'); -\OCP\Util::addScript('files_sharing', 'sharetabview'); -\OCP\Util::addScript('files_sharing', 'external'); +$eventDispatcher = \OC::$server->getEventDispatcher(); +$eventDispatcher->addListener( + 'OCA\Files::loadAdditionalScripts', + function() { + \OCP\Util::addScript('files_sharing', 'share'); + \OCP\Util::addScript('files_sharing', 'sharetabview'); + \OCP\Util::addScript('files_sharing', 'external'); + } +); + // \OCP\Util::addStyle('files_sharing', 'sharetabview'); \OC::$server->getActivityManager()->registerExtension(function() { diff --git a/apps/files_sharing/js/share.js b/apps/files_sharing/js/share.js index c124d390d047..a4ae7fad8993 100644 --- a/apps/files_sharing/js/share.js +++ b/apps/files_sharing/js/share.js @@ -151,9 +151,7 @@ } }); - OC.addScript('files_sharing', 'sharetabview').done(function() { - fileList.registerTabView(new OCA.Sharing.ShareTabView('shareTabView')); - }); + fileList.registerTabView(new OCA.Sharing.ShareTabView('shareTabView')); }, /** diff --git a/apps/files_sharing/js/sharetabview.js b/apps/files_sharing/js/sharetabview.js index ee572b747ead..99a5a0fb1db3 100644 --- a/apps/files_sharing/js/sharetabview.js +++ b/apps/files_sharing/js/sharetabview.js @@ -10,7 +10,10 @@ (function() { var TEMPLATE = - '
    {{#if owner}}
  • Owner: {{owner}}
  • {{/if}}
'; + '
' + + '
    {{#if owner}}
  • Owner: {{owner}}
  • {{/if}}
' + + '
' + + '
'; /** * @memberof OCA.Sharing @@ -20,7 +23,12 @@ id: 'shareTabView', className: 'tab shareTabView', - _template: null, + template: function(params) { + if (!this._template) { + this._template = Handlebars.compile(TEMPLATE); + } + return this._template(params); + }, getLabel: function() { return t('files_sharing', 'Sharing'); @@ -30,10 +38,10 @@ * Renders this details view */ render: function() { - this.$el.empty(); - - if (!this._template) { - this._template = Handlebars.compile(TEMPLATE); + if (this._dialog) { + // remove/destroy older instance + this._dialog.remove(); + this._dialog = null; } if (this.model) { @@ -42,11 +50,27 @@ if (owner === OC.currentUser) { owner = null; } - this.$el.append(this._template({ + this.$el.html(this.template({ owner: owner })); + var attributes = { + itemType: 'file', + itemSource: this.model.get('id'), + // TODO: make these available + possiblePermissions: this.model.get('sharingPossiblePermissions') + }; + var shareModel = new OC.Share.ShareItemModel(attributes, {configModel: configModel}); + var configModel = new OC.Share.ShareConfigModel(); + this._dialog = new OC.Share.ShareDialogView({ + configModel: configModel, + model: shareModel + }); + this.$el.find('.dialogContainer').append(this._dialog.$el); + this._dialog.render(); + shareModel.fetch(); } else { + this.$el.empty(); // TODO: render placeholder text? } } From f709022559d434612f995ecce56a65b034590f6b Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Mon, 24 Aug 2015 13:15:33 +0200 Subject: [PATCH 20/59] Fix share permissions for share tab --- apps/files_sharing/js/sharetabview.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/files_sharing/js/sharetabview.js b/apps/files_sharing/js/sharetabview.js index 99a5a0fb1db3..11f25c0c47cc 100644 --- a/apps/files_sharing/js/sharetabview.js +++ b/apps/files_sharing/js/sharetabview.js @@ -57,8 +57,7 @@ var attributes = { itemType: 'file', itemSource: this.model.get('id'), - // TODO: make these available - possiblePermissions: this.model.get('sharingPossiblePermissions') + possiblePermissions: this.model.get('sharePermissions') }; var shareModel = new OC.Share.ShareItemModel(attributes, {configModel: configModel}); var configModel = new OC.Share.ShareConfigModel(); From fdb95613e9b133feed7b7dbad22dc289a54ef094 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Mon, 24 Aug 2015 23:20:01 +0200 Subject: [PATCH 21/59] simplification, and throwing where throwing is needed --- core/js/sharedialogexpirationview.js | 5 ++--- core/js/sharedialoglinkshareview.js | 8 +++----- core/js/sharedialogresharerinfoview.js | 5 ++--- core/js/sharedialogview.js | 2 +- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/core/js/sharedialogexpirationview.js b/core/js/sharedialogexpirationview.js index e752c66bf355..4c628f5498a3 100644 --- a/core/js/sharedialogexpirationview.js +++ b/core/js/sharedialogexpirationview.js @@ -48,7 +48,7 @@ if(!_.isUndefined(options.configModel)) { this.configModel = options.configModel; } else { - console.warn('missing OC.Share.ShareConfigModel'); + throw 'missing OC.Share.ShareConfigModel'; } }, @@ -64,8 +64,7 @@ } var expirationTemplate = this.template(); - this.$el.empty(); - this.$el.append(expirationTemplate({ + this.$el.html(expirationTemplate({ setExpirationLabel: t('core', 'Set expiration date'), expirationLabel: t('core', 'Expiration'), expirationDatePlaceholder: t('core', 'Expiration date'), diff --git a/core/js/sharedialoglinkshareview.js b/core/js/sharedialoglinkshareview.js index ca6413761b09..ff22b629dc40 100644 --- a/core/js/sharedialoglinkshareview.js +++ b/core/js/sharedialoglinkshareview.js @@ -85,7 +85,7 @@ if(!_.isUndefined(options.configModel)) { this.configModel = options.configModel; } else { - console.warn('missing OC.Share.ShareConfigModel'); + throw 'missing OC.Share.ShareConfigModel'; } }, @@ -96,8 +96,7 @@ || !this.showLink || !this.configModel.isShareWithLinkAllowed()) { - this.$el.empty(); - this.$el.append(linkShareTemplate({ + this.$el.html(linkShareTemplate({ shareAllowed: false, noSharingPlaceholder: t('core', 'Resharing is not allowed') })); @@ -114,8 +113,7 @@ publicUploadChecked = 'checked="checked"'; } - this.$el.empty(); - this.$el.append(linkShareTemplate({ + this.$el.html(linkShareTemplate({ shareAllowed: true, linkShareLabel: t('core', 'Share link'), urlLabel: t('core', 'Link'), diff --git a/core/js/sharedialogresharerinfoview.js b/core/js/sharedialogresharerinfoview.js index 8970d857fc39..63df25b4ed83 100644 --- a/core/js/sharedialogresharerinfoview.js +++ b/core/js/sharedialogresharerinfoview.js @@ -58,7 +58,7 @@ if(!_.isUndefined(options.configModel)) { this.configModel = options.configModel; } else { - console.warn('missing OC.Share.ShareConfigModel'); + throw 'missing OC.Share.ShareConfigModel'; } }, @@ -90,8 +90,7 @@ ); } - this.$el.empty(); - this.$el.append(reshareTemplate({ + this.$el.html(reshareTemplate({ avatarEnabled: this.configModel.areAvatarsEnabled(), sharedByText: sharedByText })); diff --git a/core/js/sharedialogview.js b/core/js/sharedialogview.js index 63c3473252f2..e07a5cee49a5 100644 --- a/core/js/sharedialogview.js +++ b/core/js/sharedialogview.js @@ -81,7 +81,7 @@ if(!_.isUndefined(options.configModel)) { this.configModel = options.configModel; } else { - console.warn('missing OC.Share.ShareConfigModel'); + throw 'missing OC.Share.ShareConfigModel'; } var subViewOptions = { From f2fb20ed1cfc88c5e37c2ecc84ec4ec77d98f371 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Mon, 24 Aug 2015 23:27:43 +0200 Subject: [PATCH 22/59] no auto-fetch in model --- core/js/share.js | 1 + core/js/shareitemmodel.js | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/core/js/share.js b/core/js/share.js index 84da511497e0..1fc1ac9e20cb 100644 --- a/core/js/share.js +++ b/core/js/share.js @@ -404,6 +404,7 @@ OC.Share = _.extend(OC.Share, { $dialog.slideDown(OC.menuSpeed, function() { OC.Share.droppedDown = true; }); + itemModel.fetch(); return; diff --git a/core/js/shareitemmodel.js b/core/js/shareitemmodel.js index 9c4a8141cda4..0d823f05749b 100644 --- a/core/js/shareitemmodel.js +++ b/core/js/shareitemmodel.js @@ -57,7 +57,6 @@ if(!_.isUndefined(options.configModel)) { this.configModel = options.configModel; } - this.fetch(); }, defaults: { From 755d4016b11cd90806e29b0aafcd2ad09161ae57 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Mon, 24 Aug 2015 23:37:04 +0200 Subject: [PATCH 23/59] set default value and remove now superflous method --- core/js/shareitemmodel.js | 24 +++++------------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/core/js/shareitemmodel.js b/core/js/shareitemmodel.js index 0d823f05749b..c5d0581390c7 100644 --- a/core/js/shareitemmodel.js +++ b/core/js/shareitemmodel.js @@ -60,7 +60,8 @@ }, defaults: { - allowPublicUploadStatus: false + allowPublicUploadStatus: false, + permissions: 0 }, /** @@ -130,31 +131,18 @@ return this.get('reshare').share_type; }, - /** - * @returns {number} - */ - getPermissions: function() { - var permissions = this.get('permissions'); - if(_.isUndefined(permissions)) { - // model was not properly initialized - console.warn('Sharing error: undefined permissions'); - permissions = 0; - } - return permissions; - }, - /** * @returns {boolean} */ hasSharePermission: function() { - return (this.getPermissions() & OC.PERMISSION_SHARE) === OC.PERMISSION_SHARE; + return (this.get('permissions') & OC.PERMISSION_SHARE) === OC.PERMISSION_SHARE; }, /** * @returns {boolean} */ hasCreatePermission: function() { - return (this.getPermissions() & OC.PERMISSION_CREATE) === OC.PERMISSION_CREATE; + return (this.get('permissions') & OC.PERMISSION_CREATE) === OC.PERMISSION_CREATE; }, fetch: function() { @@ -186,14 +174,12 @@ }); } - var attributes = { + return { reshare: data.reshare, shares: data.shares, permissions: permissions, allowPublicUploadStatus: allowPublicUploadStatus }; - - return attributes; } }); From f62a3be59069510f4afa479316a665011cd014f5 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Tue, 25 Aug 2015 14:41:52 +0200 Subject: [PATCH 24/59] cleanup --- core/js/sharedialogview.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/core/js/sharedialogview.js b/core/js/sharedialogview.js index e07a5cee49a5..8cd68962d423 100644 --- a/core/js/sharedialogview.js +++ b/core/js/sharedialogview.js @@ -32,10 +32,6 @@ ''; - var TEMPLATE_NO_SHARING = - '' - ; - /** * @class OCA.Share.ShareDialogView * @member {OC.Share.ShareItemModel} model From c17d022ca42dda31f321276f2c4973ca2e43c8a4 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Tue, 25 Aug 2015 16:07:14 +0200 Subject: [PATCH 25/59] started to implement sharee list view. not completed yet, do not cry please. --- core/js/shareconfigmodel.js | 7 ++ core/js/sharedialogshareelistview.js | 111 +++++++++++++++++++++++++++ core/js/sharedialogview.js | 9 ++- lib/private/share/share.php | 1 + 4 files changed, 125 insertions(+), 3 deletions(-) create mode 100644 core/js/sharedialogshareelistview.js diff --git a/core/js/shareconfigmodel.js b/core/js/shareconfigmodel.js index 857e356a3517..e948c57cbacb 100644 --- a/core/js/shareconfigmodel.js +++ b/core/js/shareconfigmodel.js @@ -55,6 +55,13 @@ return oc_appconfig.core.remoteShareAllowed; }, + /** + * @returns {boolean} + */ + isResharingAllowed: function() { + return oc_appconfig.core.resharingAllowed + }, + /** * @returns {boolean} */ diff --git a/core/js/sharedialogshareelistview.js b/core/js/sharedialogshareelistview.js new file mode 100644 index 000000000000..177e0b4a899c --- /dev/null +++ b/core/js/sharedialogshareelistview.js @@ -0,0 +1,111 @@ +/* + * 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 = + '
    ' + + '{{#each sharees}}' + + '
  • ' + + ' {{unshareLabel}}' + + ' {{#if avatarEnabled}}' + + '
    ' + + ' {{/if}}' + + ' {{shareWithDisplayName}}' + + ' {{#if mailPublicNotificationEnabled}} {{#unless isRemoteShare}}' + + ' ' + + ' {{/unless}} {{/if}}' + + ' {{#if isResharingAllowed}} {{#if hasSharePermission}}' + + ' {{/if}} {{/if}}' + + '
  • ' + + '{{/each}}' + + '
' + ; + + /** + * @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, + + initialize: function(options) { + if(!_.isUndefined(options.configModel)) { + this.configModel = options.configModel; + } else { + throw 'missing OC.Share.ShareConfigModel'; + } + }, + + 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') + }; + + // TODO: sharess must have following attributes + // shareType + // shareWith + // shareWithDisplayName + // isRemoteShare + // isMailSent + + var list = _.extend({}, universal); + + return list; + }, + + render: function() { + var shareeListTemplate = this.template(); + this.$el.html(shareeListTemplate({ + sharees: this.getShareeList() + })); + + return this; + }, + + /** + * @returns {Function} from Handlebars + * @private + */ + template: function () { + if (!this._template) { + this._template = Handlebars.compile(TEMPLATE); + } + return this._template; + } + + }); + + OC.Share.ShareDialogShareeListView = ShareDialogShareeListView; + +})(); diff --git a/core/js/sharedialogview.js b/core/js/sharedialogview.js index 8cd68962d423..2348c14bb88c 100644 --- a/core/js/sharedialogview.js +++ b/core/js/sharedialogview.js @@ -22,8 +22,7 @@ '
' + // FIXME: find a good position for remoteShareInfo '{{{remoteShareInfo}}}' + - '
    ' + - '
' + + '
' + '
' + '
' ; @@ -88,7 +87,8 @@ var subViews = { resharerInfoView: 'ShareDialogResharerInfoView', linkShareView: 'ShareDialogLinkShareView', - expirationView: 'ShareDialogExpirationView' + expirationView: 'ShareDialogExpirationView', + shareeListView: 'ShareDialogShareeListView' }; for(var name in subViews) { @@ -117,6 +117,9 @@ this.expirationView.$el = this.$el.find('.expirationView'); this.expirationView.render(); + this.shareeListView.$el = this.$el.find('.shareeListView'); + this.shareeListView.redner(); + this.$el.find('.hasTooltip').tooltip(); if(this.configModel.areAvatarsEnabled()) { this.$el.find('.avatar').avatar(this.model.getReshareOwner, 32); diff --git a/lib/private/share/share.php b/lib/private/share/share.php index b5d508b30ba7..802b146cfb62 100644 --- a/lib/private/share/share.php +++ b/lib/private/share/share.php @@ -88,6 +88,7 @@ public static function registerBackend($itemType, $class, $collectionOf = null, \OC_Util::addScript('core', 'sharedialogresharerinfoview'); \OC_Util::addScript('core', 'sharedialoglinkshareview'); \OC_Util::addScript('core', 'sharedialogexpirationview'); + \OC_Util::addScript('core', 'sharedialogshareelistview'); \OC_Util::addScript('core', 'sharedialogview'); \OC_Util::addScript('core', 'share'); \OC_Util::addStyle('core', 'share'); From 5db1db38efffc2110e34886263f3a1117fe8efa5 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Tue, 1 Sep 2015 12:43:04 +0200 Subject: [PATCH 26/59] continue to reimplement sharee list view. still WIP --- core/js/sharedialoglinkshareview.js | 4 +- core/js/sharedialogresharerinfoview.js | 3 +- core/js/sharedialogshareelistview.js | 113 ++++++++++++- core/js/sharedialogview.js | 14 +- core/js/shareitemmodel.js | 212 ++++++++++++++++++++++++- 5 files changed, 327 insertions(+), 19 deletions(-) diff --git a/core/js/sharedialoglinkshareview.js b/core/js/sharedialoglinkshareview.js index ff22b629dc40..eebf8a34247c 100644 --- a/core/js/sharedialoglinkshareview.js +++ b/core/js/sharedialoglinkshareview.js @@ -92,7 +92,7 @@ render: function() { var linkShareTemplate = this.template(); - if( !this.model.hasSharePermission() + if( !this.model.sharePermissionPossible() || !this.showLink || !this.configModel.isShareWithLinkAllowed()) { @@ -105,7 +105,7 @@ var publicUpload = this.model.isFolder() - && this.model.hasCreatePermission() + && this.model.createPermissionPossible() && this.configModel.isPublicUploadEnabled(); var publicUploadChecked = ''; diff --git a/core/js/sharedialogresharerinfoview.js b/core/js/sharedialogresharerinfoview.js index 63df25b4ed83..f7de90e264f8 100644 --- a/core/js/sharedialogresharerinfoview.js +++ b/core/js/sharedialogresharerinfoview.js @@ -16,7 +16,7 @@ var TEMPLATE = '' + ' {{#if avatarEnabled}}' + - '
' + + '
' + ' {{/if}}' + ' {{sharedByText}}' + '

' @@ -92,6 +92,7 @@ this.$el.html(reshareTemplate({ avatarEnabled: this.configModel.areAvatarsEnabled(), + reshareOwner: this.model.getReshareOwner(), sharedByText: sharedByText })); diff --git a/core/js/sharedialogshareelistview.js b/core/js/sharedialogshareelistview.js index 177e0b4a899c..8179926ad512 100644 --- a/core/js/sharedialogshareelistview.js +++ b/core/js/sharedialogshareelistview.js @@ -16,18 +16,41 @@ var TEMPLATE = '
    ' + '{{#each sharees}}' + + ' {{#if isCollection}}' + + '
  • {{text}}
  • ' + + ' {{/if}}' + + ' {{#unless isCollection}}' + '
  • ' + ' {{unshareLabel}}' + ' {{#if avatarEnabled}}' + - '
    ' + + '
    ' + ' {{/if}}' + ' {{shareWithDisplayName}}' + ' {{#if mailPublicNotificationEnabled}} {{#unless isRemoteShare}}' + - ' ' + + ' ' + ' {{/unless}} {{/if}}' + - ' {{#if isResharingAllowed}} {{#if hasSharePermission}}' + + ' {{#if isResharingAllowed}} {{#if sharePermissionPossible}}' + + ' ' + ' {{/if}} {{/if}}' + + ' {{#if editPermissionPossible}}' + + ' ' + + ' {{/if}}' + + ' {{#unless isRemoteShare}}' + + ' {{crudsLabel}}' + + '
    ' + + ' {{#if createPermissionPossible}}' + + ' ' + + ' {{/if}}' + + ' {{#if updatePermissionPossible}}' + + ' ' + + ' {{/if}}' + + ' {{#if deletePermissionPossible}}' + + ' ' + + ' {{/if}}' + + '
    ' + + ' {{/unless}}' + '
  • ' + + ' {{/unless}}' + '{{/each}}' + '
' ; @@ -61,6 +84,54 @@ } else { throw 'missing OC.Share.ShareConfigModel'; } + + var view = this; + this.model.on('change:shares', function() { + view.render(); + }); + }, + + getCollectionObject: function(shareIndex) { + var type = this.model.getCollectionType(shareIndex); + var id = this.model.getCollectionPath(shareIndex); + if(type !== 'file' && type !== 'folder') { + id = this.model.getCollectionSource(shareIndex); + } + return { + collectionID: id, + text: t('core', 'Shared in {item} with {user}', {'item': id, user: this.model.getShareWithDisplayName(shareIndex)}) + } + }, + + /** + * + * @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); + + 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') + ')'; + } + + + return { + 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 + }; }, getShareeList: function() { @@ -69,23 +140,49 @@ mailPublicNotificationEnabled: this.configModel.isMailPublicNotificationEnabled(), notifyByMailLabel: t('core', 'notify by email'), unshareLabel: t('core', 'Unshare'), - unshareImage: OC.imagePath('core', 'actions/delete') + 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.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 }; // TODO: sharess must have following attributes - // shareType - // shareWith - // shareWithDisplayName // isRemoteShare // isMailSent - var list = _.extend({}, universal); + if(!this.model.hasShares()) { + return []; + } + + var list = []; + for(var index in this.model.get('shares')) { + if(this.model.isCollection(index)) { + list.unshift(this.getCollectionObject(index)); + } else { + list.push(_.extend(this.getShareeObject(index), universal)) + } + } return list; }, render: function() { var shareeListTemplate = this.template(); + var list = this.getShareeList(); this.$el.html(shareeListTemplate({ sharees: this.getShareeList() })); diff --git a/core/js/sharedialogview.js b/core/js/sharedialogview.js index 2348c14bb88c..9bfe38eea3cf 100644 --- a/core/js/sharedialogview.js +++ b/core/js/sharedialogview.js @@ -63,6 +63,9 @@ /** @type {object} **/ expirationView: undefined, + /** @type {object} **/ + shareeListView: undefined, + initialize: function(options) { var view = this; this.model.on('change', function() { @@ -118,11 +121,18 @@ this.expirationView.render(); this.shareeListView.$el = this.$el.find('.shareeListView'); - this.shareeListView.redner(); + this.shareeListView.render(); this.$el.find('.hasTooltip').tooltip(); if(this.configModel.areAvatarsEnabled()) { - this.$el.find('.avatar').avatar(this.model.getReshareOwner, 32); + this.$el.find('.avatar').each(function() { + var $this = $(this); + $this.avatar($this.data('username'), 32); + }); + this.$el.find('.avatar.imageplaceholderseed').each(function() { + var $this = $(this); + $this.imageplaceholder($this.data('seed')); + }); } return this; diff --git a/core/js/shareitemmodel.js b/core/js/shareitemmodel.js index c5d0581390c7..b5370faca0a0 100644 --- a/core/js/shareitemmodel.js +++ b/core/js/shareitemmodel.js @@ -14,6 +14,13 @@ OC.Share.Types = {}; } + /** + * @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 @@ -33,7 +40,7 @@ * @property {string} share_with * @property {string} share_with_displayname * @property {string} share_mail_send - * @property {bool} collection //TODO: verify + * @property {OC.Share.Types.Collection|undefined} collection * @property {Date} expiration optional? * @property {number} stime optional? */ @@ -95,13 +102,79 @@ }, /** - * whether this item has reshare information + * whether this item has share information * @returns {boolean} */ hasShares: function() { - return _.isObject(this.get('shares')); + var shares = this.get('shares'); + return _.isArray(this.get('shares')); }, + /** + * @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} */ @@ -131,20 +204,145 @@ 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"; + } + 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'; + }, + /** * @returns {boolean} */ - hasSharePermission: function() { + 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} */ - hasCreatePermission: function() { + 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) { @@ -159,6 +357,8 @@ return {}; } + console.log(data.shares); + var permissions = this.get('possiblePermissions'); if(!_.isUndefined(data.reshare) && !_.isUndefined(data.reshare.permissions)) { permissions = permissions & data.reshare.permissions; @@ -176,7 +376,7 @@ return { reshare: data.reshare, - shares: data.shares, + shares: $.map(data.shares, function(value) { return [value]; }), permissions: permissions, allowPublicUploadStatus: allowPublicUploadStatus }; From 44ecdde10d347dc0e77576b35dc3d066dfc0fce6 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Wed, 2 Sep 2015 13:06:00 +0200 Subject: [PATCH 27/59] sharee list view: better handle collections --- core/js/sharedialogshareelistview.js | 41 ++++++++++++++++++++++++---- core/js/shareitemmodel.js | 4 +-- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/core/js/sharedialogshareelistview.js b/core/js/sharedialogshareelistview.js index 8179926ad512..8e08e2b4f76c 100644 --- a/core/js/sharedialogshareelistview.js +++ b/core/js/sharedialogshareelistview.js @@ -78,6 +78,9 @@ /** @type {boolean} **/ showLink: true, + /** @type {object} **/ + _collections: {}, + initialize: function(options) { if(!_.isUndefined(options.configModel)) { this.configModel = options.configModel; @@ -91,6 +94,23 @@ }); }, + 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; + } + }, + getCollectionObject: function(shareIndex) { var type = this.model.getCollectionType(shareIndex); var id = this.model.getCollectionPath(shareIndex); @@ -119,7 +139,6 @@ shareWithDisplayName = shareWithDisplayName + " (" + t('core', 'remote') + ')'; } - return { hasSharePermission: this.model.hasSharePermission(shareIndex), hasEditPermission: this.model.hasEditPermission(shareIndex), @@ -160,21 +179,34 @@ deletePermission: OC.PERMISSION_DELETE }; + this._collections = {}; + // TODO: sharess must have following attributes // isRemoteShare - // isMailSent if(!this.model.hasShares()) { return []; } + var shares = this.model.get('shares'); var list = []; - for(var index in this.model.get('shares')) { + for(var index = 0; index < shares.length; index++) { + + // #### FIXME: LEGACY #### + // this does not belong to a view + var shareType = this.model.getShareType(index); + if (!OC.Share.currentShares[shareType]) { + OC.Share.currentShares[shareType] = []; + } + OC.Share.currentShares[shareType].push(this.model.getShareWith(index)); + // #### /FIXME: LEGACY #### + if(this.model.isCollection(index)) { - list.unshift(this.getCollectionObject(index)); + this.processCollectionShare(index); } else { list.push(_.extend(this.getShareeObject(index), universal)) } + list = _.union(_.values(this._collections), list); } return list; @@ -182,7 +214,6 @@ render: function() { var shareeListTemplate = this.template(); - var list = this.getShareeList(); this.$el.html(shareeListTemplate({ sharees: this.getShareeList() })); diff --git a/core/js/shareitemmodel.js b/core/js/shareitemmodel.js index b5370faca0a0..d4fbc3543a7b 100644 --- a/core/js/shareitemmodel.js +++ b/core/js/shareitemmodel.js @@ -357,8 +357,6 @@ return {}; } - console.log(data.shares); - var permissions = this.get('possiblePermissions'); if(!_.isUndefined(data.reshare) && !_.isUndefined(data.reshare.permissions)) { permissions = permissions & data.reshare.permissions; @@ -376,7 +374,7 @@ return { reshare: data.reshare, - shares: $.map(data.shares, function(value) { return [value]; }), + shares: _.toArray(data.shares), permissions: permissions, allowPublicUploadStatus: allowPublicUploadStatus }; From 60abfcdab16ed674015422a6300bd28f3baa5e91 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Wed, 2 Sep 2015 16:47:25 +0200 Subject: [PATCH 28/59] old OC.Share.addShareWith now reimplemented --- core/js/share.js | 106 --------------------------- core/js/sharedialogshareelistview.js | 28 +------ core/js/shareitemmodel.js | 26 ++++++- 3 files changed, 27 insertions(+), 133 deletions(-) diff --git a/core/js/share.js b/core/js/share.js index 1fc1ac9e20cb..bd87ab10d40b 100644 --- a/core/js/share.js +++ b/core/js/share.js @@ -685,112 +685,6 @@ OC.Share = _.extend(OC.Share, { } }); }, - addShareWith:function(shareType, shareWith, shareWithDisplayName, permissions, possiblePermissions, mailSend, collection) { - var shareItem = { - share_type: shareType, - share_with: shareWith, - share_with_displayname: shareWithDisplayName, - permissions: permissions - }; - if (shareType === OC.Share.SHARE_TYPE_GROUP) { - shareWithDisplayName = shareWithDisplayName + " (" + t('core', 'group') + ')'; - } - if (shareType === OC.Share.SHARE_TYPE_REMOTE) { - shareWithDisplayName = shareWithDisplayName + " (" + t('core', 'remote') + ')'; - } - if (!OC.Share.itemShares[shareType]) { - OC.Share.itemShares[shareType] = []; - } - OC.Share.itemShares[shareType].push(shareWith); - if (collection) { - if (collection.item_type == 'file' || collection.item_type == 'folder') { - var item = collection.path; - } else { - var item = collection.item_source; - } - var collectionList = $('#shareWithList li').filterAttr('data-collection', item); - if (collectionList.length > 0) { - $(collectionList).append(', '+shareWithDisplayName); - } else { - var html = '
  • '+t('core', 'Shared in {item} with {user}', {'item': item, user: shareWithDisplayName})+'
  • '; - $('#shareWithList').prepend(html); - } - } else { - var editChecked = createChecked = updateChecked = deleteChecked = shareChecked = ''; - if (permissions & OC.PERMISSION_CREATE) { - createChecked = 'checked="checked"'; - editChecked = 'checked="checked"'; - } - if (permissions & OC.PERMISSION_UPDATE) { - updateChecked = 'checked="checked"'; - editChecked = 'checked="checked"'; - } - if (permissions & OC.PERMISSION_DELETE) { - deleteChecked = 'checked="checked"'; - editChecked = 'checked="checked"'; - } - if (permissions & OC.PERMISSION_SHARE) { - shareChecked = 'checked="checked"'; - } - var html = '
  • '; - var showCrudsButton; - html += ''+t('core', 'Unshare')+''; - if (oc_config.enable_avatars === true) { - html += '
    '; - } - html += '' + escapeHTML(shareWithDisplayName) + ''; - var mailNotificationEnabled = $('input:hidden[name=mailNotificationEnabled]').val(); - if (mailNotificationEnabled === 'yes' && shareType !== OC.Share.SHARE_TYPE_REMOTE) { - var checked = ''; - if (mailSend === '1') { - checked = 'checked'; - } - html += ' '; - } - if (oc_appconfig.core.resharingAllowed && (possiblePermissions & OC.PERMISSION_SHARE)) { - html += ''; - } - if (possiblePermissions & OC.PERMISSION_CREATE || possiblePermissions & OC.PERMISSION_UPDATE || possiblePermissions & OC.PERMISSION_DELETE) { - html += ''; - } - if (shareType !== OC.Share.SHARE_TYPE_REMOTE) { - showCrudsButton = ''+t('core', 'access control')+''; - } - html += ''; - html += '
  • '; - html = $(html).appendTo('#shareWithList'); - if (oc_config.enable_avatars === true) { - if (shareType === OC.Share.SHARE_TYPE_USER) { - html.find('.avatar').avatar(escapeHTML(shareWith), 32); - } else { - //Add sharetype to generate different seed if there is a group and use with the same name - html.find('.avatar').imageplaceholder(escapeHTML(shareWith) + ' ' + shareType); - } - } - // insert cruds button into last label element - var lastLabel = html.find('>label:last'); - if (lastLabel.exists()){ - lastLabel.append(showCrudsButton); - } - else{ - html.find('.cruds').before(showCrudsButton); - } - if (!OC.Share.currentShares[shareType]) { - OC.Share.currentShares[shareType] = []; - } - OC.Share.currentShares[shareType].push(shareItem); - } - }, showLink:function(token, password, itemSource) { OC.Share.itemShares[OC.Share.SHARE_TYPE_LINK] = true; $('#linkCheckbox').attr('checked', true); diff --git a/core/js/sharedialogshareelistview.js b/core/js/sharedialogshareelistview.js index 8e08e2b4f76c..f9d725bf51eb 100644 --- a/core/js/sharedialogshareelistview.js +++ b/core/js/sharedialogshareelistview.js @@ -111,18 +111,6 @@ } }, - getCollectionObject: function(shareIndex) { - var type = this.model.getCollectionType(shareIndex); - var id = this.model.getCollectionPath(shareIndex); - if(type !== 'file' && type !== 'folder') { - id = this.model.getCollectionSource(shareIndex); - } - return { - collectionID: id, - text: t('core', 'Shared in {item} with {user}', {'item': id, user: this.model.getShareWithDisplayName(shareIndex)}) - } - }, - /** * * @param {OC.Share.Types.ShareInfo} shareInfo @@ -149,7 +137,8 @@ shareWith: shareWith, shareWithDisplayName: shareWithDisplayName, shareType: shareType, - modSeed: shareType !== OC.Share.SHARE_TYPE_USER + modSeed: shareType !== OC.Share.SHARE_TYPE_USER, + isRemoteShare: shareType === OC.Share.SHARE_TYPE_REMOTE }; }, @@ -181,9 +170,6 @@ this._collections = {}; - // TODO: sharess must have following attributes - // isRemoteShare - if(!this.model.hasShares()) { return []; } @@ -191,16 +177,6 @@ var shares = this.model.get('shares'); var list = []; for(var index = 0; index < shares.length; index++) { - - // #### FIXME: LEGACY #### - // this does not belong to a view - var shareType = this.model.getShareType(index); - if (!OC.Share.currentShares[shareType]) { - OC.Share.currentShares[shareType] = []; - } - OC.Share.currentShares[shareType].push(this.model.getShareWith(index)); - // #### /FIXME: LEGACY #### - if(this.model.isCollection(index)) { this.processCollectionShare(index); } else { diff --git a/core/js/shareitemmodel.js b/core/js/shareitemmodel.js index d4fbc3543a7b..8afc4954902b 100644 --- a/core/js/shareitemmodel.js +++ b/core/js/shareitemmodel.js @@ -350,6 +350,27 @@ }); }, + legacyFillCurrentShares: function(shares) { + OC.Share.currentShares = {}; + OC.Share.itemShares = []; + _.each(shares, + /** + * @param {OC.Share.Types.ShareInfo} share + */ + function(share) { + if (!OC.Share.currentShares[share.share_type]) { + OC.Share.currentShares[share.share_type] = []; + } + OC.Share.currentShares[share.share_type].push(share); + + 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'); @@ -372,9 +393,12 @@ }); } + var shares = _.toArray(data.shares); + this.legacyFillCurrentShares(shares); + return { reshare: data.reshare, - shares: _.toArray(data.shares), + shares: shares, permissions: permissions, allowPublicUploadStatus: allowPublicUploadStatus }; From 5dc2c35ce5f3023ccea4cc599cf54d6b1c0e2c01 Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Wed, 2 Sep 2015 17:27:25 +0200 Subject: [PATCH 29/59] fixed set of possible permissions for remote shares --- core/js/sharedialogshareelistview.js | 19 ++++++++++++------- core/js/shareitemmodel.js | 6 ++++++ 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/core/js/sharedialogshareelistview.js b/core/js/sharedialogshareelistview.js index f9d725bf51eb..6a356b8144c8 100644 --- a/core/js/sharedialogshareelistview.js +++ b/core/js/sharedialogshareelistview.js @@ -29,9 +29,9 @@ ' {{#if mailPublicNotificationEnabled}} {{#unless isRemoteShare}}' + ' ' + ' {{/unless}} {{/if}}' + - ' {{#if isResharingAllowed}} {{#if sharePermissionPossible}}' + + ' {{#if isResharingAllowed}} {{#if sharePermissionPossible}} {{#unless isRemoteShare}}' + ' ' + - ' {{/if}} {{/if}}' + + ' {{/unless}} {{/if}} {{/if}}' + ' {{#if editPermissionPossible}}' + ' ' + ' {{/if}}' + @@ -44,9 +44,9 @@ ' {{#if updatePermissionPossible}}' + ' ' + ' {{/if}}' + - ' {{#if deletePermissionPossible}}' + + ' {{#if deletePermissionPossible}} {{#unless isRemoteShare}}' + ' ' + - ' {{/if}}' + + ' {{/unless}} {{/if}}' + '
    ' + ' {{/unless}}' + ' ' + @@ -121,13 +121,18 @@ 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 { + return _.extend(hasPermissionOverride, { hasSharePermission: this.model.hasSharePermission(shareIndex), hasEditPermission: this.model.hasEditPermission(shareIndex), hasCreatePermission: this.model.hasCreatePermission(shareIndex), @@ -139,7 +144,7 @@ shareType: shareType, modSeed: shareType !== OC.Share.SHARE_TYPE_USER, isRemoteShare: shareType === OC.Share.SHARE_TYPE_REMOTE - }; + }); }, getShareeList: function() { @@ -180,7 +185,7 @@ if(this.model.isCollection(index)) { this.processCollectionShare(index); } else { - list.push(_.extend(this.getShareeObject(index), universal)) + list.push(_.extend(universal, this.getShareeObject(index))) } list = _.union(_.values(this._collections), list); } diff --git a/core/js/shareitemmodel.js b/core/js/shareitemmodel.js index 8afc4954902b..acaa890be115 100644 --- a/core/js/shareitemmodel.js +++ b/core/js/shareitemmodel.js @@ -253,6 +253,12 @@ 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; }, From ce1b0c650e3a0e4cb701609ee08a13182909219a Mon Sep 17 00:00:00 2001 From: Arthur Schiwon Date: Thu, 3 Sep 2015 15:53:17 +0200 Subject: [PATCH 30/59] show link share --- apps/files_sharing/js/sharetabview.js | 6 ++- core/css/share.css | 4 -- core/js/shareconfigmodel.js | 3 +- core/js/sharedialogexpirationview.js | 10 ++++- core/js/sharedialoglinkshareview.js | 32 ++++++++++----- core/js/shareitemmodel.js | 57 ++++++++++++++++++++++++++- 6 files changed, 92 insertions(+), 20 deletions(-) diff --git a/apps/files_sharing/js/sharetabview.js b/apps/files_sharing/js/sharetabview.js index 11f25c0c47cc..6836107306ab 100644 --- a/apps/files_sharing/js/sharetabview.js +++ b/apps/files_sharing/js/sharetabview.js @@ -45,7 +45,6 @@ } if (this.model) { - console.log(this.model); var owner = this.model.get('shareOwner'); if (owner === OC.currentUser) { owner = null; @@ -59,8 +58,11 @@ itemSource: this.model.get('id'), possiblePermissions: this.model.get('sharePermissions') }; - var shareModel = new OC.Share.ShareItemModel(attributes, {configModel: configModel}); var configModel = new OC.Share.ShareConfigModel(); + var shareModel = new OC.Share.ShareItemModel(attributes, { + configModel: configModel, + fileInfoModel: this.model + }); this._dialog = new OC.Share.ShareDialogView({ configModel: configModel, model: shareModel diff --git a/core/css/share.css b/core/css/share.css index 0d687cb76dac..bdf34e9a863c 100644 --- a/core/css/share.css +++ b/core/css/share.css @@ -126,10 +126,6 @@ a.unshare { margin-right: 0; } -#linkText,#linkPass,#expiration { - display:none; -} - #link #showPassword img { padding-left:5px; width:12px; diff --git a/core/js/shareconfigmodel.js b/core/js/shareconfigmodel.js index e948c57cbacb..846b2692ecbe 100644 --- a/core/js/shareconfigmodel.js +++ b/core/js/shareconfigmodel.js @@ -16,7 +16,8 @@ var ShareConfigModel = OC.Backbone.Model.extend({ defaults: { - publicUploadEnabled: false + publicUploadEnabled: false, + enforcePasswordForPublicLink: oc_appconfig.core.enforcePasswordForPublicLink }, /** diff --git a/core/js/sharedialogexpirationview.js b/core/js/sharedialogexpirationview.js index 4c628f5498a3..9fb404a090e0 100644 --- a/core/js/sharedialogexpirationview.js +++ b/core/js/sharedialogexpirationview.js @@ -14,11 +14,14 @@ } var TEMPLATE = + // well that could go to linkShareView… + '{{#if isLinkShare}}' + '' + '' + '' + '' + - '{{defaultExpireMessage}}' + '{{defaultExpireMessage}}' + + '{{/if}}' ; /** @@ -44,6 +47,8 @@ /** @type {boolean} **/ showLink: true, + className: 'hidden', + initialize: function(options) { if(!_.isUndefined(options.configModel)) { this.configModel = options.configModel; @@ -68,7 +73,8 @@ setExpirationLabel: t('core', 'Set expiration date'), expirationLabel: t('core', 'Expiration'), expirationDatePlaceholder: t('core', 'Expiration date'), - defaultExpireMessage: defaultExpireMessage + defaultExpireMessage: defaultExpireMessage, + isLinkShare: this.model.get('linkShare').isLinkShare })); return this; diff --git a/core/js/sharedialoglinkshareview.js b/core/js/sharedialoglinkshareview.js index eebf8a34247c..373d6d180bc1 100644 --- a/core/js/sharedialoglinkshareview.js +++ b/core/js/sharedialoglinkshareview.js @@ -16,16 +16,20 @@ var TEMPLATE = '{{#if shareAllowed}}' + '' + - '' + + '' + '
    ' + '' + - '' + - '' + + '' + + ' {{#if showPasswordCheckBox}}' + + '' + + ' {{/if}}' + + ' {{#if isPasswordSet}}' + '
    ' + ' ' + - ' ' + + ' ' + ' ' + '
    ' + + ' {{/if}}' + ' {{#if publicUpload}}' + ''; - dropDownEl = $(html); - dropDownEl.appendTo(appendTo); - } - dropDownEl.attr('data-item-source-name', filename); - $('#dropdown').slideDown(OC.menuSpeed, function() { - OC.Share.droppedDown = true; - }); - if ($('html').hasClass('lte9')){ - $('#dropdown input[placeholder]').placeholder(); - } - $('#shareWith').focus(); }, hideDropDown:function(callback) { OC.Share.currentShares = null; @@ -687,66 +421,6 @@ OC.Share = _.extend(OC.Share, { }, dirname:function(path) { return path.replace(/\\/g,'/').replace(/\/[^\/]*$/, ''); - }, - /** - * Parses a string to an valid integer (unix timestamp) - * @param time - * @returns {*} - * @internal Only used to work around a bug in the backend - */ - _parseTime: function(time) { - if (_.isString(time)) { - // skip empty strings and hex values - if (time === '' || (time.length > 1 && time[0] === '0' && time[1] === 'x')) { - return null; - } - time = parseInt(time, 10); - if(isNaN(time)) { - time = null; - } - } - return time; - }, - /** - * Displays the expiration date field - * - * @param {Date} date current expiration date - * @param {int} [shareTime] share timestamp in seconds, defaults to now - */ - showExpirationDate:function(date, shareTime) { - var now = new Date(); - // min date should always be the next day - var minDate = new Date(); - minDate.setDate(minDate.getDate()+1); - var datePickerOptions = { - minDate: minDate, - maxDate: null - }; - // TODO: hack: backend returns string instead of integer - shareTime = OC.Share._parseTime(shareTime); - if (_.isNumber(shareTime)) { - shareTime = new Date(shareTime * 1000); - } - if (!shareTime) { - shareTime = now; - } - $('#expirationCheckbox').attr('checked', true); - $('#expirationDate').val(date); - $('#expirationDate').slideDown(OC.menuSpeed); - $('#expirationDate').css('display','block'); - $('#expirationDate').datepicker({ - dateFormat : 'dd-mm-yy' - }); - if (oc_appconfig.core.defaultExpireDateEnforced) { - $('#expirationCheckbox').attr('disabled', true); - shareTime = OC.Util.stripTime(shareTime).getTime(); - // max date is share date + X days - datePickerOptions.maxDate = new Date(shareTime + oc_appconfig.core.defaultExpireDate * 24 * 3600 * 1000); - } - if(oc_appconfig.core.defaultExpireDateEnabled) { - $('#defaultExpireMessage').slideDown(OC.menuSpeed); - } - $.datepicker.setDefaults(datePickerOptions); } }); @@ -776,104 +450,6 @@ $(document).ready(function() { } }); - $(document).on('click', '#dropdown #expirationCheckbox', function() { - if (this.checked) { - OC.Share.showExpirationDate(''); - } else { - var itemType = $('#dropdown').data('item-type'); - var itemSource = $('#dropdown').data('item-source'); - $.post(OC.filePath('core', 'ajax', 'share.php'), { action: 'setExpirationDate', itemType: itemType, itemSource: itemSource, date: '' }, function(result) { - if (!result || result.status !== 'success') { - OC.dialogs.alert(t('core', 'Error unsetting expiration date'), t('core', 'Error')); - } - $('#expirationDate').slideUp(OC.menuSpeed); - if (oc_appconfig.core.defaultExpireDateEnforced === false) { - $('#defaultExpireMessage').slideDown(OC.menuSpeed); - } - }); - } - }); - - $(document).on('change', '#dropdown #expirationDate', function() { - var itemType = $('#dropdown').data('item-type'); - var itemSource = $('#dropdown').data('item-source'); - - $(this).tipsy('hide'); - $(this).removeClass('error'); - - $.post(OC.filePath('core', 'ajax', 'share.php'), { action: 'setExpirationDate', itemType: itemType, itemSource: itemSource, date: $(this).val() }, function(result) { - if (!result || result.status !== 'success') { - var expirationDateField = $('#dropdown #expirationDate'); - if (!result.data.message) { - expirationDateField.attr('original-title', t('core', 'Error setting expiration date')); - } else { - expirationDateField.attr('original-title', result.data.message); - } - expirationDateField.tipsy({gravity: 'n'}); - expirationDateField.tipsy('show'); - expirationDateField.addClass('error'); - } else { - if (oc_appconfig.core.defaultExpireDateEnforced === 'no') { - $('#defaultExpireMessage').slideUp(OC.menuSpeed); - } - } - }); - }); - - - $(document).on('submit', '#dropdown #emailPrivateLink', function(event) { - event.preventDefault(); - var link = $('#linkText').val(); - var itemType = $('#dropdown').data('item-type'); - var itemSource = $('#dropdown').data('item-source'); - var file = $('tr').filterAttr('data-id', String(itemSource)).data('file'); - var email = $('#email').val(); - var expirationDate = ''; - if ( $('#expirationCheckbox').is(':checked') === true ) { - expirationDate = $( "#expirationDate" ).val(); - } - if (email != '') { - $('#email').prop('disabled', true); - $('#email').val(t('core', 'Sending ...')); - $('#emailButton').prop('disabled', true); - - $.post(OC.filePath('core', 'ajax', 'share.php'), { action: 'email', toaddress: email, link: link, itemType: itemType, itemSource: itemSource, file: file, expiration: expirationDate}, - function(result) { - $('#email').prop('disabled', false); - $('#emailButton').prop('disabled', false); - if (result && result.status == 'success') { - $('#email').css('font-weight', 'bold').val(t('core','Email sent')); - setTimeout(function() { - $('#email').css('font-weight', 'normal').val(''); - }, 2000); - } else { - OC.dialogs.alert(result.data.message, t('core', 'Error while sharing')); - } - }); - } - }); - - $(document).on('click', '#dropdown input[name=mailNotification]', function() { - var $li = $(this).closest('li'); - var itemType = $('#dropdown').data('item-type'); - var itemSource = $('#dropdown').data('item-source'); - var action = ''; - if (this.checked) { - action = 'informRecipients'; - } else { - action = 'informRecipientsDisabled'; - } - - var shareType = $li.data('share-type'); - var shareWith = $li.attr('data-share-with'); - - $.post(OC.filePath('core', 'ajax', 'share.php'), {action: action, recipient: shareWith, shareType: shareType, itemSource: itemSource, itemType: itemType}, function(result) { - if (result.status !== 'success') { - OC.dialogs.alert(t('core', result.data.message), t('core', 'Warning')); - } - }); - -}); }); From 886f1ed660e91edb8f370cf711ed395fdff7dc30 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Tue, 15 Sep 2015 10:27:33 +0200 Subject: [PATCH 53/59] Update JS unit tests for share dialog (WIP) --- apps/files_sharing/js/sharetabview.js | 1 + apps/files_sharing/tests/js/shareSpec.js | 125 +-- core/js/core.json | 7 + core/js/share.js | 4 +- core/js/shareconfigmodel.js | 2 + core/js/sharedialoglinkshareview.js | 2 +- core/js/tests/specs/shareSpec.js | 1100 -------------------- core/js/tests/specs/sharedialogviewSpec.js | 911 ++++++++++++++++ core/js/tests/specs/shareitemmodelSpec.js | 234 +++++ tests/karma.config.js | 3 +- 10 files changed, 1217 insertions(+), 1172 deletions(-) create mode 100644 core/js/tests/specs/sharedialogviewSpec.js create mode 100644 core/js/tests/specs/shareitemmodelSpec.js diff --git a/apps/files_sharing/js/sharetabview.js b/apps/files_sharing/js/sharetabview.js index 1ad173659570..68288500ae0d 100644 --- a/apps/files_sharing/js/sharetabview.js +++ b/apps/files_sharing/js/sharetabview.js @@ -55,6 +55,7 @@ owner: owner })); + // TODO: the model should read these directly off the passed fileInfoModel var attributes = { itemType: this.model.isDirectory() ? 'folder' : 'file', itemSource: this.model.get('id'), diff --git a/apps/files_sharing/tests/js/shareSpec.js b/apps/files_sharing/tests/js/shareSpec.js index b6368b901eeb..96a96f1b8146 100644 --- a/apps/files_sharing/tests/js/shareSpec.js +++ b/apps/files_sharing/tests/js/shareSpec.js @@ -97,7 +97,6 @@ describe('OCA.Sharing.Util tests', function() { }]); $tr = fileList.$el.find('tbody tr:first'); $action = $tr.find('.action-share'); - expect($action.hasClass('permanent')).toEqual(true); expect(OC.basename($action.find('img').attr('src'))).toEqual('share.svg'); expect(OC.basename(getImageUrl($tr.find('.filename .thumbnail')))).toEqual('folder.svg'); expect($action.find('img').length).toEqual(1); @@ -116,7 +115,6 @@ describe('OCA.Sharing.Util tests', function() { }]); $tr = fileList.$el.find('tbody tr:first'); $action = $tr.find('.action-share'); - expect($action.hasClass('permanent')).toEqual(true); expect($action.find('>span').text().trim()).toEqual('Shared'); expect(OC.basename($action.find('img').attr('src'))).toEqual('share.svg'); expect(OC.basename(getImageUrl($tr.find('.filename .thumbnail')))).toEqual('folder-shared.svg'); @@ -137,7 +135,6 @@ describe('OCA.Sharing.Util tests', function() { }]); $tr = fileList.$el.find('tbody tr:first'); $action = $tr.find('.action-share'); - expect($action.hasClass('permanent')).toEqual(true); expect($action.find('>span').text().trim()).toEqual('Shared'); expect(OC.basename($action.find('img').attr('src'))).toEqual('public.svg'); expect(OC.basename(getImageUrl($tr.find('.filename .thumbnail')))).toEqual('folder-public.svg'); @@ -158,7 +155,6 @@ describe('OCA.Sharing.Util tests', function() { }]); $tr = fileList.$el.find('tbody tr:first'); $action = $tr.find('.action-share'); - expect($action.hasClass('permanent')).toEqual(true); expect($action.find('>span').text().trim()).toEqual('User One'); expect(OC.basename($action.find('img').attr('src'))).toEqual('share.svg'); expect(OC.basename(getImageUrl($tr.find('.filename .thumbnail')))).toEqual('folder-shared.svg'); @@ -178,7 +174,6 @@ describe('OCA.Sharing.Util tests', function() { }]); $tr = fileList.$el.find('tbody tr:first'); $action = $tr.find('.action-share'); - expect($action.hasClass('permanent')).toEqual(true); expect($action.find('>span').text().trim()).toEqual('Shared with User One, User Two'); expect(OC.basename($action.find('img').attr('src'))).toEqual('share.svg'); expect(OC.basename(getImageUrl($tr.find('.filename .thumbnail')))).toEqual('folder-shared.svg'); @@ -200,7 +195,6 @@ describe('OCA.Sharing.Util tests', function() { $tr = fileList.$el.find('tbody tr:first'); expect($tr.find('.action-share').length).toEqual(0); $action = $tr.find('.action-share-notification'); - expect($action.hasClass('permanent')).toEqual(true); expect($action.find('>span').text().trim()).toEqual('User One'); expect(OC.basename($action.find('img').attr('src'))).toEqual('share.svg'); expect(OC.basename(getImageUrl($tr.find('.filename .thumbnail')))).toEqual('folder-shared.svg'); @@ -225,7 +219,7 @@ describe('OCA.Sharing.Util tests', function() { }); }); describe('Share action', function() { - var showDropDownStub; + var shareTab; function makeDummyShareItem(displayName) { return { @@ -234,12 +228,35 @@ describe('OCA.Sharing.Util tests', function() { } beforeEach(function() { - showDropDownStub = sinon.stub(OC.Share, 'showDropDown', function() { - $('#testArea').append($('')); - }); + // make it look like not the "All files" list + fileList.id = 'test'; + shareTab = fileList._detailsView._tabViews[0]; }); afterEach(function() { - showDropDownStub.restore(); + shareTab = null; + }); + it('clicking share action opens sidebar and share tab', function() { + var showDetailsViewStub = sinon.stub(fileList, 'showDetailsView'); + + fileList.setFiles([{ + id: 1, + type: 'file', + name: 'One.txt', + path: '/subdir', + mimetype: 'text/plain', + size: 12, + permissions: OC.PERMISSION_ALL, + etag: 'abc' + }]); + + var $tr = fileList.$el.find('tr:first'); + $tr.find('.action-share').click(); + + expect(showDetailsViewStub.calledOnce).toEqual(true); + expect(showDetailsViewStub.getCall(0).args[0]).toEqual('One.txt'); + expect(showDetailsViewStub.getCall(0).args[1]).toEqual('shareTabView'); + + showDetailsViewStub.restore(); }); it('adds share icon after sharing a non-shared file', function() { var $action, $tr; @@ -257,24 +274,20 @@ describe('OCA.Sharing.Util tests', function() { $action = fileList.$el.find('tbody tr:first .action-share'); $tr = fileList.$el.find('tr:first'); - expect($action.hasClass('permanent')).toEqual(true); - $tr.find('.action-share').click(); - expect(showDropDownStub.calledOnce).toEqual(true); - - // simulate what the dropdown does - var shares = {}; - OC.Share.itemShares[OC.Share.SHARE_TYPE_USER] = ['user1', 'user2']; - OC.Share.itemShares[OC.Share.SHARE_TYPE_GROUP] = ['group1', 'group2']; - shares[OC.Share.SHARE_TYPE_USER] = _.map(['User One', 'User Two'], makeDummyShareItem); - shares[OC.Share.SHARE_TYPE_GROUP] = _.map(['Group One', 'Group Two'], makeDummyShareItem); - $('#dropdown').trigger(new $.Event('sharesChanged', {shares: shares})); + // simulate updating shares + shareTab._dialog.model.set({ + shares: [ + {share_with_displayname: 'User One'}, + {share_with_displayname: 'User Two'}, + {share_with_displayname: 'Group One'}, + {share_with_displayname: 'Group Two'} + ] + }); expect($tr.attr('data-share-recipients')).toEqual('Group One, Group Two, User One, User Two'); - OC.Share.updateIcon('file', 1); - expect($action.hasClass('permanent')).toEqual(true); expect($action.find('>span').text().trim()).toEqual('Shared with Group One, Group Two, User One, User Two'); expect(OC.basename($action.find('img').attr('src'))).toEqual('share.svg'); }); @@ -294,23 +307,19 @@ describe('OCA.Sharing.Util tests', function() { $action = fileList.$el.find('tbody tr:first .action-share'); $tr = fileList.$el.find('tr:first'); - expect($action.hasClass('permanent')).toEqual(true); - $tr.find('.action-share').click(); - expect(showDropDownStub.calledOnce).toEqual(true); - - // simulate what the dropdown does - var shares = {}; - OC.Share.itemShares[OC.Share.SHARE_TYPE_USER] = ['user1', 'user2', 'user3']; - shares[OC.Share.SHARE_TYPE_USER] = _.map(['User One', 'User Two', 'User Three'], makeDummyShareItem); - $('#dropdown').trigger(new $.Event('sharesChanged', {shares: shares})); + // simulate updating shares + shareTab._dialog.model.set({ + shares: [ + {share_with_displayname: 'User One'}, + {share_with_displayname: 'User Two'}, + {share_with_displayname: 'User Three'} + ] + }); expect($tr.attr('data-share-recipients')).toEqual('User One, User Three, User Two'); - OC.Share.updateIcon('file', 1); - - expect($action.hasClass('permanent')).toEqual(true); expect($action.find('>span').text().trim()).toEqual('Shared with User One, User Three, User Two'); expect(OC.basename($action.find('img').attr('src'))).toEqual('share.svg'); }); @@ -331,20 +340,14 @@ describe('OCA.Sharing.Util tests', function() { $action = fileList.$el.find('tbody tr:first .action-share'); $tr = fileList.$el.find('tr:first'); - expect($action.hasClass('permanent')).toEqual(true); - $tr.find('.action-share').click(); - expect(showDropDownStub.calledOnce).toEqual(true); - - // simulate what the dropdown does - OC.Share.itemShares = {}; - $('#dropdown').trigger(new $.Event('sharesChanged', {shares: {}})); + // simulate updating shares + shareTab._dialog.model.set({ + shares: [] + }); expect($tr.attr('data-share-recipients')).not.toBeDefined(); - - OC.Share.updateIcon('file', 1); - expect($action.hasClass('permanent')).toEqual(true); }); it('keep share text after updating reshare', function() { var $action, $tr; @@ -363,23 +366,15 @@ describe('OCA.Sharing.Util tests', function() { $action = fileList.$el.find('tbody tr:first .action-share'); $tr = fileList.$el.find('tr:first'); - expect($action.hasClass('permanent')).toEqual(true); - $tr.find('.action-share').click(); - expect(showDropDownStub.calledOnce).toEqual(true); - - // simulate what the dropdown does - var shares = {}; - OC.Share.itemShares[OC.Share.SHARE_TYPE_USER] = ['user2']; - shares[OC.Share.SHARE_TYPE_USER] = _.map(['User Two'], makeDummyShareItem); - $('#dropdown').trigger(new $.Event('sharesChanged', {shares: shares})); + // simulate updating shares + shareTab._dialog.model.set({ + shares: [{share_with_displayname: 'User Two'}] + }); expect($tr.attr('data-share-recipients')).toEqual('User Two'); - OC.Share.updateIcon('file', 1); - - expect($action.hasClass('permanent')).toEqual(true); expect($action.find('>span').text().trim()).toEqual('User One'); expect(OC.basename($action.find('img').attr('src'))).toEqual('share.svg'); }); @@ -401,21 +396,15 @@ describe('OCA.Sharing.Util tests', function() { $action = fileList.$el.find('tbody tr:first .action-share'); $tr = fileList.$el.find('tr:first'); - expect($action.hasClass('permanent')).toEqual(true); - $tr.find('.action-share').click(); - expect(showDropDownStub.calledOnce).toEqual(true); - - // simulate what the dropdown does - OC.Share.itemShares = {}; - $('#dropdown').trigger(new $.Event('sharesChanged', {shares: {}})); + // simulate updating shares + shareTab._dialog.model.set({ + shares: [] + }); expect($tr.attr('data-share-recipients')).not.toBeDefined(); - OC.Share.updateIcon('file', 1); - - expect($action.hasClass('permanent')).toEqual(true); expect($action.find('>span').text().trim()).toEqual('User One'); expect(OC.basename($action.find('img').attr('src'))).toEqual('share.svg'); }); diff --git a/core/js/core.json b/core/js/core.json index a67491c4a357..a80636e84636 100644 --- a/core/js/core.json +++ b/core/js/core.json @@ -24,6 +24,13 @@ "l10n.js", "apps.js", "share.js", + "shareconfigmodel.js", + "shareitemmodel.js", + "sharedialogview.js", + "sharedialogexpirationview.js", + "sharedialoglinkshareview.js", + "sharedialogresharerinfoview.js", + "sharedialogshareelistview.js", "octemplate.js", "eventsource.js", "config.js", diff --git a/core/js/share.js b/core/js/share.js index 0a3bba49b6c2..a2e6e6af0fcc 100644 --- a/core/js/share.js +++ b/core/js/share.js @@ -3,7 +3,7 @@ /** * @namespace */ -OC.Share = _.extend(OC.Share, { +OC.Share = _.extend(OC.Share || {}, { SHARE_TYPE_USER:0, SHARE_TYPE_GROUP:1, SHARE_TYPE_LINK:3, @@ -445,7 +445,7 @@ $(document).ready(function() { var target = $(event.target); var isMatched = !target.is('.drop, .ui-datepicker-next, .ui-datepicker-prev, .ui-icon') && !target.closest('#ui-datepicker-div').length && !target.closest('.ui-autocomplete').length; - if (OC.Share.droppedDown && isMatched && $('#dropdown').has(event.target).length === 0) { + if (OC.Share && OC.Share.droppedDown && isMatched && $('#dropdown').has(event.target).length === 0) { OC.Share.hideDropDown(); } }); diff --git a/core/js/shareconfigmodel.js b/core/js/shareconfigmodel.js index 16f7e2514d84..8729698d136f 100644 --- a/core/js/shareconfigmodel.js +++ b/core/js/shareconfigmodel.js @@ -14,6 +14,8 @@ OC.Share.Types = {}; } + // FIXME: the config model should populate its own model attributes based on + // the old DOM-based config var ShareConfigModel = OC.Backbone.Model.extend({ defaults: { publicUploadEnabled: false, diff --git a/core/js/sharedialoglinkshareview.js b/core/js/sharedialoglinkshareview.js index 1191aa401b4a..2c7914b25456 100644 --- a/core/js/sharedialoglinkshareview.js +++ b/core/js/sharedialoglinkshareview.js @@ -236,7 +236,7 @@ })); // TODO: move this to delegate events instead - this.$el.find('#linkCheckbox').change(this.onLinkCheckBoxChange); + this.$el.find('#linkCheckbox').click(this.onLinkCheckBoxChange); this.$el.find('#sharingDialogAllowPublicUpload').change(this.onAllowPublicUploadChange); this.$el.find('#linkText').click(this.onLinkTextClick); this.$el.find('#showPassword').click(this.onShowPasswordClick); diff --git a/core/js/tests/specs/shareSpec.js b/core/js/tests/specs/shareSpec.js index 5a59a117d77d..9e80f4fe19d6 100644 --- a/core/js/tests/specs/shareSpec.js +++ b/core/js/tests/specs/shareSpec.js @@ -21,1090 +21,6 @@ /* global oc_appconfig */ describe('OC.Share tests', function() { - describe('dropdown', function() { - var $container; - var oldAppConfig; - var loadItemStub; - var autocompleteStub; - var oldEnableAvatars; - var avatarStub; - var placeholderStub; - var oldCurrentUser; - - beforeEach(function() { - $('#testArea').append($('
    ')); - // horrible parameters - $('#testArea').append(''); - $('#testArea').append(''); - $container = $('#shareContainer'); - /* jshint camelcase:false */ - oldAppConfig = _.extend({}, oc_appconfig.core); - oc_appconfig.core.enforcePasswordForPublicLink = false; - - loadItemStub = sinon.stub(OC.Share, 'loadItem'); - loadItemStub.returns({ - reshare: [], - shares: [] - }); - - autocompleteStub = sinon.stub($.fn, 'autocomplete', function() { - // dummy container with the expected attributes - if (!$(this).length) { - // simulate the real autocomplete that returns - // nothing at all when no element is specified - // (and potentially break stuff) - return null; - } - var $el = $('
    ').data('ui-autocomplete', {}); - return $el; - }); - - oldEnableAvatars = oc_config.enable_avatars; - oc_config.enable_avatars = false; - avatarStub = sinon.stub($.fn, 'avatar'); - placeholderStub = sinon.stub($.fn, 'imageplaceholder'); - - oldCurrentUser = OC.currentUser; - OC.currentUser = 'user0'; - }); - afterEach(function() { - OC.currentUser = oldCurrentUser; - /* jshint camelcase:false */ - oc_appconfig.core = oldAppConfig; - loadItemStub.restore(); - - autocompleteStub.restore(); - avatarStub.restore(); - placeholderStub.restore(); - oc_config.enable_avatars = oldEnableAvatars; - $('#dropdown').remove(); - }); - it('calls loadItem with the correct arguments', function() { - OC.Share.showDropDown( - 'file', - 123, - $container, - true, - 31, - 'shared_file_name.txt' - ); - expect(loadItemStub.calledOnce).toEqual(true); - expect(loadItemStub.calledWith('file', 123)).toEqual(true); - }); - it('shows the dropdown with default values', function() { - var $el; - OC.Share.showDropDown( - 'file', - 123, - $container, - true, - 31, - 'shared_file_name.txt' - ); - $el = $container.find('#dropdown'); - expect($el.length).toEqual(1); - expect($el.attr('data-item-type')).toEqual('file'); - expect($el.attr('data-item-source')).toEqual('123'); - // TODO: expect that other parts are rendered correctly - }); - describe('Share with link', function() { - // TODO: test ajax calls - // TODO: test password field visibility (whenever enforced or not) - it('update password on focus out', function() { - $('#allowShareWithLink').val('yes'); - - OC.Share.showDropDown( - 'file', - 123, - $container, - true, - 31, - 'shared_file_name.txt' - ); - - // Toggle linkshare - $('#dropdown [name=linkCheckbox]').click(); - fakeServer.requests[0].respond( - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({data: {token: 'xyz'}, status: 'success'}) - ); - - // Enable password, enter password and focusout - $('#dropdown [name=showPassword]').click(); - $('#dropdown #linkPassText').focus(); - $('#dropdown #linkPassText').val('foo'); - $('#dropdown #linkPassText').focusout(); - - expect(fakeServer.requests[1].method).toEqual('POST'); - var body = OC.parseQueryString(fakeServer.requests[1].requestBody); - expect(body['shareWith']).toEqual('foo'); - - // Set password response - fakeServer.requests[1].respond( - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({data: {token: 'xyz'}, status: 'success'}) - ); - - expect($('#dropdown #linkPassText').val()).toEqual(''); - expect($('#dropdown #linkPassText').attr('placeholder')).toEqual('Password protected'); - }); - it('update password on enter', function() { - $('#allowShareWithLink').val('yes'); - - OC.Share.showDropDown( - 'file', - 123, - $container, - true, - 31, - 'shared_file_name.txt' - ); - - // Toggle linkshare - $('#dropdown [name=linkCheckbox]').click(); - fakeServer.requests[0].respond( - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({data: {token: 'xyz'}, status: 'success'}) - ); - - // Enable password and enter password - $('#dropdown [name=showPassword]').click(); - $('#dropdown #linkPassText').focus(); - $('#dropdown #linkPassText').val('foo'); - $('#dropdown #linkPassText').trigger(new $.Event('keyup', {keyCode: 13})); - - expect(fakeServer.requests[1].method).toEqual('POST'); - var body = OC.parseQueryString(fakeServer.requests[1].requestBody); - expect(body['shareWith']).toEqual('foo'); - - // Set password response - fakeServer.requests[1].respond( - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({data: {token: 'xyz'}, status: 'success'}) - ); - - expect($('#dropdown #linkPassText').val()).toEqual(''); - expect($('#dropdown #linkPassText').attr('placeholder')).toEqual('Password protected'); - }); - it('shows share with link checkbox when allowed', function() { - $('#allowShareWithLink').val('yes'); - OC.Share.showDropDown( - 'file', - 123, - $container, - true, - 31, - 'shared_file_name.txt' - ); - expect($('#dropdown #linkCheckbox').length).toEqual(1); - }); - it('does not show share with link checkbox when not allowed', function() { - $('#allowShareWithLink').val('no'); - OC.Share.showDropDown( - 'file', - 123, - $container, - true, - 31, - 'shared_file_name.txt' - ); - expect($('#dropdown #linkCheckbox').length).toEqual(0); - }); - it('Reset link when password is enforced and link is toggled', function() { - var old = oc_appconfig.core.enforcePasswordForPublicLink; - oc_appconfig.core.enforcePasswordForPublicLink = true; - $('#allowShareWithLink').val('yes'); - - OC.Share.showDropDown( - 'file', - 123, - $container, - true, - 31, - 'shared_file_name.txt' - ); - - // Toggle linkshare - $('#dropdown [name=linkCheckbox]').click(); - expect($('#dropdown #linkText').val()).toEqual(''); - - // Set password - $('#dropdown #linkPassText').val('foo'); - $('#dropdown #linkPassText').trigger(new $.Event('keyup', {keyCode: 13})); - fakeServer.requests[0].respond( - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({data: {token: 'xyz'}, status: 'success'}) - ); - - // Remove link - $('#dropdown [name=linkCheckbox]').click(); - fakeServer.requests[1].respond( - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({status: 'success'}) - ); - - /* - * Try to share again - * The linkText should be emptied - */ - $('#dropdown [name=linkCheckbox]').click(); - expect($('#dropdown #linkText').val()).toEqual(''); - - /* - * Do not set password but untoggle - * Since there is no share this should not result in another request to the server - */ - $('#dropdown [name=linkCheckbox]').click(); - expect(fakeServer.requests.length).toEqual(2); - - oc_appconfig.core.enforcePasswordForPublicLink = old; - }); - - it('Reset password placeholder when password is enforced and link is toggled', function() { - var old = oc_appconfig.core.enforcePasswordForPublicLink; - oc_appconfig.core.enforcePasswordForPublicLink = true; - $('#allowShareWithLink').val('yes'); - - OC.Share.showDropDown( - 'file', - 123, - $container, - true, - 31, - 'shared_file_name.txt' - ); - - // Toggle linkshare - $('#dropdown [name=linkCheckbox]').click(); - expect($('#dropdown #linkPassText').attr('placeholder')).toEqual('Choose a password for the public link'); - - // Set password - $('#dropdown #linkPassText').val('foo'); - $('#dropdown #linkPassText').trigger(new $.Event('keyup', {keyCode: 13})); - fakeServer.requests[0].respond( - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({data: {token: 'xyz'}, status: 'success'}) - ); - expect($('#dropdown #linkPassText').attr('placeholder')).toEqual('**********'); - - // Remove link - $('#dropdown [name=linkCheckbox]').click(); - fakeServer.requests[1].respond( - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({status: 'success'}) - ); - - // Try to share again - $('#dropdown [name=linkCheckbox]').click(); - expect($('#dropdown #linkPassText').attr('placeholder')).toEqual('Choose a password for the public link'); - - oc_appconfig.core.enforcePasswordForPublicLink = old; - }); - it('reset password on toggle of share', function() { - $('#allowShareWithLink').val('yes'); - OC.Share.showDropDown( - 'file', - 123, - $container, - true, - 31, - 'shared_file_name.txt' - ); - $('#dropdown [name=linkCheckbox]').click(); - fakeServer.requests[0].respond( - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({data: {token: 'xyz'}, status: 'success'}) - ); - - //Password protection should be unchecked and password field not visible - expect($('#dropdown [name=showPassword]').prop('checked')).toEqual(false); - expect($('#dropdown #linkPass').is(":visible")).toEqual(false); - - // Toggle and set password - $('#dropdown [name=showPassword]').click(); - $('#dropdown #linkPassText').val('foo'); - $('#dropdown #linkPassText').trigger(new $.Event('keyup', {keyCode: 13})); - fakeServer.requests[1].respond( - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({data: {token: 'xyz2'}, status: 'success'}) - ); - - // Unshare - $('#dropdown [name=linkCheckbox]').click(); - fakeServer.requests[2].respond( - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({status: 'success'}) - ); - - // Toggle share again - $('#dropdown [name=linkCheckbox]').click(); - fakeServer.requests[3].respond( - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({data: {token: 'xyz3'}, status: 'success'}) - ); - - - // Password checkbox should be unchecked - expect($('#dropdown [name=showPassword]').prop('checked')).toEqual(false); - expect($('#dropdown #linkPass').is(":visible")).toEqual(false); - }); - it('reset expiration on toggle of share', function() { - $('#allowShareWithLink').val('yes'); - OC.Share.showDropDown( - 'file', - 123, - $container, - true, - 31, - 'shared_file_name.txt' - ); - $('#dropdown [name=linkCheckbox]').click(); - fakeServer.requests[0].respond( - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({data: {token: 'xyz'}, status: 'success'}) - ); - - //Expiration should be unchecked and expiration field not visible - expect($('#dropdown [name=expirationCheckbox]').prop('checked')).toEqual(false); - expect($('#dropdown #expirationDate').is(":visible")).toEqual(false); - - // Toggle and set password - $('#dropdown [name=expirationCheckbox]').click(); - d = new Date(); - d.setDate(d.getDate() + 1); - date=d.getDate() + '-' + (d.getMonth()+1) + '-' + d.getFullYear(); - $('#dropdown #expirationDate').val(date); - $('#dropdown #expirationDate').change(); - fakeServer.requests[1].respond( - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({data: {token: 'xyz2'}, status: 'success'}) - ); - - // Unshare - $('#dropdown [name=linkCheckbox]').click(); - fakeServer.requests[2].respond( - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({status: 'success'}) - ); - - // Toggle share again - $('#dropdown [name=linkCheckbox]').click(); - fakeServer.requests[3].respond( - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({data: {token: 'xyz3'}, status: 'success'}) - ); - - // Recheck expire visibility - expect($('#dropdown [name=expirationCheckbox]').prop('checked')).toEqual(false); - expect($('#dropdown #expirationDate').is(":visible")).toEqual(false); - }); - it('shows populated link share when a link share exists', function() { - loadItemStub.returns({ - reshare: [], - /* jshint camelcase: false */ - shares: [{ - displayname_owner: 'root', - expiration: null, - file_source: 123, - file_target: '/folder', - id: 20, - item_source: '123', - item_type: 'folder', - mail_send: '0', - parent: null, - path: '/folder', - permissions: OC.PERMISSION_READ, - share_type: OC.Share.SHARE_TYPE_LINK, - share_with: null, - stime: 1403884258, - storage: 1, - token: 'tehtoken', - uid_owner: 'root' - }] - }); - OC.Share.showDropDown( - 'file', - 123, - $container, - true, - 31, - 'folder' - ); - expect($('#dropdown #linkCheckbox').prop('checked')).toEqual(true); - // this is how the OC.Share class does it... - var link = parent.location.protocol + '//' + location.host + - OC.generateUrl('/s/') + 'tehtoken'; - expect($('#dropdown #linkText').val()).toEqual(link); - }); - it('does not show populated link share when a link share exists for a different file', function() { - loadItemStub.returns({ - reshare: [], - /* jshint camelcase: false */ - shares: [{ - displayname_owner: 'root', - expiration: null, - file_source: 123, - file_target: '/folder', - id: 20, - item_source: '123', - item_type: 'folder', - mail_send: '0', - parent: null, - path: '/folder', - permissions: OC.PERMISSION_READ, - share_type: OC.Share.SHARE_TYPE_LINK, - share_with: null, - stime: 1403884258, - storage: 1, - token: 'tehtoken', - uid_owner: 'root' - }] - }); - OC.Share.showDropDown( - 'file', - 456, // another file - $container, - true, - 31, - 'folder' - ); - expect($('#dropdown #linkCheckbox').prop('checked')).toEqual(false); - }); - it('shows correct link share when a nest link share exists along with parent one', function() { - loadItemStub.returns({ - reshare: [], - /* jshint camelcase: false */ - shares: [{ - displayname_owner: 'root', - expiration: null, - file_source: 123, - file_target: '/folder', - id: 20, - item_source: '123', - item_type: 'file', - mail_send: '0', - parent: null, - path: '/folder', - permissions: OC.PERMISSION_READ, - share_type: OC.Share.SHARE_TYPE_LINK, - share_with: null, - stime: 1403884258, - storage: 1, - token: 'tehtoken', - uid_owner: 'root' - }, { - displayname_owner: 'root', - expiration: null, - file_source: 456, - file_target: '/file_in_folder.txt', - id: 21, - item_source: '456', - item_type: 'file', - mail_send: '0', - parent: null, - path: '/folder/file_in_folder.txt', - permissions: OC.PERMISSION_READ, - share_type: OC.Share.SHARE_TYPE_LINK, - share_with: null, - stime: 1403884509, - storage: 1, - token: 'anothertoken', - uid_owner: 'root' - }] - }); - - // parent one - OC.Share.showDropDown( - 'folder', - 123, - $container, - true, - 31, - 'folder' - ); - expect($('#dropdown #linkCheckbox').prop('checked')).toEqual(true); - // this is how the OC.Share class does it... - var link = parent.location.protocol + '//' + location.host + - OC.generateUrl('/s/') + 'tehtoken'; - expect($('#dropdown #linkText').val()).toEqual(link); - - // nested one - OC.Share.showDropDown( - 'file', - 456, - $container, - true, - 31, - 'file_in_folder.txt' - ); - expect($('#dropdown #linkCheckbox').prop('checked')).toEqual(true); - // this is how the OC.Share class does it... - link = parent.location.protocol + '//' + location.host + - OC.generateUrl('/s/') + 'anothertoken'; - expect($('#dropdown #linkText').val()).toEqual(link); - }); - describe('expiration date', function() { - var shareData; - var shareItem; - var clock; - var expectedMinDate; - - function showDropDown() { - OC.Share.showDropDown( - 'file', - 123, - $container, - true, - 31, - 'folder' - ); - } - - beforeEach(function() { - // pick a fake date - clock = sinon.useFakeTimers(new Date(2014, 0, 20, 14, 0, 0).getTime()); - expectedMinDate = new Date(2014, 0, 21, 14, 0, 0); - shareItem = { - displayname_owner: 'root', - expiration: null, - file_source: 123, - file_target: '/folder', - id: 20, - item_source: '123', - item_type: 'folder', - mail_send: '0', - parent: null, - path: '/folder', - permissions: OC.PERMISSION_READ, - share_type: OC.Share.SHARE_TYPE_LINK, - share_with: null, - stime: 1403884258, - storage: 1, - token: 'tehtoken', - uid_owner: 'root' - }; - shareData = { - reshare: [], - shares: [] - }; - loadItemStub.returns(shareData); - oc_appconfig.core.defaultExpireDate = 7; - oc_appconfig.core.enforcePasswordForPublicLink = false; - oc_appconfig.core.defaultExpireDateEnabled = false; - oc_appconfig.core.defaultExpireDateEnforced = false; - }); - afterEach(function() { - clock.restore(); - }); - - it('does not check expiration date checkbox when no date was set', function() { - shareItem.expiration = null; - shareData.shares.push(shareItem); - showDropDown(); - expect($('#dropdown [name=expirationCheckbox]').prop('checked')).toEqual(false); - expect($('#dropdown #expirationDate').val()).toEqual(''); - }); - it('does not check expiration date checkbox for new share', function() { - showDropDown(); - expect($('#dropdown [name=expirationCheckbox]').prop('checked')).toEqual(false); - expect($('#dropdown #expirationDate').val()).toEqual(''); - }); - it('checks expiration date checkbox and populates field when expiration date was set', function() { - shareItem.expiration = 1234; - shareData.shares.push(shareItem); - showDropDown(); - expect($('#dropdown [name=expirationCheckbox]').prop('checked')).toEqual(true); - expect($('#dropdown #expirationDate').val()).toEqual('1234'); - }); - it('sets default date when default date setting is enabled', function() { - /* jshint camelcase:false */ - oc_appconfig.core.defaultExpireDateEnabled = true; - showDropDown(); - $('#dropdown [name=linkCheckbox]').click(); - // enabled by default - expect($('#dropdown [name=expirationCheckbox]').prop('checked')).toEqual(true); - // TODO: those zeros must go... - expect($('#dropdown #expirationDate').val()).toEqual('2014-1-27 00:00:00'); - - // disabling is allowed - $('#dropdown [name=expirationCheckbox]').click(); - expect($('#dropdown [name=expirationCheckbox]').prop('checked')).toEqual(false); - }); - it('enforces default date when enforced date setting is enabled', function() { - /* jshint camelcase:false */ - oc_appconfig.core.defaultExpireDateEnabled = true; - oc_appconfig.core.defaultExpireDateEnforced = true; - showDropDown(); - $('#dropdown [name=linkCheckbox]').click(); - expect($('#dropdown [name=expirationCheckbox]').prop('checked')).toEqual(true); - // TODO: those zeros must go... - expect($('#dropdown #expirationDate').val()).toEqual('2014-1-27 00:00:00'); - - // disabling is not allowed - expect($('#dropdown [name=expirationCheckbox]').prop('disabled')).toEqual(true); - $('#dropdown [name=expirationCheckbox]').click(); - expect($('#dropdown [name=expirationCheckbox]').prop('checked')).toEqual(true); - }); - it('enforces default date when enforced date setting is enabled and password is enforced', function() { - /* jshint camelcase:false */ - oc_appconfig.core.enforcePasswordForPublicLink = true; - oc_appconfig.core.defaultExpireDateEnabled = true; - oc_appconfig.core.defaultExpireDateEnforced = true; - showDropDown(); - $('#dropdown [name=linkCheckbox]').click(); - - //Enter password - $('#dropdown #linkPassText').val('foo'); - $('#dropdown #linkPassText').trigger(new $.Event('keyup', {keyCode: 13})); - fakeServer.requests[0].respond( - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({data: {token: 'xyz'}, status: 'success'}) - ); - - expect($('#dropdown [name=expirationCheckbox]').prop('checked')).toEqual(true); - // TODO: those zeros must go... - expect($('#dropdown #expirationDate').val()).toEqual('2014-1-27 00:00:00'); - - // disabling is not allowed - expect($('#dropdown [name=expirationCheckbox]').prop('disabled')).toEqual(true); - $('#dropdown [name=expirationCheckbox]').click(); - expect($('#dropdown [name=expirationCheckbox]').prop('checked')).toEqual(true); - }); - it('displayes email form when sending emails is enabled', function() { - $('input[name=mailPublicNotificationEnabled]').val('yes'); - showDropDown(); - expect($('#emailPrivateLink').length).toEqual(1); - }); - it('not renders email form when sending emails is disabled', function() { - $('input[name=mailPublicNotificationEnabled]').val('no'); - showDropDown(); - expect($('#emailPrivateLink').length).toEqual(0); - }); - it('sets picker minDate to today and no maxDate by default', function() { - showDropDown(); - $('#dropdown [name=linkCheckbox]').click(); - $('#dropdown [name=expirationCheckbox]').click(); - expect($.datepicker._defaults.minDate).toEqual(expectedMinDate); - expect($.datepicker._defaults.maxDate).toEqual(null); - }); - it('limits the date range to X days after share time when enforced', function() { - /* jshint camelcase:false */ - oc_appconfig.core.defaultExpireDateEnabled = true; - oc_appconfig.core.defaultExpireDateEnforced = true; - showDropDown(); - $('#dropdown [name=linkCheckbox]').click(); - expect($.datepicker._defaults.minDate).toEqual(expectedMinDate); - expect($.datepicker._defaults.maxDate).toEqual(new Date(2014, 0, 27, 0, 0, 0, 0)); - }); - it('limits the date range to X days after share time when enforced, even when redisplayed the next days', function() { - // item exists, was created two days ago - shareItem.expiration = '2014-1-27'; - // share time has time component but must be stripped later - shareItem.stime = new Date(2014, 0, 20, 11, 0, 25).getTime() / 1000; - shareData.shares.push(shareItem); - /* jshint camelcase:false */ - oc_appconfig.core.defaultExpireDateEnabled = true; - oc_appconfig.core.defaultExpireDateEnforced = true; - showDropDown(); - expect($.datepicker._defaults.minDate).toEqual(expectedMinDate); - expect($.datepicker._defaults.maxDate).toEqual(new Date(2014, 0, 27, 0, 0, 0, 0)); - }); - }); - }); - describe('check for avatar', function() { - beforeEach(function() { - loadItemStub.returns({ - reshare: { - share_type: OC.Share.SHARE_TYPE_USER, - uid_owner: 'owner', - displayname_owner: 'Owner', - permissions: 31 - }, - shares: [{ - id: 100, - item_source: 123, - permissions: 31, - share_type: OC.Share.SHARE_TYPE_USER, - share_with: 'user1', - share_with_displayname: 'User One' - },{ - id: 101, - item_source: 123, - permissions: 31, - share_type: OC.Share.SHARE_TYPE_GROUP, - share_with: 'group', - share_with_displayname: 'group' - },{ - id: 102, - item_source: 123, - permissions: 31, - share_type: OC.Share.SHARE_TYPE_REMOTE, - share_with: 'foo@bar.com/baz', - share_with_displayname: 'foo@bar.com/baz' - - }] - }); - }); - - describe('avatars enabled', function() { - beforeEach(function() { - oc_config.enable_avatars = true; - OC.Share.showDropDown( - 'file', - 123, - $container, - true, - 31, - 'shared_file_name.txt' - ); - }); - - afterEach(function() { - oc_config.enable_avatars = false; - }); - - it('test correct function calls', function() { - expect(avatarStub.calledTwice).toEqual(true); - expect(placeholderStub.calledTwice).toEqual(true); - expect($('#shareWithList').children().length).toEqual(3); - expect($('.avatar').length).toEqual(4); - }); - - it('test avatar owner', function() { - var args = avatarStub.getCall(0).args; - expect(args.length).toEqual(2); - expect(args[0]).toEqual('owner'); - }); - - it('test avatar user', function() { - var args = avatarStub.getCall(1).args; - expect(args.length).toEqual(2); - expect(args[0]).toEqual('user1'); - }); - - it('test avatar for groups', function() { - var args = placeholderStub.getCall(0).args; - expect(args.length).toEqual(1); - expect(args[0]).toEqual('group ' + OC.Share.SHARE_TYPE_GROUP); - }); - - it('test avatar for remotes', function() { - var args = placeholderStub.getCall(1).args; - expect(args.length).toEqual(1); - expect(args[0]).toEqual('foo@bar.com/baz ' + OC.Share.SHARE_TYPE_REMOTE); - }); - }); - - describe('avatars disabled', function() { - beforeEach(function() { - OC.Share.showDropDown( - 'file', - 123, - $container, - true, - 31, - 'shared_file_name.txt' - ); - }); - - it('no avatar classes', function() { - expect($('.avatar').length).toEqual(0); - expect(avatarStub.callCount).toEqual(0); - expect(placeholderStub.callCount).toEqual(0); - }); - }); - }); - describe('"sharesChanged" event', function() { - var autocompleteOptions; - var handler; - beforeEach(function() { - handler = sinon.stub(); - loadItemStub.returns({ - reshare: [], - shares: [{ - id: 100, - item_source: 123, - permissions: 31, - share_type: OC.Share.SHARE_TYPE_USER, - share_with: 'user1', - share_with_displayname: 'User One' - }] - }); - OC.Share.showDropDown( - 'file', - 123, - $container, - true, - 31, - 'shared_file_name.txt' - ); - $('#dropdown').on('sharesChanged', handler); - autocompleteOptions = autocompleteStub.getCall(0).args[0]; - }); - afterEach(function() { - autocompleteOptions = null; - handler = null; - }); - it('triggers "sharesChanged" event when adding shares', function() { - // simulate autocomplete selection - autocompleteOptions.select(new $.Event('select'), { - item: { - label: 'User Two', - value: { - shareType: OC.Share.SHARE_TYPE_USER, - shareWith: 'user2' - } - } - }); - fakeServer.requests[0].respond( - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({status: 'success'}) - ); - expect(handler.calledOnce).toEqual(true); - var shares = handler.getCall(0).args[0].shares; - expect(shares).toBeDefined(); - expect(shares[OC.Share.SHARE_TYPE_USER][0].share_with_displayname).toEqual('User One'); - expect(shares[OC.Share.SHARE_TYPE_USER][1].share_with_displayname).toEqual('User Two'); - expect(shares[OC.Share.SHARE_TYPE_GROUP]).not.toBeDefined(); - }); - it('triggers "sharesChanged" event when deleting shares', function() { - $('#dropdown .unshare:eq(0)').click(); - fakeServer.requests[0].respond( - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({status: 'success'}) - ); - expect(handler.calledOnce).toEqual(true); - var shares = handler.getCall(0).args[0].shares; - expect(shares).toBeDefined(); - expect(shares[OC.Share.SHARE_TYPE_USER]).toEqual([]); - expect(shares[OC.Share.SHARE_TYPE_GROUP]).not.toBeDefined(); - }); - it('triggers "sharesChanged" event when toggling link share', function() { - // simulate autocomplete selection - $('#dropdown #linkCheckbox').click(); - fakeServer.requests[0].respond( - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({status: 'success', data: { token: 'abc' }}) - ); - expect(handler.calledOnce).toEqual(true); - var shares = handler.getCall(0).args[0].shares; - expect(shares).toBeDefined(); - expect(shares[OC.Share.SHARE_TYPE_USER][0].share_with_displayname).toEqual('User One'); - expect(shares[OC.Share.SHARE_TYPE_GROUP]).not.toBeDefined(); - - handler.reset(); - - // uncheck checkbox - $('#dropdown #linkCheckbox').click(); - fakeServer.requests[1].respond( - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({status: 'success'}) - ); - - expect(handler.calledOnce).toEqual(true); - shares = handler.getCall(0).args[0].shares; - expect(shares).toBeDefined(); - expect(shares[OC.Share.SHARE_TYPE_USER][0].share_with_displayname).toEqual('User One'); - expect(shares[OC.Share.SHARE_TYPE_GROUP]).not.toBeDefined(); - }); - }); - describe('share permissions', function() { - beforeEach(function() { - oc_appconfig.core.resharingAllowed = true; - }); - - /** - * Tests sharing with the given possible permissions - * - * @param {int} possiblePermissions - * @return {int} permissions sent to the server - */ - function testWithPermissions(possiblePermissions) { - OC.Share.showDropDown( - 'file', - 123, - $container, - true, - possiblePermissions, - 'shared_file_name.txt' - ); - var autocompleteOptions = autocompleteStub.getCall(0).args[0]; - // simulate autocomplete selection - autocompleteOptions.select(new $.Event('select'), { - item: { - label: 'User Two', - value: { - shareType: OC.Share.SHARE_TYPE_USER, - shareWith: 'user2' - } - } - }); - autocompleteStub.reset(); - var requestBody = OC.parseQueryString(_.last(fakeServer.requests).requestBody); - return parseInt(requestBody.permissions, 10); - } - - describe('regular sharing', function() { - it('shares with given permissions with default config', function() { - loadItemStub.returns({ - reshare: [], - shares: [] - }); - expect( - testWithPermissions(OC.PERMISSION_READ | OC.PERMISSION_UPDATE | OC.PERMISSION_SHARE) - ).toEqual(OC.PERMISSION_READ | OC.PERMISSION_UPDATE | OC.PERMISSION_SHARE); - expect( - testWithPermissions(OC.PERMISSION_READ | OC.PERMISSION_SHARE) - ).toEqual(OC.PERMISSION_READ | OC.PERMISSION_SHARE); - }); - it('removes share permission when not allowed', function() { - oc_appconfig.core.resharingAllowed = false; - loadItemStub.returns({ - reshare: [], - shares: [] - }); - expect( - testWithPermissions(OC.PERMISSION_READ | OC.PERMISSION_UPDATE | OC.PERMISSION_SHARE) - ).toEqual(OC.PERMISSION_READ | OC.PERMISSION_UPDATE); - }); - it('automatically adds READ permission even when not specified', function() { - oc_appconfig.core.resharingAllowed = false; - loadItemStub.returns({ - reshare: [], - shares: [] - }); - expect( - testWithPermissions(OC.PERMISSION_UPDATE | OC.PERMISSION_SHARE) - ).toEqual(OC.PERMISSION_READ | OC.PERMISSION_UPDATE | OC.PERMISSION_UPDATE); - }); - it('does not show sharing options when sharing not allowed', function() { - loadItemStub.returns({ - reshare: [], - shares: [] - }); - OC.Share.showDropDown( - 'file', - 123, - $container, - true, - OC.PERMISSION_READ, - 'shared_file_name.txt' - ); - expect($('#dropdown #shareWithList').length).toEqual(0); - }); - }); - describe('resharing', function() { - it('shares with given permissions when original share had all permissions', function() { - loadItemStub.returns({ - reshare: { - permissions: OC.PERMISSION_ALL - }, - shares: [] - }); - expect( - testWithPermissions(OC.PERMISSION_READ | OC.PERMISSION_UPDATE | OC.PERMISSION_SHARE) - ).toEqual(OC.PERMISSION_READ | OC.PERMISSION_UPDATE | OC.PERMISSION_SHARE); - }); - it('reduces reshare permissions to the ones from the original share', function() { - loadItemStub.returns({ - reshare: { - permissions: OC.PERMISSION_READ, - uid_owner: 'user1' - }, - shares: [] - }); - OC.Share.showDropDown( - 'file', - 123, - $container, - true, - OC.PERMISSION_ALL, - 'shared_file_name.txt' - ); - // no resharing allowed - expect($('#dropdown #shareWithList').length).toEqual(0); - }); - it('reduces reshare permissions to possible permissions', function() { - loadItemStub.returns({ - reshare: { - permissions: OC.PERMISSION_ALL, - uid_owner: 'user1' - }, - shares: [] - }); - OC.Share.showDropDown( - 'file', - 123, - $container, - true, - OC.PERMISSION_READ, - 'shared_file_name.txt' - ); - // no resharing allowed - expect($('#dropdown #shareWithList').length).toEqual(0); - }); - it('does not show sharing options when resharing not allowed', function() { - loadItemStub.returns({ - reshare: { - permissions: OC.PERMISSION_READ | OC.PERMISSION_UPDATE | OC.PERMISSION_DELETE, - uid_owner: 'user1' - }, - shares: [] - }); - OC.Share.showDropDown( - 'file', - 123, - $container, - true, - OC.PERMISSION_ALL, - 'shared_file_name.txt' - ); - expect($('#dropdown #shareWithList').length).toEqual(0); - }); - it('allows owner to share their own share when they are also the recipient', function() { - OC.currentUser = 'user1'; - loadItemStub.returns({ - reshare: { - permissions: OC.PERMISSION_READ, - uid_owner: 'user1' - }, - shares: [] - }); - OC.Share.showDropDown( - 'file', - 123, - $container, - true, - OC.PERMISSION_ALL, - 'shared_file_name.txt' - ); - // sharing still allowed - expect($('#dropdown #shareWithList').length).toEqual(1); - }); - }); - }); - }); describe('markFileAsShared', function() { var $file; var tipsyStub; @@ -1316,21 +232,5 @@ describe('OC.Share tests', function() { }); }); }); - describe('OC.Share utils', function() { - it('parseTime should properly parse strings', function() { - - _.each([ - [ '123456', 123456], - [ 123456 , 123456], - ['0123456', 123456], - ['abcdefg', null], - ['0x12345', null], - [ '', null], - ], function(value) { - expect(OC.Share._parseTime(value[0])).toEqual(value[1]); - }); - - }); - }); }); diff --git a/core/js/tests/specs/sharedialogviewSpec.js b/core/js/tests/specs/sharedialogviewSpec.js new file mode 100644 index 000000000000..b50432a034fb --- /dev/null +++ b/core/js/tests/specs/sharedialogviewSpec.js @@ -0,0 +1,911 @@ +/** +* ownCloud +* +* @author Vincent Petry +* @copyright 2015 Vincent Petry +* +* 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 . +* +*/ + +/* 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(''); + $('#testArea').append(''); + $container = $('#shareContainer'); + /* jshint camelcase:false */ + oldAppConfig = _.extend({}, oc_appconfig.core); + oc_appconfig.core.enforcePasswordForPublicLink = false; + + fetchStub = sinon.stub(OC.Share.ShareItemModel.prototype, 'fetch'); + + fileInfoModel = new OCA.Files.FileInfoModel({ + id: 123, + name: 'shared_file_name.txt', + path: '/subdir', + size: 100, + mimetype: 'text/plain', + permissions: 31, + sharePermissions: 31 + }); + + var attributes = { + itemType: fileInfoModel.isDirectory() ? 'folder' : 'file', + itemSource: fileInfoModel.get('id'), + possiblePermissions: 31, + permissions: 31 + }; + configModel = new OC.Share.ShareConfigModel(); + shareModel = new OC.Share.ShareItemModel(attributes, { + configModel: configModel, + fileInfoModel: fileInfoModel + }); + dialog = new OC.Share.ShareDialogView({ + configModel: configModel, + model: shareModel + }); + + // triggers rendering + shareModel.set({ + shares: [], + linkShare: {isLinkShare: false} + }); + + autocompleteStub = sinon.stub($.fn, 'autocomplete', function() { + // dummy container with the expected attributes + if (!$(this).length) { + // simulate the real autocomplete that returns + // nothing at all when no element is specified + // (and potentially break stuff) + return null; + } + var $el = $('
    ').data('ui-autocomplete', {}); + return $el; + }); + + oldEnableAvatars = oc_config.enable_avatars; + oc_config.enable_avatars = false; + avatarStub = sinon.stub($.fn, 'avatar'); + placeholderStub = sinon.stub($.fn, 'imageplaceholder'); + + oldCurrentUser = OC.currentUser; + OC.currentUser = 'user0'; + }); + afterEach(function() { + OC.currentUser = oldCurrentUser; + /* jshint camelcase:false */ + oc_appconfig.core = oldAppConfig; + + fetchStub.restore(); + + autocompleteStub.restore(); + avatarStub.restore(); + placeholderStub.restore(); + oc_config.enable_avatars = oldEnableAvatars; + }); + describe('Share with link', function() { + beforeEach(function() { + configModel.set('enforcePasswordForPublicLink', false); + }); + // TODO: test ajax calls + // TODO: test password field visibility (whenever enforced or not) + it('update password on focus out', function() { + $('#allowShareWithLink').val('yes'); + + dialog.render(); + + // Toggle linkshare + dialog.$el.find('[name=linkCheckbox]').click(); + fakeServer.requests[0].respond( + 200, + { 'Content-Type': 'application/json' }, + JSON.stringify({data: {token: 'xyz'}, status: 'success'}) + ); + + // Enable password, enter password and focusout + dialog.$el.find('[name=showPassword]').click(); + dialog.$el.find('#linkPassText').focus(); + dialog.$el.find('#linkPassText').val('foo'); + dialog.$el.find('#linkPassText').focusout(); + + expect(fakeServer.requests[1].method).toEqual('POST'); + var body = OC.parseQueryString(fakeServer.requests[1].requestBody); + expect(body['shareWith']).toEqual('foo'); + + // Set password response + fakeServer.requests[1].respond( + 200, + { 'Content-Type': 'application/json' }, + JSON.stringify({data: {token: 'xyz'}, status: 'success'}) + ); + + expect(dialog.$el.find('#linkPassText').val()).toEqual(''); + expect(dialog.$el.find('#linkPassText').attr('placeholder')).toEqual('Password protected'); + }); + it('update password on enter', function() { + $('#allowShareWithLink').val('yes'); + + dialog.render(); + + // Toggle linkshare + dialog.$el.find('[name=linkCheckbox]').click(); + fakeServer.requests[0].respond( + 200, + { 'Content-Type': 'application/json' }, + JSON.stringify({data: {token: 'xyz'}, status: 'success'}) + ); + + // Enable password and enter password + dialog.$el.find('[name=showPassword]').click(); + dialog.$el.find('#linkPassText').focus(); + dialog.$el.find('#linkPassText').val('foo'); + dialog.$el.find('#linkPassText').trigger(new $.Event('keyup', {keyCode: 13})); + + expect(fakeServer.requests[1].method).toEqual('POST'); + var body = OC.parseQueryString(fakeServer.requests[1].requestBody); + expect(body['shareWith']).toEqual('foo'); + + // Set password response + fakeServer.requests[1].respond( + 200, + { 'Content-Type': 'application/json' }, + JSON.stringify({data: {token: 'xyz'}, status: 'success'}) + ); + + expect(dialog.$el.find('#linkPassText').val()).toEqual(''); + expect(dialog.$el.find('#linkPassText').attr('placeholder')).toEqual('Password protected'); + }); + it('shows share with link checkbox when allowed', function() { + $('#allowShareWithLink').val('yes'); + + dialog.render(); + + expect(dialog.$el.find('#linkCheckbox').length).toEqual(1); + }); + it('does not show share with link checkbox when not allowed', function() { + $('#allowShareWithLink').val('no'); + + dialog.render(); + + expect(dialog.$el.find('#linkCheckbox').length).toEqual(0); + }); + it('Reset link when password is enforced and link is toggled', function() { + var old = oc_appconfig.core.enforcePasswordForPublicLink; + oc_appconfig.core.enforcePasswordForPublicLink = true; + $('#allowShareWithLink').val('yes'); + + dialog.render(); + + // Toggle linkshare + dialog.$el.find('[name=linkCheckbox]').click(); + expect(dialog.$el.find('#linkText').val()).toEqual(''); + + // Set password + dialog.$el.find('#linkPassText').val('foo'); + dialog.$el.find('#linkPassText').trigger(new $.Event('keyup', {keyCode: 13})); + fakeServer.requests[0].respond( + 200, + { 'Content-Type': 'application/json' }, + JSON.stringify({data: {token: 'xyz'}, status: 'success'}) + ); + + // Remove link + dialog.$el.find('[name=linkCheckbox]').click(); + fakeServer.requests[1].respond( + 200, + { 'Content-Type': 'application/json' }, + JSON.stringify({status: 'success'}) + ); + + /* + * Try to share again + * The linkText should be emptied + */ + dialog.$el.find('[name=linkCheckbox]').click(); + expect(dialog.$el.find('#linkText').val()).toEqual(''); + + /* + * Do not set password but untoggle + * Since there is no share this should not result in another request to the server + */ + dialog.$el.find('[name=linkCheckbox]').click(); + expect(fakeServer.requests.length).toEqual(2); + + oc_appconfig.core.enforcePasswordForPublicLink = old; + }); + + it('Reset password placeholder when password is enforced and link is toggled', function() { + var old = oc_appconfig.core.enforcePasswordForPublicLink; + oc_appconfig.core.enforcePasswordForPublicLink = true; + $('#allowShareWithLink').val('yes'); + + dialog.render(); + + // Toggle linkshare + dialog.$el.find('[name=linkCheckbox]').click(); + expect(dialog.$el.find('#linkPassText').attr('placeholder')).toEqual('Choose a password for the public link'); + + // Set password + dialog.$el.find('#linkPassText').val('foo'); + dialog.$el.find('#linkPassText').trigger(new $.Event('keyup', {keyCode: 13})); + fakeServer.requests[0].respond( + 200, + { 'Content-Type': 'application/json' }, + JSON.stringify({data: {token: 'xyz'}, status: 'success'}) + ); + expect(dialog.$el.find('#linkPassText').attr('placeholder')).toEqual('**********'); + + // Remove link + dialog.$el.find('[name=linkCheckbox]').click(); + fakeServer.requests[1].respond( + 200, + { 'Content-Type': 'application/json' }, + JSON.stringify({status: 'success'}) + ); + + // Try to share again + dialog.$el.find('[name=linkCheckbox]').click(); + expect(dialog.$el.find('#linkPassText').attr('placeholder')).toEqual('Choose a password for the public link'); + + oc_appconfig.core.enforcePasswordForPublicLink = old; + }); + it('reset password on toggle of share', function() { + $('#allowShareWithLink').val('yes'); + + dialog.render(); + + dialog.$el.find('[name=linkCheckbox]').click(); + fakeServer.requests[0].respond( + 200, + { 'Content-Type': 'application/json' }, + JSON.stringify({data: {token: 'xyz'}, status: 'success'}) + ); + + //Password protection should be unchecked and password field not visible + expect(dialog.$el.find('[name=showPassword]').prop('checked')).toEqual(false); + expect(dialog.$el.find('#linkPass').is(":visible")).toEqual(false); + + // Toggle and set password + dialog.$el.find('[name=showPassword]').click(); + dialog.$el.find('#linkPassText').val('foo'); + dialog.$el.find('#linkPassText').trigger(new $.Event('keyup', {keyCode: 13})); + fakeServer.requests[1].respond( + 200, + { 'Content-Type': 'application/json' }, + JSON.stringify({data: {token: 'xyz2'}, status: 'success'}) + ); + + // Unshare + dialog.$el.find('[name=linkCheckbox]').click(); + fakeServer.requests[2].respond( + 200, + { 'Content-Type': 'application/json' }, + JSON.stringify({status: 'success'}) + ); + + // Toggle share again + dialog.$el.find('[name=linkCheckbox]').click(); + fakeServer.requests[3].respond( + 200, + { 'Content-Type': 'application/json' }, + JSON.stringify({data: {token: 'xyz3'}, status: 'success'}) + ); + + + // Password checkbox should be unchecked + expect(dialog.$el.find('[name=showPassword]').prop('checked')).toEqual(false); + expect(dialog.$el.find('#linkPass').is(":visible")).toEqual(false); + }); + it('reset expiration on toggle of share', function() { + $('#allowShareWithLink').val('yes'); + + dialog.render(); + + dialog.$el.find('[name=linkCheckbox]').click(); + fakeServer.requests[0].respond( + 200, + { 'Content-Type': 'application/json' }, + JSON.stringify({data: {token: 'xyz'}, status: 'success'}) + ); + + //Expiration should be unchecked and expiration field not visible + expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(false); + expect(dialog.$el.find('#expirationDate').is(":visible")).toEqual(false); + + // Toggle and set password + dialog.$el.find('[name=expirationCheckbox]').click(); + d = new Date(); + d.setDate(d.getDate() + 1); + date=d.getDate() + '-' + (d.getMonth()+1) + '-' + d.getFullYear(); + dialog.$el.find('#expirationDate').val(date); + dialog.$el.find('#expirationDate').change(); + fakeServer.requests[1].respond( + 200, + { 'Content-Type': 'application/json' }, + JSON.stringify({data: {token: 'xyz2'}, status: 'success'}) + ); + + // Unshare + dialog.$el.find('[name=linkCheckbox]').click(); + fakeServer.requests[2].respond( + 200, + { 'Content-Type': 'application/json' }, + JSON.stringify({status: 'success'}) + ); + + // Toggle share again + dialog.$el.find('[name=linkCheckbox]').click(); + fakeServer.requests[3].respond( + 200, + { 'Content-Type': 'application/json' }, + JSON.stringify({data: {token: 'xyz3'}, status: 'success'}) + ); + + // Recheck expire visibility + expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(false); + expect(dialog.$el.find('#expirationDate').is(":visible")).toEqual(false); + }); + it('shows populated link share when a link share exists', function() { + shareModel.set('linkShare', { + isLinkShare: true, + token: 'tehtoken', + link: 'TODO', + expiration: '', + permissions: OC.PERMISSION_READ, + stime: 1403884258, + }); + + expect(dialog.$el.find('#linkCheckbox').prop('checked')).toEqual(true); + // this is how the OC.Share class does it... + var link = parent.location.protocol + '//' + location.host + + OC.generateUrl('/s/') + 'tehtoken'; + expect(dialog.$el.find('#linkText').val()).toEqual(link); + }); + describe('expiration date', function() { + var shareData; + var shareItem; + var clock; + var expectedMinDate; + + beforeEach(function() { + // pick a fake date + clock = sinon.useFakeTimers(new Date(2014, 0, 20, 14, 0, 0).getTime()); + expectedMinDate = new Date(2014, 0, 21, 14, 0, 0); + + oc_appconfig.core.defaultExpireDate = 7; + oc_appconfig.core.enforcePasswordForPublicLink = false; + oc_appconfig.core.defaultExpireDateEnabled = false; + oc_appconfig.core.defaultExpireDateEnforced = false; + + shareModel.set('linkShare', { + isLinkShare: true, + token: 'tehtoken', + permissions: OC.PERMISSION_READ, + expiration: null + }); + }); + afterEach(function() { + clock.restore(); + }); + + it('does not check expiration date checkbox when no date was set', function() { + shareModel.get('linkShare').expiration = null; + dialog.render(); + + expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(false); + expect(dialog.$el.find('#expirationDate').val()).toEqual(''); + }); + it('does not check expiration date checkbox for new share', function() { + dialog.render(); + + expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(false); + expect(dialog.$el.find('#expirationDate').val()).toEqual(''); + }); + it('checks expiration date checkbox and populates field when expiration date was set', function() { + shareModel.get('linkShare').expiration = 1234; + dialog.render(); + expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(true); + expect(dialog.$el.find('#expirationDate').val()).toEqual('1234'); + }); + it('sets default date when default date setting is enabled', function() { + /* jshint camelcase:false */ + oc_appconfig.core.defaultExpireDateEnabled = true; + dialog.render(); + dialog.$el.find('[name=linkCheckbox]').click(); + // enabled by default + expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(true); + // TODO: those zeros must go... + expect(dialog.$el.find('#expirationDate').val()).toEqual('2014-1-27 00:00:00'); + + // disabling is allowed + dialog.$el.find('[name=expirationCheckbox]').click(); + expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(false); + }); + it('enforces default date when enforced date setting is enabled', function() { + /* jshint camelcase:false */ + oc_appconfig.core.defaultExpireDateEnabled = true; + oc_appconfig.core.defaultExpireDateEnforced = true; + dialog.render(); + dialog.$el.find('[name=linkCheckbox]').click(); + expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(true); + // TODO: those zeros must go... + expect(dialog.$el.find('#expirationDate').val()).toEqual('2014-1-27 00:00:00'); + + // disabling is not allowed + expect(dialog.$el.find('[name=expirationCheckbox]').prop('disabled')).toEqual(true); + dialog.$el.find('[name=expirationCheckbox]').click(); + expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(true); + }); + it('enforces default date when enforced date setting is enabled and password is enforced', function() { + /* jshint camelcase:false */ + oc_appconfig.core.enforcePasswordForPublicLink = true; + oc_appconfig.core.defaultExpireDateEnabled = true; + oc_appconfig.core.defaultExpireDateEnforced = true; + dialog.render(); + dialog.$el.find('[name=linkCheckbox]').click(); + + //Enter password + dialog.$el.find('#linkPassText').val('foo'); + dialog.$el.find('#linkPassText').trigger(new $.Event('keyup', {keyCode: 13})); + fakeServer.requests[0].respond( + 200, + { 'Content-Type': 'application/json' }, + JSON.stringify({data: {token: 'xyz'}, status: 'success'}) + ); + + expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(true); + // TODO: those zeros must go... + expect(dialog.$el.find('#expirationDate').val()).toEqual('2014-1-27 00:00:00'); + + // disabling is not allowed + expect(dialog.$el.find('[name=expirationCheckbox]').prop('disabled')).toEqual(true); + dialog.$el.find('[name=expirationCheckbox]').click(); + expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(true); + }); + it('displayes email form when sending emails is enabled', function() { + $('input[name=mailPublicNotificationEnabled]').val('yes'); + dialog.render(); + expect($('#emailPrivateLink').length).toEqual(1); + }); + it('not renders email form when sending emails is disabled', function() { + $('input[name=mailPublicNotificationEnabled]').val('no'); + dialog.render(); + expect($('#emailPrivateLink').length).toEqual(0); + }); + it('sets picker minDate to today and no maxDate by default', function() { + dialog.render(); + dialog.$el.find('[name=linkCheckbox]').click(); + dialog.$el.find('[name=expirationCheckbox]').click(); + expect($.datepicker._defaults.minDate).toEqual(expectedMinDate); + expect($.datepicker._defaults.maxDate).toEqual(null); + }); + it('limits the date range to X days after share time when enforced', function() { + /* jshint camelcase:false */ + oc_appconfig.core.defaultExpireDateEnabled = true; + oc_appconfig.core.defaultExpireDateEnforced = true; + dialog.render(); + dialog.$el.find('[name=linkCheckbox]').click(); + expect($.datepicker._defaults.minDate).toEqual(expectedMinDate); + expect($.datepicker._defaults.maxDate).toEqual(new Date(2014, 0, 27, 0, 0, 0, 0)); + }); + it('limits the date range to X days after share time when enforced, even when redisplayed the next days', function() { + // item exists, was created two days ago + shareItem.expiration = '2014-1-27'; + // share time has time component but must be stripped later + shareItem.stime = new Date(2014, 0, 20, 11, 0, 25).getTime() / 1000; + shareData.shares.push(shareItem); + /* jshint camelcase:false */ + oc_appconfig.core.defaultExpireDateEnabled = true; + oc_appconfig.core.defaultExpireDateEnforced = true; + dialog.render(); + expect($.datepicker._defaults.minDate).toEqual(expectedMinDate); + expect($.datepicker._defaults.maxDate).toEqual(new Date(2014, 0, 27, 0, 0, 0, 0)); + }); + }); + }); + describe('check for avatar', function() { + beforeEach(function() { + loadItemStub.returns({ + reshare: { + share_type: OC.Share.SHARE_TYPE_USER, + uid_owner: 'owner', + displayname_owner: 'Owner', + permissions: 31 + }, + shares: [{ + id: 100, + item_source: 123, + permissions: 31, + share_type: OC.Share.SHARE_TYPE_USER, + share_with: 'user1', + share_with_displayname: 'User One' + },{ + id: 101, + item_source: 123, + permissions: 31, + share_type: OC.Share.SHARE_TYPE_GROUP, + share_with: 'group', + share_with_displayname: 'group' + },{ + id: 102, + item_source: 123, + permissions: 31, + share_type: OC.Share.SHARE_TYPE_REMOTE, + share_with: 'foo@bar.com/baz', + share_with_displayname: 'foo@bar.com/baz' + + }] + }); + }); + + describe('avatars enabled', function() { + beforeEach(function() { + oc_config.enable_avatars = true; + OC.Share.showDropDown( + 'file', + 123, + $container, + true, + 31, + 'shared_file_name.txt' + ); + }); + + afterEach(function() { + oc_config.enable_avatars = false; + }); + + it('test correct function calls', function() { + expect(avatarStub.calledTwice).toEqual(true); + expect(placeholderStub.calledTwice).toEqual(true); + expect($('#shareWithList').children().length).toEqual(3); + expect($('.avatar').length).toEqual(4); + }); + + it('test avatar owner', function() { + var args = avatarStub.getCall(0).args; + expect(args.length).toEqual(2); + expect(args[0]).toEqual('owner'); + }); + + it('test avatar user', function() { + var args = avatarStub.getCall(1).args; + expect(args.length).toEqual(2); + expect(args[0]).toEqual('user1'); + }); + + it('test avatar for groups', function() { + var args = placeholderStub.getCall(0).args; + expect(args.length).toEqual(1); + expect(args[0]).toEqual('group ' + OC.Share.SHARE_TYPE_GROUP); + }); + + it('test avatar for remotes', function() { + var args = placeholderStub.getCall(1).args; + expect(args.length).toEqual(1); + expect(args[0]).toEqual('foo@bar.com/baz ' + OC.Share.SHARE_TYPE_REMOTE); + }); + }); + + describe('avatars disabled', function() { + beforeEach(function() { + OC.Share.showDropDown( + 'file', + 123, + $container, + true, + 31, + 'shared_file_name.txt' + ); + }); + + it('no avatar classes', function() { + expect($('.avatar').length).toEqual(0); + expect(avatarStub.callCount).toEqual(0); + expect(placeholderStub.callCount).toEqual(0); + }); + }); + }); + describe('"sharesChanged" event', function() { + var autocompleteOptions; + var handler; + beforeEach(function() { + handler = sinon.stub(); + loadItemStub.returns({ + reshare: [], + shares: [{ + id: 100, + item_source: 123, + permissions: 31, + share_type: OC.Share.SHARE_TYPE_USER, + share_with: 'user1', + share_with_displayname: 'User One' + }] + }); + OC.Share.showDropDown( + 'file', + 123, + $container, + true, + 31, + 'shared_file_name.txt' + ); + $('#dropdown').on('sharesChanged', handler); + autocompleteOptions = autocompleteStub.getCall(0).args[0]; + }); + afterEach(function() { + autocompleteOptions = null; + handler = null; + }); + it('triggers "sharesChanged" event when adding shares', function() { + // simulate autocomplete selection + autocompleteOptions.select(new $.Event('select'), { + item: { + label: 'User Two', + value: { + shareType: OC.Share.SHARE_TYPE_USER, + shareWith: 'user2' + } + } + }); + fakeServer.requests[0].respond( + 200, + { 'Content-Type': 'application/json' }, + JSON.stringify({status: 'success'}) + ); + expect(handler.calledOnce).toEqual(true); + var shares = handler.getCall(0).args[0].shares; + expect(shares).toBeDefined(); + expect(shares[OC.Share.SHARE_TYPE_USER][0].share_with_displayname).toEqual('User One'); + expect(shares[OC.Share.SHARE_TYPE_USER][1].share_with_displayname).toEqual('User Two'); + expect(shares[OC.Share.SHARE_TYPE_GROUP]).not.toBeDefined(); + }); + it('triggers "sharesChanged" event when deleting shares', function() { + dialog.$el.find('.unshare:eq(0)').click(); + fakeServer.requests[0].respond( + 200, + { 'Content-Type': 'application/json' }, + JSON.stringify({status: 'success'}) + ); + expect(handler.calledOnce).toEqual(true); + var shares = handler.getCall(0).args[0].shares; + expect(shares).toBeDefined(); + expect(shares[OC.Share.SHARE_TYPE_USER]).toEqual([]); + expect(shares[OC.Share.SHARE_TYPE_GROUP]).not.toBeDefined(); + }); + it('triggers "sharesChanged" event when toggling link share', function() { + // simulate autocomplete selection + dialog.$el.find('#linkCheckbox').click(); + fakeServer.requests[0].respond( + 200, + { 'Content-Type': 'application/json' }, + JSON.stringify({status: 'success', data: { token: 'abc' }}) + ); + expect(handler.calledOnce).toEqual(true); + var shares = handler.getCall(0).args[0].shares; + expect(shares).toBeDefined(); + expect(shares[OC.Share.SHARE_TYPE_USER][0].share_with_displayname).toEqual('User One'); + expect(shares[OC.Share.SHARE_TYPE_GROUP]).not.toBeDefined(); + + handler.reset(); + + // uncheck checkbox + dialog.$el.find('#linkCheckbox').click(); + fakeServer.requests[1].respond( + 200, + { 'Content-Type': 'application/json' }, + JSON.stringify({status: 'success'}) + ); + + expect(handler.calledOnce).toEqual(true); + shares = handler.getCall(0).args[0].shares; + expect(shares).toBeDefined(); + expect(shares[OC.Share.SHARE_TYPE_USER][0].share_with_displayname).toEqual('User One'); + expect(shares[OC.Share.SHARE_TYPE_GROUP]).not.toBeDefined(); + }); + }); + describe('share permissions', function() { + beforeEach(function() { + oc_appconfig.core.resharingAllowed = true; + }); + + /** + * Tests sharing with the given possible permissions + * + * @param {int} possiblePermissions + * @return {int} permissions sent to the server + */ + function testWithPermissions(possiblePermissions) { + OC.Share.showDropDown( + 'file', + 123, + $container, + true, + possiblePermissions, + 'shared_file_name.txt' + ); + var autocompleteOptions = autocompleteStub.getCall(0).args[0]; + // simulate autocomplete selection + autocompleteOptions.select(new $.Event('select'), { + item: { + label: 'User Two', + value: { + shareType: OC.Share.SHARE_TYPE_USER, + shareWith: 'user2' + } + } + }); + autocompleteStub.reset(); + var requestBody = OC.parseQueryString(_.last(fakeServer.requests).requestBody); + return parseInt(requestBody.permissions, 10); + } + + describe('regular sharing', function() { + it('shares with given permissions with default config', function() { + loadItemStub.returns({ + reshare: [], + shares: [] + }); + expect( + testWithPermissions(OC.PERMISSION_READ | OC.PERMISSION_UPDATE | OC.PERMISSION_SHARE) + ).toEqual(OC.PERMISSION_READ | OC.PERMISSION_UPDATE | OC.PERMISSION_SHARE); + expect( + testWithPermissions(OC.PERMISSION_READ | OC.PERMISSION_SHARE) + ).toEqual(OC.PERMISSION_READ | OC.PERMISSION_SHARE); + }); + it('removes share permission when not allowed', function() { + oc_appconfig.core.resharingAllowed = false; + loadItemStub.returns({ + reshare: [], + shares: [] + }); + expect( + testWithPermissions(OC.PERMISSION_READ | OC.PERMISSION_UPDATE | OC.PERMISSION_SHARE) + ).toEqual(OC.PERMISSION_READ | OC.PERMISSION_UPDATE); + }); + it('automatically adds READ permission even when not specified', function() { + oc_appconfig.core.resharingAllowed = false; + loadItemStub.returns({ + reshare: [], + shares: [] + }); + expect( + testWithPermissions(OC.PERMISSION_UPDATE | OC.PERMISSION_SHARE) + ).toEqual(OC.PERMISSION_READ | OC.PERMISSION_UPDATE | OC.PERMISSION_UPDATE); + }); + it('does not show sharing options when sharing not allowed', function() { + loadItemStub.returns({ + reshare: [], + shares: [] + }); + OC.Share.showDropDown( + 'file', + 123, + $container, + true, + OC.PERMISSION_READ, + 'shared_file_name.txt' + ); + expect(dialog.$el.find('#shareWithList').length).toEqual(0); + }); + }); + describe('resharing', function() { + it('shares with given permissions when original share had all permissions', function() { + loadItemStub.returns({ + reshare: { + permissions: OC.PERMISSION_ALL + }, + shares: [] + }); + expect( + testWithPermissions(OC.PERMISSION_READ | OC.PERMISSION_UPDATE | OC.PERMISSION_SHARE) + ).toEqual(OC.PERMISSION_READ | OC.PERMISSION_UPDATE | OC.PERMISSION_SHARE); + }); + it('reduces reshare permissions to the ones from the original share', function() { + loadItemStub.returns({ + reshare: { + permissions: OC.PERMISSION_READ, + uid_owner: 'user1' + }, + shares: [] + }); + OC.Share.showDropDown( + 'file', + 123, + $container, + true, + OC.PERMISSION_ALL, + 'shared_file_name.txt' + ); + // no resharing allowed + expect(dialog.$el.find('#shareWithList').length).toEqual(0); + }); + it('reduces reshare permissions to possible permissions', function() { + loadItemStub.returns({ + reshare: { + permissions: OC.PERMISSION_ALL, + uid_owner: 'user1' + }, + shares: [] + }); + OC.Share.showDropDown( + 'file', + 123, + $container, + true, + OC.PERMISSION_READ, + 'shared_file_name.txt' + ); + // no resharing allowed + expect(dialog.$el.find('#shareWithList').length).toEqual(0); + }); + it('does not show sharing options when resharing not allowed', function() { + loadItemStub.returns({ + reshare: { + permissions: OC.PERMISSION_READ | OC.PERMISSION_UPDATE | OC.PERMISSION_DELETE, + uid_owner: 'user1' + }, + shares: [] + }); + OC.Share.showDropDown( + 'file', + 123, + $container, + true, + OC.PERMISSION_ALL, + 'shared_file_name.txt' + ); + expect(dialog.$el.find('#shareWithList').length).toEqual(0); + }); + it('allows owner to share their own share when they are also the recipient', function() { + OC.currentUser = 'user1'; + loadItemStub.returns({ + reshare: { + permissions: OC.PERMISSION_READ, + uid_owner: 'user1' + }, + shares: [] + }); + OC.Share.showDropDown( + 'file', + 123, + $container, + true, + OC.PERMISSION_ALL, + 'shared_file_name.txt' + ); + // sharing still allowed + expect(dialog.$el.find('#shareWithList').length).toEqual(1); + }); + }); + }); +}); + diff --git a/core/js/tests/specs/shareitemmodelSpec.js b/core/js/tests/specs/shareitemmodelSpec.js new file mode 100644 index 000000000000..d3c4b7d4004b --- /dev/null +++ b/core/js/tests/specs/shareitemmodelSpec.js @@ -0,0 +1,234 @@ +/** +* ownCloud +* +* @author Vincent Petry +* @copyright 2015 Vincent Petry +* +* 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 . +* +*/ + +/* global oc_appconfig */ +describe('OC.Share.ShareItemModel', function() { + var loadItemStub; + var fileInfoModel, configModel, model; + + beforeEach(function() { + loadItemStub = sinon.stub(OC.Share, 'loadItem'); + loadItemStub.returns({ + reshare: [], + shares: [] + }); + + fileInfoModel = new OCA.Files.FileInfoModel({ + id: 123, + name: 'shared_file_name.txt', + path: '/subdir', + size: 100, + mimetype: 'text/plain', + permissions: 31, + sharePermissions: 31 + }); + + var attributes = { + itemType: fileInfoModel.isDirectory() ? 'folder' : 'file', + itemSource: fileInfoModel.get('id'), + possiblePermissions: fileInfoModel.get('sharePermissions') + }; + configModel = new OC.Share.ShareConfigModel(); + model = new OC.Share.ShareItemModel(attributes, { + configModel: configModel, + fileInfoModel: fileInfoModel + }); + }); + afterEach(function() { + loadItemStub.restore(); + }); + + describe('Fetching and parsing', function() { + it('fetching calls loadItem with the correct arguments', function() { + model.fetch(); + + expect(loadItemStub.calledOnce).toEqual(true); + expect(loadItemStub.calledWith('file', 123)).toEqual(true); + }); + it('populates attributes with parsed response', function() { + loadItemStub.returns({ + /* jshint camelcase: false */ + reshare: { + share_type: OC.Share.SHARE_TYPE_USER, + uid_owner: 'owner', + displayname_owner: 'Owner', + permissions: 31 + }, + shares: [{ + id: 100, + item_source: 123, + permissions: 31, + share_type: OC.Share.SHARE_TYPE_USER, + share_with: 'user1', + share_with_displayname: 'User One' + }, { + id: 101, + item_source: 123, + permissions: 31, + share_type: OC.Share.SHARE_TYPE_GROUP, + share_with: 'group', + share_with_displayname: 'group' + }, { + id: 102, + item_source: 123, + permissions: 31, + share_type: OC.Share.SHARE_TYPE_REMOTE, + share_with: 'foo@bar.com/baz', + share_with_displayname: 'foo@bar.com/baz' + + }, { + displayname_owner: 'root', + expiration: null, + file_source: 123, + file_target: '/folder', + id: 20, + item_source: '123', + item_type: 'folder', + mail_send: '0', + parent: null, + path: '/folder', + permissions: OC.PERMISSION_READ, + share_type: OC.Share.SHARE_TYPE_LINK, + share_with: null, + stime: 1403884258, + storage: 1, + token: 'tehtoken', + uid_owner: 'root' + }] + }); + model.fetch(); + + var shares = model.get('shares'); + expect(shares.length).toEqual(3); + expect(shares.file_source).toEqual(123); + + var linkShare = model.get('linkShare'); + expect(linkShare.isLinkShare).toEqual(true); + + // TODO: check more attributes + }); + it('does not parse link share when for a different file', function() { + loadItemStub.returns({ + reshare: [], + /* jshint camelcase: false */ + shares: [{ + displayname_owner: 'root', + expiration: null, + file_source: 456, + file_target: '/folder', + id: 20, + item_source: '456', + item_type: 'folder', + mail_send: '0', + parent: null, + path: '/folder', + permissions: OC.PERMISSION_READ, + share_type: OC.Share.SHARE_TYPE_LINK, + share_with: null, + stime: 1403884258, + storage: 1, + token: 'tehtoken', + uid_owner: 'root' + }] + }); + + model.fetch(); + + var shares = model.get('shares'); + expect(shares.length).toEqual(0); + + var linkShare = model.get('linkShare'); + expect(linkShare.isLinkShare).toEqual(false); + }); + it('parsess correct link share when a nested link share exists along with parent one', function() { + loadItemStub.returns({ + reshare: [], + /* jshint camelcase: false */ + shares: [{ + displayname_owner: 'root', + expiration: 1111, + file_source: 123, + file_target: '/folder', + id: 20, + item_source: '123', + item_type: 'file', + mail_send: '0', + parent: null, + path: '/folder', + permissions: OC.PERMISSION_READ, + share_type: OC.Share.SHARE_TYPE_LINK, + share_with: null, + stime: 1403884258, + storage: 1, + token: 'tehtoken', + uid_owner: 'root' + }, { + displayname_owner: 'root', + expiration: 2222, + file_source: 456, + file_target: '/file_in_folder.txt', + id: 21, + item_source: '456', + item_type: 'file', + mail_send: '0', + parent: null, + path: '/folder/file_in_folder.txt', + permissions: OC.PERMISSION_READ, + share_type: OC.Share.SHARE_TYPE_LINK, + share_with: null, + stime: 1403884509, + storage: 1, + token: 'anothertoken', + uid_owner: 'root' + }] + }); + + model.fetch(); + + var shares = model.get('shares'); + expect(shares.length).toEqual(0); + + var linkShare = model.get('linkShare'); + expect(linkShare.isLinkShare).toEqual(false); + expect(linkShare.token).toEqual('tehtoken'); + + // TODO: check child too + }); + }); + + describe('Util', function() { + it('parseTime should properly parse strings', function() { + + _.each([ + [ '123456', 123456], + [ 123456 , 123456], + ['0123456', 123456], + ['abcdefg', null], + ['0x12345', null], + [ '', null], + ], function(value) { + expect(OC.Share._parseTime(value[0])).toEqual(value[1]); + }); + + }); + }); +}); + diff --git a/tests/karma.config.js b/tests/karma.config.js index d3280b2939ac..64a94ef230bc 100644 --- a/tests/karma.config.js +++ b/tests/karma.config.js @@ -55,7 +55,8 @@ module.exports = function(config) { 'apps/files_sharing/js/sharedfilelist.js', 'apps/files_sharing/js/share.js', 'apps/files_sharing/js/external.js', - 'apps/files_sharing/js/public.js' + 'apps/files_sharing/js/public.js', + 'apps/files_sharing/js/sharetabview.js' ], testFiles: ['apps/files_sharing/tests/js/*.js'] }, From 996639f4fbd4107851efc11ee34b24cead43e790 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Tue, 15 Sep 2015 15:29:30 +0200 Subject: [PATCH 54/59] More unit tests for share dialog --- apps/files_sharing/js/sharetabview.js | 9 +- core/js/sharedialogexpirationview.js | 23 +- core/js/sharedialogresharerinfoview.js | 4 +- core/js/sharedialogshareelistview.js | 11 +- core/js/sharedialogview.js | 24 +- core/js/tests/specs/sharedialogviewSpec.js | 467 +++++---------------- core/js/tests/specs/shareitemmodelSpec.js | 73 +++- 7 files changed, 203 insertions(+), 408 deletions(-) diff --git a/apps/files_sharing/js/sharetabview.js b/apps/files_sharing/js/sharetabview.js index 68288500ae0d..e24320604fb1 100644 --- a/apps/files_sharing/js/sharetabview.js +++ b/apps/files_sharing/js/sharetabview.js @@ -11,7 +11,6 @@ (function() { var TEMPLATE = '
    ' + - '
      {{#if owner}}
    • Owner: {{owner}}
    • {{/if}}
    ' + '
    ' + '
    '; @@ -47,13 +46,7 @@ } if (this.model) { - var owner = this.model.get('shareOwner'); - if (owner === OC.currentUser) { - owner = null; - } - this.$el.html(this.template({ - owner: owner - })); + this.$el.html(this.template()); // TODO: the model should read these directly off the passed fileInfoModel var attributes = { diff --git a/core/js/sharedialogexpirationview.js b/core/js/sharedialogexpirationview.js index c5d8c8296571..3fba4b135d99 100644 --- a/core/js/sharedialogexpirationview.js +++ b/core/js/sharedialogexpirationview.js @@ -130,7 +130,7 @@ ); } - var isExpirationSet = !!this.model.get('linkShare').expiration; + var isExpirationSet = !!this.model.get('linkShare').expiration || isExpirationEnforced; var expirationTemplate = this.template(); this.$el.html(expirationTemplate({ @@ -145,13 +145,13 @@ expirationValue: this.model.get('linkShare').expiration })); - if(isExpirationSet) { - // what if there is another date picker on that page? - var minDate = new Date(); - // min date should always be the next day - minDate.setDate(minDate.getDate()+1); + // 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); - var maxDate = null; + if(isExpirationSet) { if(isExpirationEnforced) { // TODO: hack: backend returns string instead of integer var shareTime = this.model.get('linkShare').stime; @@ -164,12 +164,11 @@ shareTime = OC.Util.stripTime(shareTime).getTime(); maxDate = new Date(shareTime + defaultExpireDays * 24 * 3600 * 1000); } - - $.datepicker.setDefaults({ - minDate: minDate, - maxDate: maxDate - }); } + $.datepicker.setDefaults({ + minDate: minDate, + maxDate: maxDate + }); this.$el.find('.datepicker').datepicker({dateFormat : 'dd-mm-yy'}); diff --git a/core/js/sharedialogresharerinfoview.js b/core/js/sharedialogresharerinfoview.js index f6b927c08c52..600e2ecbf56d 100644 --- a/core/js/sharedialogresharerinfoview.js +++ b/core/js/sharedialogresharerinfoview.js @@ -63,8 +63,8 @@ }, render: function() { - if ( !this.model.hasReshare() - || !this.model.getReshareOwner() !== OC.currentUser) + if (!this.model.hasReshare() + || this.model.getReshareOwner() === OC.currentUser) { this.$el.empty(); return this; diff --git a/core/js/sharedialogshareelistview.js b/core/js/sharedialogshareelistview.js index 463d2468bbd8..c1d7ba4cf66f 100644 --- a/core/js/sharedialogshareelistview.js +++ b/core/js/sharedialogshareelistview.js @@ -211,11 +211,12 @@ if(this.configModel.areAvatarsEnabled()) { this.$el.find('.avatar').each(function() { var $this = $(this); - $this.avatar($this.data('username'), 32); - }); - this.$el.find('.avatar.imageplaceholderseed').each(function() { - var $this = $(this); - $this.imageplaceholder($this.data('seed')); + if ($this.hasClass('imageplaceholderseed')) { + $this.css({width: 32, height: 32}); + $this.imageplaceholder($this.data('seed')); + } else { + $this.avatar($this.data('username'), 32); + } }); } diff --git a/core/js/sharedialogview.js b/core/js/sharedialogview.js index 36e639932032..8c390ee5450c 100644 --- a/core/js/sharedialogview.js +++ b/core/js/sharedialogview.js @@ -15,6 +15,7 @@ var TEMPLATE_BASE = '
    ' + + '{{#if isSharingAllowed}}' + '' + '
    ' + ' ' + @@ -22,6 +23,7 @@ '
    ' + // FIXME: find a good position for remoteShareInfo '{{{remoteShareInfo}}}' + + '{{/if}}' + '
    ' + '
    ' + '
    ' @@ -82,6 +84,9 @@ this.configModel.on('change:isRemoteShareAllowed', function() { view.render(); }); + this.model.on('change:permissions', function() { + view.render(); + }); var subViewOptions = { model: this.model, @@ -163,16 +168,19 @@ this.$el.html(baseTemplate({ shareLabel: t('core', 'Share'), sharePlaceholder: this._renderSharePlaceholderPart(), - remoteShareInfo: this._renderRemoteShareInfoPart() + remoteShareInfo: this._renderRemoteShareInfoPart(), + isSharingAllowed: this.model.sharePermissionPossible() })); - var view = this; - this.$el.find('#shareWith').autocomplete({ - minLength: 2, - delay: 750, - source: this.autocompleteHandler, - select: this._onSelectRecipient - }).data('ui-autocomplete')._renderItem = this.autocompleteRenderItem; + 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(); diff --git a/core/js/tests/specs/sharedialogviewSpec.js b/core/js/tests/specs/sharedialogviewSpec.js index b50432a034fb..071c9c58c4ee 100644 --- a/core/js/tests/specs/sharedialogviewSpec.js +++ b/core/js/tests/specs/sharedialogviewSpec.js @@ -63,7 +63,14 @@ describe('OC.Share.ShareDialogView', function() { possiblePermissions: 31, permissions: 31 }; - configModel = new OC.Share.ShareConfigModel(); + 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 @@ -112,9 +119,6 @@ describe('OC.Share.ShareDialogView', function() { oc_config.enable_avatars = oldEnableAvatars; }); describe('Share with link', function() { - beforeEach(function() { - configModel.set('enforcePasswordForPublicLink', false); - }); // TODO: test ajax calls // TODO: test password field visibility (whenever enforced or not) it('update password on focus out', function() { @@ -138,7 +142,9 @@ describe('OC.Share.ShareDialogView', function() { expect(fakeServer.requests[1].method).toEqual('POST'); var body = OC.parseQueryString(fakeServer.requests[1].requestBody); - expect(body['shareWith']).toEqual('foo'); + expect(body.shareWith).toEqual('foo'); + + fetchStub.reset(); // Set password response fakeServer.requests[1].respond( @@ -147,6 +153,10 @@ describe('OC.Share.ShareDialogView', function() { 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('Password protected'); }); @@ -171,7 +181,9 @@ describe('OC.Share.ShareDialogView', function() { expect(fakeServer.requests[1].method).toEqual('POST'); var body = OC.parseQueryString(fakeServer.requests[1].requestBody); - expect(body['shareWith']).toEqual('foo'); + expect(body.shareWith).toEqual('foo'); + + fetchStub.reset(); // Set password response fakeServer.requests[1].respond( @@ -180,6 +192,10 @@ describe('OC.Share.ShareDialogView', function() { 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('Password protected'); }); @@ -198,8 +214,7 @@ describe('OC.Share.ShareDialogView', function() { expect(dialog.$el.find('#linkCheckbox').length).toEqual(0); }); it('Reset link when password is enforced and link is toggled', function() { - var old = oc_appconfig.core.enforcePasswordForPublicLink; - oc_appconfig.core.enforcePasswordForPublicLink = true; + configModel.set('enforcePasswordForPublicLink', true); $('#allowShareWithLink').val('yes'); dialog.render(); @@ -238,13 +253,9 @@ describe('OC.Share.ShareDialogView', function() { */ dialog.$el.find('[name=linkCheckbox]').click(); expect(fakeServer.requests.length).toEqual(2); - - oc_appconfig.core.enforcePasswordForPublicLink = old; }); it('Reset password placeholder when password is enforced and link is toggled', function() { - var old = oc_appconfig.core.enforcePasswordForPublicLink; - oc_appconfig.core.enforcePasswordForPublicLink = true; $('#allowShareWithLink').val('yes'); dialog.render(); @@ -261,6 +272,7 @@ describe('OC.Share.ShareDialogView', function() { { 'Content-Type': 'application/json' }, JSON.stringify({data: {token: 'xyz'}, status: 'success'}) ); + dialog.render(); expect(dialog.$el.find('#linkPassText').attr('placeholder')).toEqual('**********'); // Remove link @@ -274,119 +286,23 @@ describe('OC.Share.ShareDialogView', function() { // Try to share again dialog.$el.find('[name=linkCheckbox]').click(); expect(dialog.$el.find('#linkPassText').attr('placeholder')).toEqual('Choose a password for the public link'); - - oc_appconfig.core.enforcePasswordForPublicLink = old; - }); - it('reset password on toggle of share', function() { - $('#allowShareWithLink').val('yes'); - - dialog.render(); - - dialog.$el.find('[name=linkCheckbox]').click(); - fakeServer.requests[0].respond( - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({data: {token: 'xyz'}, status: 'success'}) - ); - - //Password protection should be unchecked and password field not visible - expect(dialog.$el.find('[name=showPassword]').prop('checked')).toEqual(false); - expect(dialog.$el.find('#linkPass').is(":visible")).toEqual(false); - - // Toggle and set password - dialog.$el.find('[name=showPassword]').click(); - dialog.$el.find('#linkPassText').val('foo'); - dialog.$el.find('#linkPassText').trigger(new $.Event('keyup', {keyCode: 13})); - fakeServer.requests[1].respond( - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({data: {token: 'xyz2'}, status: 'success'}) - ); - - // Unshare - dialog.$el.find('[name=linkCheckbox]').click(); - fakeServer.requests[2].respond( - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({status: 'success'}) - ); - - // Toggle share again - dialog.$el.find('[name=linkCheckbox]').click(); - fakeServer.requests[3].respond( - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({data: {token: 'xyz3'}, status: 'success'}) - ); - - - // Password checkbox should be unchecked - expect(dialog.$el.find('[name=showPassword]').prop('checked')).toEqual(false); - expect(dialog.$el.find('#linkPass').is(":visible")).toEqual(false); - }); - it('reset expiration on toggle of share', function() { - $('#allowShareWithLink').val('yes'); - - dialog.render(); - - dialog.$el.find('[name=linkCheckbox]').click(); - fakeServer.requests[0].respond( - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({data: {token: 'xyz'}, status: 'success'}) - ); - - //Expiration should be unchecked and expiration field not visible - expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(false); - expect(dialog.$el.find('#expirationDate').is(":visible")).toEqual(false); - - // Toggle and set password - dialog.$el.find('[name=expirationCheckbox]').click(); - d = new Date(); - d.setDate(d.getDate() + 1); - date=d.getDate() + '-' + (d.getMonth()+1) + '-' + d.getFullYear(); - dialog.$el.find('#expirationDate').val(date); - dialog.$el.find('#expirationDate').change(); - fakeServer.requests[1].respond( - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({data: {token: 'xyz2'}, status: 'success'}) - ); - - // Unshare - dialog.$el.find('[name=linkCheckbox]').click(); - fakeServer.requests[2].respond( - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({status: 'success'}) - ); - - // Toggle share again - dialog.$el.find('[name=linkCheckbox]').click(); - fakeServer.requests[3].respond( - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({data: {token: 'xyz3'}, status: 'success'}) - ); - - // Recheck expire visibility - expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(false); - expect(dialog.$el.find('#expirationDate').is(":visible")).toEqual(false); }); it('shows populated link share when a link share exists', function() { + // 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: 'TODO', + link: link, expiration: '', permissions: OC.PERMISSION_READ, stime: 1403884258, }); + dialog.render(); + expect(dialog.$el.find('#linkCheckbox').prop('checked')).toEqual(true); - // this is how the OC.Share class does it... - var link = parent.location.protocol + '//' + location.host + - OC.generateUrl('/s/') + 'tehtoken'; expect(dialog.$el.find('#linkText').val()).toEqual(link); }); describe('expiration date', function() { @@ -400,10 +316,12 @@ describe('OC.Share.ShareDialogView', function() { clock = sinon.useFakeTimers(new Date(2014, 0, 20, 14, 0, 0).getTime()); expectedMinDate = new Date(2014, 0, 21, 14, 0, 0); - oc_appconfig.core.defaultExpireDate = 7; - oc_appconfig.core.enforcePasswordForPublicLink = false; - oc_appconfig.core.defaultExpireDateEnabled = false; - oc_appconfig.core.defaultExpireDateEnforced = false; + configModel.set({ + enforcePasswordForPublicLink: false, + isDefaultExpireDateEnabled: false, + isDefaultExpireDateEnforced: false, + defaultExpireDate: 7 + }); shareModel.set('linkShare', { isLinkShare: true, @@ -436,10 +354,13 @@ describe('OC.Share.ShareDialogView', function() { expect(dialog.$el.find('#expirationDate').val()).toEqual('1234'); }); it('sets default date when default date setting is enabled', function() { - /* jshint camelcase:false */ - oc_appconfig.core.defaultExpireDateEnabled = true; + 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... @@ -450,11 +371,16 @@ describe('OC.Share.ShareDialogView', function() { expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(false); }); it('enforces default date when enforced date setting is enabled', function() { - /* jshint camelcase:false */ - oc_appconfig.core.defaultExpireDateEnabled = true; - oc_appconfig.core.defaultExpireDateEnforced = true; + 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'); @@ -465,12 +391,16 @@ describe('OC.Share.ShareDialogView', function() { expect(dialog.$el.find('[name=expirationCheckbox]').prop('checked')).toEqual(true); }); it('enforces default date when enforced date setting is enabled and password is enforced', function() { - /* jshint camelcase:false */ - oc_appconfig.core.enforcePasswordForPublicLink = true; - oc_appconfig.core.defaultExpireDateEnabled = true; - oc_appconfig.core.defaultExpireDateEnforced = true; + 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'); @@ -493,12 +423,12 @@ describe('OC.Share.ShareDialogView', function() { it('displayes email form when sending emails is enabled', function() { $('input[name=mailPublicNotificationEnabled]').val('yes'); dialog.render(); - expect($('#emailPrivateLink').length).toEqual(1); + 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($('#emailPrivateLink').length).toEqual(0); + expect(dialog.$('#emailPrivateLink').length).toEqual(0); }); it('sets picker minDate to today and no maxDate by default', function() { dialog.render(); @@ -508,9 +438,10 @@ describe('OC.Share.ShareDialogView', function() { expect($.datepicker._defaults.maxDate).toEqual(null); }); it('limits the date range to X days after share time when enforced', function() { - /* jshint camelcase:false */ - oc_appconfig.core.defaultExpireDateEnabled = true; - oc_appconfig.core.defaultExpireDateEnforced = true; + configModel.set({ + isDefaultExpireDateEnabled: true, + isDefaultExpireDateEnforced: true + }); dialog.render(); dialog.$el.find('[name=linkCheckbox]').click(); expect($.datepicker._defaults.minDate).toEqual(expectedMinDate); @@ -518,13 +449,14 @@ describe('OC.Share.ShareDialogView', function() { }); 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; - shareData.shares.push(shareItem); - /* jshint camelcase:false */ - oc_appconfig.core.defaultExpireDateEnabled = true; - oc_appconfig.core.defaultExpireDateEnforced = true; + 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)); @@ -533,7 +465,7 @@ describe('OC.Share.ShareDialogView', function() { }); describe('check for avatar', function() { beforeEach(function() { - loadItemStub.returns({ + shareModel.set({ reshare: { share_type: OC.Share.SHARE_TYPE_USER, uid_owner: 'owner', @@ -569,14 +501,8 @@ describe('OC.Share.ShareDialogView', function() { describe('avatars enabled', function() { beforeEach(function() { oc_config.enable_avatars = true; - OC.Share.showDropDown( - 'file', - 123, - $container, - true, - 31, - 'shared_file_name.txt' - ); + avatarStub.reset(); + dialog.render(); }); afterEach(function() { @@ -586,8 +512,8 @@ describe('OC.Share.ShareDialogView', function() { it('test correct function calls', function() { expect(avatarStub.calledTwice).toEqual(true); expect(placeholderStub.calledTwice).toEqual(true); - expect($('#shareWithList').children().length).toEqual(3); - expect($('.avatar').length).toEqual(4); + expect(dialog.$('#shareWithList').children().length).toEqual(3); + expect(dialog.$('.avatar').length).toEqual(4); }); it('test avatar owner', function() { @@ -617,14 +543,7 @@ describe('OC.Share.ShareDialogView', function() { describe('avatars disabled', function() { beforeEach(function() { - OC.Share.showDropDown( - 'file', - 123, - $container, - true, - 31, - 'shared_file_name.txt' - ); + dialog.render(); }); it('no avatar classes', function() { @@ -634,104 +553,6 @@ describe('OC.Share.ShareDialogView', function() { }); }); }); - describe('"sharesChanged" event', function() { - var autocompleteOptions; - var handler; - beforeEach(function() { - handler = sinon.stub(); - loadItemStub.returns({ - reshare: [], - shares: [{ - id: 100, - item_source: 123, - permissions: 31, - share_type: OC.Share.SHARE_TYPE_USER, - share_with: 'user1', - share_with_displayname: 'User One' - }] - }); - OC.Share.showDropDown( - 'file', - 123, - $container, - true, - 31, - 'shared_file_name.txt' - ); - $('#dropdown').on('sharesChanged', handler); - autocompleteOptions = autocompleteStub.getCall(0).args[0]; - }); - afterEach(function() { - autocompleteOptions = null; - handler = null; - }); - it('triggers "sharesChanged" event when adding shares', function() { - // simulate autocomplete selection - autocompleteOptions.select(new $.Event('select'), { - item: { - label: 'User Two', - value: { - shareType: OC.Share.SHARE_TYPE_USER, - shareWith: 'user2' - } - } - }); - fakeServer.requests[0].respond( - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({status: 'success'}) - ); - expect(handler.calledOnce).toEqual(true); - var shares = handler.getCall(0).args[0].shares; - expect(shares).toBeDefined(); - expect(shares[OC.Share.SHARE_TYPE_USER][0].share_with_displayname).toEqual('User One'); - expect(shares[OC.Share.SHARE_TYPE_USER][1].share_with_displayname).toEqual('User Two'); - expect(shares[OC.Share.SHARE_TYPE_GROUP]).not.toBeDefined(); - }); - it('triggers "sharesChanged" event when deleting shares', function() { - dialog.$el.find('.unshare:eq(0)').click(); - fakeServer.requests[0].respond( - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({status: 'success'}) - ); - expect(handler.calledOnce).toEqual(true); - var shares = handler.getCall(0).args[0].shares; - expect(shares).toBeDefined(); - expect(shares[OC.Share.SHARE_TYPE_USER]).toEqual([]); - expect(shares[OC.Share.SHARE_TYPE_GROUP]).not.toBeDefined(); - }); - it('triggers "sharesChanged" event when toggling link share', function() { - // simulate autocomplete selection - dialog.$el.find('#linkCheckbox').click(); - fakeServer.requests[0].respond( - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({status: 'success', data: { token: 'abc' }}) - ); - expect(handler.calledOnce).toEqual(true); - var shares = handler.getCall(0).args[0].shares; - expect(shares).toBeDefined(); - expect(shares[OC.Share.SHARE_TYPE_USER][0].share_with_displayname).toEqual('User One'); - expect(shares[OC.Share.SHARE_TYPE_GROUP]).not.toBeDefined(); - - handler.reset(); - - // uncheck checkbox - dialog.$el.find('#linkCheckbox').click(); - fakeServer.requests[1].respond( - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({status: 'success'}) - ); - - expect(handler.calledOnce).toEqual(true); - shares = handler.getCall(0).args[0].shares; - expect(shares).toBeDefined(); - expect(shares[OC.Share.SHARE_TYPE_USER][0].share_with_displayname).toEqual('User One'); - expect(shares[OC.Share.SHARE_TYPE_GROUP]).not.toBeDefined(); - }); - }); describe('share permissions', function() { beforeEach(function() { oc_appconfig.core.resharingAllowed = true; @@ -744,14 +565,11 @@ describe('OC.Share.ShareDialogView', function() { * @return {int} permissions sent to the server */ function testWithPermissions(possiblePermissions) { - OC.Share.showDropDown( - 'file', - 123, - $container, - true, - possiblePermissions, - 'shared_file_name.txt' - ); + shareModel.set({ + permissions: possiblePermissions, + possiblePermissions: possiblePermissions + }); + dialog.render(); var autocompleteOptions = autocompleteStub.getCall(0).args[0]; // simulate autocomplete selection autocompleteOptions.select(new $.Event('select'), { @@ -770,8 +588,8 @@ describe('OC.Share.ShareDialogView', function() { describe('regular sharing', function() { it('shares with given permissions with default config', function() { - loadItemStub.returns({ - reshare: [], + shareModel.set({ + reshare: {}, shares: [] }); expect( @@ -782,9 +600,9 @@ describe('OC.Share.ShareDialogView', function() { ).toEqual(OC.PERMISSION_READ | OC.PERMISSION_SHARE); }); it('removes share permission when not allowed', function() { - oc_appconfig.core.resharingAllowed = false; - loadItemStub.returns({ - reshare: [], + configModel.set('isResharingAllowed', false); + shareModel.set({ + reshare: {}, shares: [] }); expect( @@ -792,9 +610,9 @@ describe('OC.Share.ShareDialogView', function() { ).toEqual(OC.PERMISSION_READ | OC.PERMISSION_UPDATE); }); it('automatically adds READ permission even when not specified', function() { - oc_appconfig.core.resharingAllowed = false; - loadItemStub.returns({ - reshare: [], + configModel.set('isResharingAllowed', false); + shareModel.set({ + reshare: {}, shares: [] }); expect( @@ -802,108 +620,35 @@ describe('OC.Share.ShareDialogView', function() { ).toEqual(OC.PERMISSION_READ | OC.PERMISSION_UPDATE | OC.PERMISSION_UPDATE); }); it('does not show sharing options when sharing not allowed', function() { - loadItemStub.returns({ - reshare: [], - shares: [] + shareModel.set({ + reshare: {}, + shares: [], + permissions: OC.PERMISSION_READ }); - OC.Share.showDropDown( - 'file', - 123, - $container, - true, - OC.PERMISSION_READ, - 'shared_file_name.txt' - ); - expect(dialog.$el.find('#shareWithList').length).toEqual(0); - }); - }); - describe('resharing', function() { - it('shares with given permissions when original share had all permissions', function() { - loadItemStub.returns({ - reshare: { - permissions: OC.PERMISSION_ALL - }, - shares: [] - }); - expect( - testWithPermissions(OC.PERMISSION_READ | OC.PERMISSION_UPDATE | OC.PERMISSION_SHARE) - ).toEqual(OC.PERMISSION_READ | OC.PERMISSION_UPDATE | OC.PERMISSION_SHARE); - }); - it('reduces reshare permissions to the ones from the original share', function() { - loadItemStub.returns({ - reshare: { - permissions: OC.PERMISSION_READ, - uid_owner: 'user1' - }, - shares: [] - }); - OC.Share.showDropDown( - 'file', - 123, - $container, - true, - OC.PERMISSION_ALL, - 'shared_file_name.txt' - ); - // no resharing allowed - expect(dialog.$el.find('#shareWithList').length).toEqual(0); - }); - it('reduces reshare permissions to possible permissions', function() { - loadItemStub.returns({ - reshare: { - permissions: OC.PERMISSION_ALL, - uid_owner: 'user1' - }, - shares: [] - }); - OC.Share.showDropDown( - 'file', - 123, - $container, - true, - OC.PERMISSION_READ, - 'shared_file_name.txt' - ); - // no resharing allowed - expect(dialog.$el.find('#shareWithList').length).toEqual(0); + dialog.render(); + expect(dialog.$el.find('#shareWith').prop('disabled')).toEqual(true); }); - it('does not show sharing options when resharing not allowed', function() { - loadItemStub.returns({ + it('shows reshare owner', function() { + shareModel.set({ reshare: { - permissions: OC.PERMISSION_READ | OC.PERMISSION_UPDATE | OC.PERMISSION_DELETE, uid_owner: 'user1' }, - shares: [] + shares: [], + permissions: OC.PERMISSION_READ }); - OC.Share.showDropDown( - 'file', - 123, - $container, - true, - OC.PERMISSION_ALL, - 'shared_file_name.txt' - ); - expect(dialog.$el.find('#shareWithList').length).toEqual(0); + dialog.render(); + expect(dialog.$el.find('.resharerInfoView .reshare').length).toEqual(1); }); - it('allows owner to share their own share when they are also the recipient', function() { - OC.currentUser = 'user1'; - loadItemStub.returns({ + it('does not show reshare owner if owner is current user', function() { + shareModel.set({ reshare: { - permissions: OC.PERMISSION_READ, - uid_owner: 'user1' + uid_owner: OC.currentUser }, - shares: [] + shares: [], + permissions: OC.PERMISSION_READ }); - OC.Share.showDropDown( - 'file', - 123, - $container, - true, - OC.PERMISSION_ALL, - 'shared_file_name.txt' - ); - // sharing still allowed - expect(dialog.$el.find('#shareWithList').length).toEqual(1); + dialog.render(); + expect(dialog.$el.find('.resharerInfoView .reshare').length).toEqual(0); }); }); }); diff --git a/core/js/tests/specs/shareitemmodelSpec.js b/core/js/tests/specs/shareitemmodelSpec.js index d3c4b7d4004b..2f676a4ba1f4 100644 --- a/core/js/tests/specs/shareitemmodelSpec.js +++ b/core/js/tests/specs/shareitemmodelSpec.js @@ -23,13 +23,12 @@ describe('OC.Share.ShareItemModel', function() { var loadItemStub; var fileInfoModel, configModel, model; + var oldCurrentUser; beforeEach(function() { + oldCurrentUser = OC.currentUser; + loadItemStub = sinon.stub(OC.Share, 'loadItem'); - loadItemStub.returns({ - reshare: [], - shares: [] - }); fileInfoModel = new OCA.Files.FileInfoModel({ id: 123, @@ -54,6 +53,7 @@ describe('OC.Share.ShareItemModel', function() { }); afterEach(function() { loadItemStub.restore(); + OC.currentUser = oldCurrentUser; }); describe('Fetching and parsing', function() { @@ -64,7 +64,7 @@ describe('OC.Share.ShareItemModel', function() { expect(loadItemStub.calledWith('file', 123)).toEqual(true); }); it('populates attributes with parsed response', function() { - loadItemStub.returns({ + loadItemStub.yields({ /* jshint camelcase: false */ reshare: { share_type: OC.Share.SHARE_TYPE_USER, @@ -118,7 +118,11 @@ describe('OC.Share.ShareItemModel', function() { var shares = model.get('shares'); expect(shares.length).toEqual(3); - expect(shares.file_source).toEqual(123); + 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); @@ -126,7 +130,7 @@ describe('OC.Share.ShareItemModel', function() { // TODO: check more attributes }); it('does not parse link share when for a different file', function() { - loadItemStub.returns({ + loadItemStub.yields({ reshare: [], /* jshint camelcase: false */ shares: [{ @@ -153,13 +157,14 @@ describe('OC.Share.ShareItemModel', function() { model.fetch(); var shares = model.get('shares'); - expect(shares.length).toEqual(0); + // remaining share appears in this list + expect(shares.length).toEqual(1); var linkShare = model.get('linkShare'); expect(linkShare.isLinkShare).toEqual(false); }); - it('parsess correct link share when a nested link share exists along with parent one', function() { - loadItemStub.returns({ + it('parses correct link share when a nested link share exists along with parent one', function() { + loadItemStub.yields({ reshare: [], /* jshint camelcase: false */ shares: [{ @@ -204,14 +209,58 @@ describe('OC.Share.ShareItemModel', function() { model.fetch(); var shares = model.get('shares'); - expect(shares.length).toEqual(0); + // the parent share remains in the list + expect(shares.length).toEqual(1); var linkShare = model.get('linkShare'); - expect(linkShare.isLinkShare).toEqual(false); + 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() { From f439c07ba9e76d0baebc7ec3000ee5bc5bc3c675 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Tue, 15 Sep 2015 15:47:45 +0200 Subject: [PATCH 55/59] Fix allow reshare for owner when sharing with self through group --- core/js/shareitemmodel.js | 22 +++++++++++++++++++++- core/js/tests/specs/shareitemmodelSpec.js | 2 +- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/core/js/shareitemmodel.js b/core/js/shareitemmodel.js index e0d44586613f..ff0d3a6d8009 100644 --- a/core/js/shareitemmodel.js +++ b/core/js/shareitemmodel.js @@ -656,7 +656,7 @@ } var permissions = this.get('possiblePermissions'); - if(!_.isUndefined(data.reshare) && !_.isUndefined(data.reshare.permissions)) { + if(!_.isUndefined(data.reshare) && !_.isUndefined(data.reshare.permissions) && data.reshare.uid_owner !== OC.currentUser) { permissions = permissions & data.reshare.permissions; } @@ -723,6 +723,26 @@ 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; } }); diff --git a/core/js/tests/specs/shareitemmodelSpec.js b/core/js/tests/specs/shareitemmodelSpec.js index 2f676a4ba1f4..c1d820052e24 100644 --- a/core/js/tests/specs/shareitemmodelSpec.js +++ b/core/js/tests/specs/shareitemmodelSpec.js @@ -274,7 +274,7 @@ describe('OC.Share.ShareItemModel', function() { ['0x12345', null], [ '', null], ], function(value) { - expect(OC.Share._parseTime(value[0])).toEqual(value[1]); + expect(OC.Share.ShareItemModel.prototype._parseTime(value[0])).toEqual(value[1]); }); }); From 02d68d06130345c3397976edd8f69169bbf8e980 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Tue, 15 Sep 2015 15:48:11 +0200 Subject: [PATCH 56/59] Removed obsolete tests --- core/js/tests/specs/sharedialogviewSpec.js | 78 +--------------------- 1 file changed, 2 insertions(+), 76 deletions(-) diff --git a/core/js/tests/specs/sharedialogviewSpec.js b/core/js/tests/specs/sharedialogviewSpec.js index 071c9c58c4ee..de6f99440947 100644 --- a/core/js/tests/specs/sharedialogviewSpec.js +++ b/core/js/tests/specs/sharedialogviewSpec.js @@ -158,7 +158,7 @@ describe('OC.Share.ShareDialogView', function() { dialog.render(); expect(dialog.$el.find('#linkPassText').val()).toEqual(''); - expect(dialog.$el.find('#linkPassText').attr('placeholder')).toEqual('Password protected'); + expect(dialog.$el.find('#linkPassText').attr('placeholder')).toEqual('**********'); }); it('update password on enter', function() { $('#allowShareWithLink').val('yes'); @@ -197,7 +197,7 @@ describe('OC.Share.ShareDialogView', function() { dialog.render(); expect(dialog.$el.find('#linkPassText').val()).toEqual(''); - expect(dialog.$el.find('#linkPassText').attr('placeholder')).toEqual('Password protected'); + expect(dialog.$el.find('#linkPassText').attr('placeholder')).toEqual('**********'); }); it('shows share with link checkbox when allowed', function() { $('#allowShareWithLink').val('yes'); @@ -213,80 +213,6 @@ describe('OC.Share.ShareDialogView', function() { expect(dialog.$el.find('#linkCheckbox').length).toEqual(0); }); - it('Reset link when password is enforced and link is toggled', function() { - configModel.set('enforcePasswordForPublicLink', true); - $('#allowShareWithLink').val('yes'); - - dialog.render(); - - // Toggle linkshare - dialog.$el.find('[name=linkCheckbox]').click(); - expect(dialog.$el.find('#linkText').val()).toEqual(''); - - // Set password - dialog.$el.find('#linkPassText').val('foo'); - dialog.$el.find('#linkPassText').trigger(new $.Event('keyup', {keyCode: 13})); - fakeServer.requests[0].respond( - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({data: {token: 'xyz'}, status: 'success'}) - ); - - // Remove link - dialog.$el.find('[name=linkCheckbox]').click(); - fakeServer.requests[1].respond( - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({status: 'success'}) - ); - - /* - * Try to share again - * The linkText should be emptied - */ - dialog.$el.find('[name=linkCheckbox]').click(); - expect(dialog.$el.find('#linkText').val()).toEqual(''); - - /* - * Do not set password but untoggle - * Since there is no share this should not result in another request to the server - */ - dialog.$el.find('[name=linkCheckbox]').click(); - expect(fakeServer.requests.length).toEqual(2); - }); - - it('Reset password placeholder when password is enforced and link is toggled', function() { - $('#allowShareWithLink').val('yes'); - - dialog.render(); - - // Toggle linkshare - dialog.$el.find('[name=linkCheckbox]').click(); - expect(dialog.$el.find('#linkPassText').attr('placeholder')).toEqual('Choose a password for the public link'); - - // Set password - dialog.$el.find('#linkPassText').val('foo'); - dialog.$el.find('#linkPassText').trigger(new $.Event('keyup', {keyCode: 13})); - fakeServer.requests[0].respond( - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({data: {token: 'xyz'}, status: 'success'}) - ); - dialog.render(); - expect(dialog.$el.find('#linkPassText').attr('placeholder')).toEqual('**********'); - - // Remove link - dialog.$el.find('[name=linkCheckbox]').click(); - fakeServer.requests[1].respond( - 200, - { 'Content-Type': 'application/json' }, - JSON.stringify({status: 'success'}) - ); - - // Try to share again - dialog.$el.find('[name=linkCheckbox]').click(); - expect(dialog.$el.find('#linkPassText').attr('placeholder')).toEqual('Choose a password for the public link'); - }); 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 + From 36e452a472c8144812eb710d44ebbe03d01884d4 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Tue, 15 Sep 2015 15:51:21 +0200 Subject: [PATCH 57/59] Fix cruds button in share dialog Only toggle the current row --- core/js/sharedialogshareelistview.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/js/sharedialogshareelistview.js b/core/js/sharedialogshareelistview.js index c1d7ba4cf66f..41be49dc2418 100644 --- a/core/js/sharedialogshareelistview.js +++ b/core/js/sharedialogshareelistview.js @@ -281,8 +281,9 @@ this.model.setPermissions(shareType, shareWith, permissions); }, - onCrudsToggle: function() { - this.$el.find('.cruds').toggleClass('hidden'); + onCrudsToggle: function(event) { + var $target = $(event.target); + $target.closest('li').find('.cruds').toggleClass('hidden'); return false; }, From 6141ea12b1fbac1de2c5bc4c1f2b756b13d8034e Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Tue, 15 Sep 2015 16:14:29 +0200 Subject: [PATCH 58/59] Port share.css styles to the sidebar panel --- core/css/share.css | 30 +++++++++++++++++------------ core/js/sharedialoglinkshareview.js | 2 +- core/js/sharedialogview.js | 3 +-- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/core/css/share.css b/core/css/share.css index 73bad5c500d9..6467cc0239ce 100644 --- a/core/css/share.css +++ b/core/css/share.css @@ -23,29 +23,29 @@ } } -#dropdown.shareDropDown .unshare.icon-loading-small { +.shareTabView .unshare.icon-loading-small { margin-top: 1px; } -#dropdown.shareDropDown .shareWithLoading, -#dropdown.shareDropDown .linkShare .icon-loading-small { +.shareTabView .shareWithLoading, +.shareTabView .linkShare .icon-loading-small { display: inline-block !important; padding-left: 10px; } -#dropdown.shareDropDown .shareWithLoading { +.shareTabView .shareWithLoading { position: relative; right: 70px; top: 2px; } -#dropdown.shareDropDown .icon-loading-small.hidden { +.shareTabView .icon-loading-small.hidden { display: none !important; } -#dropdown .shareWithRemoteInfo { +.shareTabView .shareWithRemoteInfo { padding: 11px 20px; } -#dropdown .avatar { +.shareTabView .avatar { margin-right: 8px; display: inline-block; overflow: hidden; @@ -87,12 +87,12 @@ #shareWithList li label{ margin-right: 8px; } -#dropdown label { +.shareTabView label { font-weight:400; white-space: nowrap; } -#dropdown input[type="checkbox"] { +.shareTabView input[type="checkbox"] { margin:0 3px 0 8px; vertical-align: middle; } @@ -115,12 +115,18 @@ a.unshare { padding-top:8px; } -#dropdown input[type="text"],#dropdown input[type="password"] { - width: 86%; +.shareTabView input[type="text"], +.shareTabView input[type="password"], +.shareTabView input[type="submit"] { margin-left: 7px; } -#dropdown form { +.shareTabView input[type="text"], +.shareTabView input[type="password"] { + width: 86%; +} + +.shareTabView form { font-size: 100%; margin-left: 0; margin-right: 0; diff --git a/core/js/sharedialoglinkshareview.js b/core/js/sharedialoglinkshareview.js index 2c7914b25456..ab591b9c6e85 100644 --- a/core/js/sharedialoglinkshareview.js +++ b/core/js/sharedialoglinkshareview.js @@ -32,7 +32,7 @@ '
    ' + ' ' + ' ' + - ' ' + + '' + '
    ' + ' {{/if}}' + ' {{#if mailPublicNotificationEnabled}}' + diff --git a/core/js/sharedialogview.js b/core/js/sharedialogview.js index 8c390ee5450c..2b61dab3ceb7 100644 --- a/core/js/sharedialogview.js +++ b/core/js/sharedialogview.js @@ -20,9 +20,8 @@ '
    ' + ' ' + ' '+ - '
    ' + - // FIXME: find a good position for remoteShareInfo '{{{remoteShareInfo}}}' + + '' + '{{/if}}' + '
    ' + '
    ' + From e7e0cfe93d1e7050f35a4b78c9fc70dbe32ec397 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Tue, 15 Sep 2015 17:08:04 +0200 Subject: [PATCH 59/59] Properly show sidebar when clicking on share button --- apps/files/js/filelist.js | 1 + 1 file changed, 1 insertion(+) diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 699df691bbc6..23834fa44de3 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -379,6 +379,7 @@ if (tabId) { this._detailsView.selectTab(tabId); } + OC.Apps.showAppSidebar(); }, /**