From d677e3860d22e3a779c0f01af1adc4e193127d1c Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Mon, 2 Jun 2014 15:59:06 +0200 Subject: Added unit tests for share.js and share icon --- apps/files_sharing/tests/js/shareSpec.js | 213 +++++++++++++++++++++++++++++++ 1 file changed, 213 insertions(+) create mode 100644 apps/files_sharing/tests/js/shareSpec.js (limited to 'apps/files_sharing/tests/js') diff --git a/apps/files_sharing/tests/js/shareSpec.js b/apps/files_sharing/tests/js/shareSpec.js new file mode 100644 index 00000000000..604a4bb2bb6 --- /dev/null +++ b/apps/files_sharing/tests/js/shareSpec.js @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2014 Vincent Petry + * + * This file is licensed under the Affero General Public License version 3 + * or later. + * + * See the COPYING-README file. + * + */ + +describe('OCA.Sharing.Util tests', function() { + var oldFileListPrototype; + var fileList; + var testFiles; + + beforeEach(function() { + // back up prototype, as it will be extended by + // the sharing code + oldFileListPrototype = _.extend({}, OCA.Files.FileList.prototype); + + var $content = $('
'); + $('#testArea').append($content); + // dummy file list + var $div = $( + '
' + + '' + + '' + + '' + + '
' + + '
'); + $('#content').append($div); + + var fileActions = new OCA.Files.FileActions(); + OCA.Sharing.Util.initialize(fileActions); + fileList = new OCA.Files.FileList( + $div, { + fileActions : fileActions + } + ); + + testFiles = [{ + id: 1, + type: 'file', + name: 'One.txt', + path: '/subdir', + mimetype: 'text/plain', + size: 12, + permissions: 31, + etag: 'abc', + shareOwner: 'User One', + isShareMountPoint: false + }]; + + OCA.Sharing.sharesLoaded = true; + OC.Share.statuses = { + 1: {link: false, path: '/subdir'} + }; + }); + afterEach(function() { + OCA.Files.FileList.prototype = oldFileListPrototype; + delete OCA.Sharing.sharesLoaded; + OC.Share.statuses = {}; + }); + + describe('Sharing data in table row', function() { + // TODO: test data-permissions, data-share-owner, etc + }); + describe('Share action icon', function() { + it('do not shows share text when not shared', function() { + var $action; + OC.Share.statuses = {}; + fileList.setFiles([{ + id: 1, + type: 'file', + name: 'One.txt', + path: '/subdir', + mimetype: 'text/plain', + size: 12, + permissions: 31, + etag: 'abc' + }]); + $action = fileList.$el.find('tbody tr:first .action-share'); + expect($action.hasClass('permanent')).toEqual(false); + expect(OC.basename($action.find('img').attr('src'))).toEqual('share.svg'); + }); + it('shows simple share text with share icon', function() { + var $action; + fileList.setFiles([{ + id: 1, + type: 'file', + name: 'One.txt', + path: '/subdir', + mimetype: 'text/plain', + size: 12, + permissions: 31, + etag: 'abc' + }]); + $action = fileList.$el.find('tbody tr:first .action-share'); + expect($action.hasClass('permanent')).toEqual(true); + expect($action.find('>span').text()).toEqual('Shared'); + expect(OC.basename($action.find('img').attr('src'))).toEqual('share.svg'); + }); + it('shows simple share text with public icon when shared with link', function() { + var $action; + OC.Share.statuses = {1: {link: true, path: '/subdir'}}; + fileList.setFiles([{ + id: 1, + type: 'file', + name: 'One.txt', + path: '/subdir', + mimetype: 'text/plain', + size: 12, + permissions: 31, + etag: 'abc' + }]); + $action = fileList.$el.find('tbody tr:first .action-share'); + expect($action.hasClass('permanent')).toEqual(true); + expect($action.find('>span').text()).toEqual('Shared'); + expect(OC.basename($action.find('img').attr('src'))).toEqual('public.svg'); + }); + it('shows owner name when owner is available', function() { + var $action; + fileList.setFiles([{ + id: 1, + type: 'file', + name: 'One.txt', + path: '/subdir', + mimetype: 'text/plain', + size: 12, + permissions: 31, + shareOwner: 'User One', + etag: 'abc' + }]); + $action = fileList.$el.find('tbody tr:first .action-share'); + expect($action.hasClass('permanent')).toEqual(true); + expect($action.find('>span').text()).toEqual('Shared by User One'); + expect(OC.basename($action.find('img').attr('src'))).toEqual('share.svg'); + }); + it('shows recipients when recipients are available', function() { + var $action; + fileList.setFiles([{ + id: 1, + type: 'file', + name: 'One.txt', + path: '/subdir', + mimetype: 'text/plain', + size: 12, + permissions: 31, + recipientsDisplayName: 'User One, User Two', + etag: 'abc' + }]); + $action = fileList.$el.find('tbody tr:first .action-share'); + expect($action.hasClass('permanent')).toEqual(true); + expect($action.find('>span').text()).toEqual('Shared with User One, User Two'); + expect(OC.basename($action.find('img').attr('src'))).toEqual('share.svg'); + }); + }); + describe('Share action', function() { + // TODO: test file action / dropdown trigger + it('updates share icon when shares were changed in dropdown', function() { + fileList.setFiles(testFiles); + fileList.$el.find('tr:first .action-share').click(); + }); + }); + describe('formatRecipients', function() { + it('returns a single recipient when one passed', function() { + expect(OCA.Sharing.Util.formatRecipients(['User one'])) + .toEqual('User one'); + }); + it('returns two recipients when two passed', function() { + expect(OCA.Sharing.Util.formatRecipients(['User one', 'User two'])) + .toEqual('User one, User two'); + }); + it('returns four recipients with plus when five passed', function() { + var recipients = [ + 'User one', + 'User two', + 'User three', + 'User four', + 'User five' + ]; + expect(OCA.Sharing.Util.formatRecipients(recipients)) + .toEqual('User four, User one, User three, User two, +1'); + }); + it('returns four recipients with plus when ten passed', function() { + var recipients = [ + 'User one', + 'User two', + 'User three', + 'User four', + 'User five', + 'User six', + 'User seven', + 'User eight', + 'User nine', + 'User ten' + ]; + expect(OCA.Sharing.Util.formatRecipients(recipients)) + .toEqual('User four, User one, User three, User two, +6'); + }); + it('returns four recipients with plus when four passed with counter', function() { + var recipients = [ + 'User one', + 'User two', + 'User three', + 'User four' + ]; + expect(OCA.Sharing.Util.formatRecipients(recipients, 10)) + .toEqual('User four, User one, User three, User two, +6'); + }); + }); + +}); -- cgit v1.2.3 From 0944565f6033766abfe0ab8987d7116062748c21 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Mon, 2 Jun 2014 17:23:52 +0200 Subject: More unit tests and fixes for share --- apps/files_sharing/js/share.js | 201 +++++++++++++++---------------- apps/files_sharing/tests/js/shareSpec.js | 179 +++++++++++++++++++++++---- core/js/share.js | 37 ++++-- core/js/tests/specs/shareSpec.js | 144 +++++++++++++++++++++- 4 files changed, 421 insertions(+), 140 deletions(-) (limited to 'apps/files_sharing/tests/js') diff --git a/apps/files_sharing/js/share.js b/apps/files_sharing/js/share.js index 9f47e785821..66def1fe73f 100644 --- a/apps/files_sharing/js/share.js +++ b/apps/files_sharing/js/share.js @@ -14,119 +14,112 @@ } OCA.Sharing.Util = { initialize: function(fileActions) { - if (!_.isUndefined(OC.Share) && !_.isUndefined(OCA.Files)) { - // TODO: make a separate class for this or a hook or jQuery event ? - if (OCA.Files.FileList) { - var oldCreateRow = OCA.Files.FileList.prototype._createRow; - OCA.Files.FileList.prototype._createRow = function(fileData) { - var tr = oldCreateRow.apply(this, arguments); - if (fileData.shareOwner) { - tr.attr('data-share-owner', fileData.shareOwner); - // user should always be able to rename a mount point - if (fileData.isShareMountPoint) { - tr.attr('data-permissions', fileData.permissions | OC.PERMISSION_UPDATE); - tr.attr('data-reshare-permissions', fileData.permissions); - } + // TODO: make a separate class for this or a hook or jQuery event ? + if (OCA.Files.FileList) { + var oldCreateRow = OCA.Files.FileList.prototype._createRow; + OCA.Files.FileList.prototype._createRow = function(fileData) { + var tr = oldCreateRow.apply(this, arguments); + if (fileData.shareOwner) { + tr.attr('data-share-owner', fileData.shareOwner); + // user should always be able to rename a mount point + if (fileData.isShareMountPoint) { + tr.attr('data-permissions', fileData.permissions | OC.PERMISSION_UPDATE); + tr.attr('data-reshare-permissions', fileData.permissions); } - if (fileData.recipientsDisplayName) { - tr.attr('data-share-recipients', fileData.recipientsDisplayName); - } - return tr; - }; - } + } + if (fileData.recipientsDisplayName) { + tr.attr('data-share-recipients', fileData.recipientsDisplayName); + } + return tr; + }; + } - // use delegate to catch the case with multiple file lists - $('#content').delegate('#fileList', 'fileActionsReady',function(ev){ - // if no share action exists because the admin disabled sharing for this user - // we create a share notification action to inform the user about files - // shared with him otherwise we just update the existing share action. - var fileList = ev.fileList; - var $fileList = $(this); - $fileList.find('[data-share-owner]').each(function() { - var $tr = $(this); - var permissions = $tr.data('permissions'); - if(permissions & OC.PERMISSION_SHARE) { - OC.Share.markFileAsShared($tr, true); - } else { - // TODO: make this work like/with OC.Share.markFileAsShared() - var shareNotification = '' + - ' '; - $tr.find('.fileactions').append(function() { - var owner = $(this).closest('tr').attr('data-share-owner'); - var shareBy = t('files_sharing', 'Shared by {owner}', {owner: owner}); - var $result = $(shareNotification + ' ' + shareBy + ''); - $result.on('click', function() { - return false; - }); - return $result; + // use delegate to catch the case with multiple file lists + $('#content').delegate('#fileList', 'fileActionsReady',function(ev){ + // if no share action exists because the admin disabled sharing for this user + // we create a share notification action to inform the user about files + // shared with him otherwise we just update the existing share action. + var fileList = ev.fileList; + var $fileList = $(this); + $fileList.find('[data-share-owner]').each(function() { + var $tr = $(this); + var permissions = $tr.data('permissions'); + if(permissions & OC.PERMISSION_SHARE) { + OC.Share.markFileAsShared($tr, true); + } else { + // TODO: make this work like/with OC.Share.markFileAsShared() + var shareNotification = '' + + ' '; + $tr.find('.fileactions').append(function() { + var owner = $(this).closest('tr').attr('data-share-owner'); + var shareBy = t('files_sharing', 'Shared by {owner}', {owner: owner}); + var $result = $(shareNotification + ' ' + shareBy + ''); + $result.on('click', function() { + return false; }); - } - }); - - if (!OCA.Sharing.sharesLoaded){ - OC.Share.loadIcons('file', fileList); - // assume that we got all shares, so switching directories - // will not invalidate that list - OCA.Sharing.sharesLoaded = true; - } - else{ - OC.Share.updateIcons('file', fileList); + return $result; + }); } }); - fileActions.register( - 'all', - 'Share', - OC.PERMISSION_SHARE, - OC.imagePath('core', 'actions/share'), - function(filename, context) { + if (!OCA.Sharing.sharesLoaded){ + OC.Share.loadIcons('file', fileList); + // assume that we got all shares, so switching directories + // will not invalidate that list + OCA.Sharing.sharesLoaded = true; + } + else{ + OC.Share.updateIcons('file', fileList); + } + }); - var $tr = context.$file; - var itemType = 'file'; - if ($tr.data('type') === 'dir') { - itemType = 'folder'; - } - var possiblePermissions = $tr.data('reshare-permissions'); - if (_.isUndefined(possiblePermissions)) { - possiblePermissions = $tr.data('permissions'); - } + fileActions.register( + 'all', + 'Share', + OC.PERMISSION_SHARE, + OC.imagePath('core', 'actions/share'), + function(filename, context) { - var appendTo = $tr.find('td.filename'); - // Check if drop down is already visible for a different file - if (OC.Share.droppedDown) { - if ($tr.data('id') !== $('#dropdown').attr('data-item-source')) { - OC.Share.hideDropDown(function () { - $tr.addClass('mouseOver'); - OC.Share.showDropDown(itemType, $tr.data('id'), appendTo, true, possiblePermissions, filename); - }); - } else { - OC.Share.hideDropDown(); - } + var $tr = context.$file; + var itemType = 'file'; + if ($tr.data('type') === 'dir') { + itemType = 'folder'; + } + var possiblePermissions = $tr.data('reshare-permissions'); + if (_.isUndefined(possiblePermissions)) { + possiblePermissions = $tr.data('permissions'); + } + + var appendTo = $tr.find('td.filename'); + // Check if drop down is already visible for a different file + if (OC.Share.droppedDown) { + if ($tr.data('id') !== $('#dropdown').attr('data-item-source')) { + OC.Share.hideDropDown(function () { + $tr.addClass('mouseOver'); + OC.Share.showDropDown(itemType, $tr.data('id'), appendTo, true, possiblePermissions, filename); + }); } else { - $tr.addClass('mouseOver'); - OC.Share.showDropDown(itemType, $tr.data('id'), appendTo, true, possiblePermissions, filename); + OC.Share.hideDropDown(); + } + } else { + $tr.addClass('mouseOver'); + OC.Share.showDropDown(itemType, $tr.data('id'), appendTo, true, possiblePermissions, filename); + } + $('#dropdown').on('sharesChanged', function(ev) { + // note: we only update the data attribute because updateIcon() + // is called automatically after this event + var userShares = ev.itemShares[OC.Share.SHARE_TYPE_USER] || []; + var groupShares = ev.itemShares[OC.Share.SHARE_TYPE_GROUP] || []; + var recipients = _.union(userShares, groupShares); + if (recipients.length) { + $tr.attr('data-share-recipients', OCA.Sharing.Util.formatRecipients(recipients)); + } + else { + $tr.removeAttr('data-share-recipients'); } - $('#dropdown').on('sharesChanged', function(ev) { - // note: we only update the data attribute because updateIcon() - // is called automatically after this event - var userShares = ev.itemShares[OC.Share.SHARE_TYPE_USER] || []; - var groupShares = ev.itemShares[OC.Share.SHARE_TYPE_GROUP] || []; - var recipients = _.union(userShares, groupShares); - // only update the recipients if they existed before - // (some file lists don't have them) - if (!_.isUndefined($tr.attr('data-share-recipients'))) { - // FIXME: use display names from users, we currently only got user ids - if (recipients.length) { - $tr.attr('data-share-recipients', OCA.Sharing.Util.formatRecipients(recipients)); - } - else { - $tr.attr('data-share-recipients', ''); - } - } - }); }); - } + }); }, /** @@ -159,7 +152,9 @@ $(document).ready(function() { // FIXME: HACK: do not init when running unit tests, need a better way if (!window.TESTING) { - OCA.Sharing.Util.initialize(OCA.Files.fileActions); + if (!_.isUndefined(OC.Share) && !_.isUndefined(OCA.Files)) { + OCA.Sharing.Util.initialize(OCA.Files.fileActions); + } } }); diff --git a/apps/files_sharing/tests/js/shareSpec.js b/apps/files_sharing/tests/js/shareSpec.js index 604a4bb2bb6..d2697beeb0b 100644 --- a/apps/files_sharing/tests/js/shareSpec.js +++ b/apps/files_sharing/tests/js/shareSpec.js @@ -13,6 +13,16 @@ describe('OCA.Sharing.Util tests', function() { var fileList; var testFiles; + function getImageUrl($el) { + // might be slightly different cross-browser + var url = $el.css('background-image'); + var r = url.match(/url\(['"]?([^'")]*)['"]?\)/); + if (!r) { + return url; + } + return r[1]; + } + beforeEach(function() { // back up prototype, as it will be extended by // the sharing code @@ -59,6 +69,7 @@ describe('OCA.Sharing.Util tests', function() { afterEach(function() { OCA.Files.FileList.prototype = oldFileListPrototype; delete OCA.Sharing.sharesLoaded; + delete OC.Share.droppedDown; OC.Share.statuses = {}; }); @@ -67,62 +78,71 @@ describe('OCA.Sharing.Util tests', function() { }); describe('Share action icon', function() { it('do not shows share text when not shared', function() { - var $action; + var $action, $tr; OC.Share.statuses = {}; fileList.setFiles([{ id: 1, - type: 'file', - name: 'One.txt', + type: 'dir', + name: 'One', path: '/subdir', mimetype: 'text/plain', size: 12, permissions: 31, etag: 'abc' }]); - $action = fileList.$el.find('tbody tr:first .action-share'); + $tr = fileList.$el.find('tbody tr:first'); + $action = $tr.find('.action-share'); expect($action.hasClass('permanent')).toEqual(false); expect(OC.basename($action.find('img').attr('src'))).toEqual('share.svg'); + expect(OC.basename(getImageUrl($tr.find('.filename')))).toEqual('folder.svg'); + expect($action.find('img').length).toEqual(1); }); it('shows simple share text with share icon', function() { - var $action; + var $action, $tr; fileList.setFiles([{ id: 1, - type: 'file', - name: 'One.txt', + type: 'dir', + name: 'One', path: '/subdir', mimetype: 'text/plain', size: 12, permissions: 31, etag: 'abc' }]); - $action = fileList.$el.find('tbody tr:first .action-share'); + $tr = fileList.$el.find('tbody tr:first'); + $action = $tr.find('.action-share'); expect($action.hasClass('permanent')).toEqual(true); expect($action.find('>span').text()).toEqual('Shared'); expect(OC.basename($action.find('img').attr('src'))).toEqual('share.svg'); + expect(OC.basename(getImageUrl($tr.find('.filename')))).toEqual('folder-shared.svg'); + expect($action.find('img').length).toEqual(1); }); it('shows simple share text with public icon when shared with link', function() { - var $action; + var $action, $tr; OC.Share.statuses = {1: {link: true, path: '/subdir'}}; fileList.setFiles([{ id: 1, - type: 'file', - name: 'One.txt', + type: 'dir', + name: 'One', path: '/subdir', mimetype: 'text/plain', size: 12, permissions: 31, etag: 'abc' }]); - $action = fileList.$el.find('tbody tr:first .action-share'); + $tr = fileList.$el.find('tbody tr:first'); + $action = $tr.find('.action-share'); expect($action.hasClass('permanent')).toEqual(true); expect($action.find('>span').text()).toEqual('Shared'); expect(OC.basename($action.find('img').attr('src'))).toEqual('public.svg'); + expect(OC.basename(getImageUrl($tr.find('.filename')))).toEqual('folder-public.svg'); + expect($action.find('img').length).toEqual(1); }); it('shows owner name when owner is available', function() { - var $action; + var $action, $tr; fileList.setFiles([{ id: 1, - type: 'file', + type: 'dir', name: 'One.txt', path: '/subdir', mimetype: 'text/plain', @@ -131,16 +151,18 @@ describe('OCA.Sharing.Util tests', function() { shareOwner: 'User One', etag: 'abc' }]); - $action = fileList.$el.find('tbody tr:first .action-share'); + $tr = fileList.$el.find('tbody tr:first'); + $action = $tr.find('.action-share'); expect($action.hasClass('permanent')).toEqual(true); expect($action.find('>span').text()).toEqual('Shared by User One'); expect(OC.basename($action.find('img').attr('src'))).toEqual('share.svg'); + expect(OC.basename(getImageUrl($tr.find('.filename')))).toEqual('folder-shared.svg'); }); it('shows recipients when recipients are available', function() { - var $action; + var $action, $tr; fileList.setFiles([{ id: 1, - type: 'file', + type: 'dir', name: 'One.txt', path: '/subdir', mimetype: 'text/plain', @@ -149,17 +171,130 @@ describe('OCA.Sharing.Util tests', function() { recipientsDisplayName: 'User One, User Two', etag: 'abc' }]); - $action = fileList.$el.find('tbody tr:first .action-share'); + $tr = fileList.$el.find('tbody tr:first'); + $action = $tr.find('.action-share'); expect($action.hasClass('permanent')).toEqual(true); expect($action.find('>span').text()).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')))).toEqual('folder-shared.svg'); + expect($action.find('img').length).toEqual(1); }); }); describe('Share action', function() { - // TODO: test file action / dropdown trigger - it('updates share icon when shares were changed in dropdown', function() { - fileList.setFiles(testFiles); - fileList.$el.find('tr:first .action-share').click(); + var showDropDownStub; + + beforeEach(function() { + showDropDownStub = sinon.stub(OC.Share, 'showDropDown', function() { + $('#testArea').append($('')); + }); + }); + afterEach(function() { + showDropDownStub.restore(); + }); + it('adds share icon after sharing a non-shared file', function() { + var $action, $tr; + OC.Share.statuses = {}; + fileList.setFiles([{ + id: 1, + type: 'file', + name: 'One.txt', + path: '/subdir', + mimetype: 'text/plain', + size: 12, + permissions: 31, + etag: 'abc' + }]); + $action = fileList.$el.find('tbody tr:first .action-share'); + $tr = fileList.$el.find('tr:first'); + + expect($action.hasClass('permanent')).toEqual(false); + + $tr.find('.action-share').click(); + + expect(showDropDownStub.calledOnce).toEqual(true); + + // simulate what the dropdown does + var itemShares = {}; + itemShares[OC.Share.SHARE_TYPE_USER] = ['User One', 'User Two']; + itemShares[OC.Share.SHARE_TYPE_GROUP] = ['Group One', 'Group Two']; + OC.Share.itemShares = itemShares; + $('#dropdown').trigger(new $.Event('sharesChanged', {itemShares: itemShares})); + + 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()).toEqual('Shared with Group One, Group Two, User One, User Two'); + expect(OC.basename($action.find('img').attr('src'))).toEqual('share.svg'); + }); + it('removes share icon after unsharing a shared file', function() { + var $action, $tr; + OC.Share.statuses = {1: {link: false, path: '/subdir'}}; + fileList.setFiles([{ + id: 1, + type: 'file', + name: 'One.txt', + path: '/subdir', + mimetype: 'text/plain', + size: 12, + permissions: 31, + etag: 'abc' + }]); + $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 itemShares = {}; + itemShares[OC.Share.SHARE_TYPE_USER] = ['User One', 'User Two', 'User Three']; + OC.Share.itemShares = itemShares; + $('#dropdown').trigger(new $.Event('sharesChanged', {itemShares: itemShares})); + + 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()).toEqual('Shared with User One, User Three, User Two'); + expect(OC.basename($action.find('img').attr('src'))).toEqual('share.svg'); + }); + it('updates share icon after modifying shares on a shared file', function() { + var $action, $tr; + OC.Share.statuses = {1: {link: false, path: '/subdir'}}; + fileList.setFiles([{ + id: 1, + type: 'file', + name: 'One.txt', + path: '/subdir', + mimetype: 'text/plain', + size: 12, + permissions: 31, + etag: 'abc', + recipients: 'User One, User Two' + }]); + $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 itemShares = {}; + OC.Share.itemShares = itemShares; + $('#dropdown').trigger(new $.Event('sharesChanged', {itemShares: itemShares})); + + expect($tr.attr('data-share-recipients')).not.toBeDefined(); + + OC.Share.updateIcon('file', 1); + expect($action.hasClass('permanent')).toEqual(false); }); }); describe('formatRecipients', function() { diff --git a/core/js/share.js b/core/js/share.js index c4ca63908a9..4e12f35d19e 100644 --- a/core/js/share.js +++ b/core/js/share.js @@ -47,6 +47,7 @@ OC.Share={ $fileList = fileList.$fileList; currentDir = fileList.getCurrentDirectory(); } + // TODO: iterating over the files might be more efficient for (item in OC.Share.statuses){ var image = OC.imagePath('core', 'actions/share'); var data = OC.Share.statuses[item]; @@ -63,7 +64,7 @@ OC.Share={ var shareFolder = OC.imagePath('core', 'filetypes/folder-shared'); var img; if (file.length > 0) { - this.markFileAsShared(file, true, image); + this.markFileAsShared(file, true, hasLink); } else { var dir = currentDir; if (dir.length > 1) { @@ -122,7 +123,11 @@ OC.Share={ } else { var $tr = $('tr').filterAttr('data-id', String(itemSource)); if ($tr.length > 0) { - this.markFileAsShared($tr, shares, image); + // it might happen that multiple lists exist in the DOM + // with the same id + $tr.each(function() { + OC.Share.markFileAsShared($(this), shares, link); + }); } } if (shares) { @@ -133,13 +138,14 @@ OC.Share={ } }, /** - * Marks/unmarks a given file as shared + * Marks/unmarks a given file as shared by changing its action icon + * and folder icon. * * @param $tr file element to mark as shared - * @param state true to mark as shared, false to unmark - * @param image image to use for the icon + * @param hasShares whether shares are available + * @param hasLink whether link share is available */ - markFileAsShared: function($tr, state, image) { + markFileAsShared: function($tr, hasShares, hasLink) { var action = $tr.find('.fileactions .action[data-action="Share"]'); var type = $tr.data('type'); var img = action.find('img'); @@ -147,14 +153,22 @@ OC.Share={ var recipients; var owner; var shareFolderIcon; - if (type === 'dir' && state) { - shareFolderIcon = OC.imagePath('core', 'filetypes/folder-shared'); + var image = OC.imagePath('core', 'actions/share'); + // update folder icon + if (type === 'dir' && (hasShares || hasLink)) { + if (hasLink) { + shareFolderIcon = OC.imagePath('core', 'filetypes/folder-public'); + } + else { + shareFolderIcon = OC.imagePath('core', 'filetypes/folder-shared'); + } $tr.children('.filename').css('background-image', 'url(' + shareFolderIcon + ')'); } else if (type === 'dir') { shareFolderIcon = OC.imagePath('core', 'filetypes/folder'); $tr.children('.filename').css('background-image', 'url(' + shareFolderIcon + ')'); } - if (state) { + // update share action text / icon + if (hasShares) { recipients = $tr.attr('data-share-recipients'); owner = $tr.attr('data-share-owner'); @@ -162,7 +176,6 @@ OC.Share={ message = t('core', 'Shared'); if (owner && !recipients) { message = t('files_sharing', 'Shared by {owner}', {owner: owner}); - image = image || OC.imagePath('core', 'actions/share'); } if (recipients) { message = t('core', 'Shared with {recipients}', {recipients: recipients}); @@ -173,6 +186,9 @@ OC.Share={ action.removeClass('permanent'); action.html(' '+ escapeHTML(t('core', 'Share'))+'').prepend(img); } + if (hasLink) { + image = OC.imagePath('core', 'actions/public'); + } img.attr('src', image); }, loadItem:function(itemType, itemSource) { @@ -756,6 +772,7 @@ $(document).ready(function() { if ($('#linkText').val() !== '') { OC.Share.unshare(itemType, itemSource, OC.Share.SHARE_TYPE_LINK, '', function() { OC.Share.itemShares[OC.Share.SHARE_TYPE_LINK] = false; + $('#dropdown').trigger(new $.Event('sharesChanged', {itemShares: OC.Share.itemShares})); OC.Share.updateIcon(itemType, itemSource); if (typeof OC.Share.statuses[itemSource] === 'undefined') { $('#expiration').hide('blind'); diff --git a/core/js/tests/specs/shareSpec.js b/core/js/tests/specs/shareSpec.js index a487b71fdbb..a8d9225649c 100644 --- a/core/js/tests/specs/shareSpec.js +++ b/core/js/tests/specs/shareSpec.js @@ -26,13 +26,17 @@ describe('OC.Share tests', function() { var oldAppConfig; var loadItemStub; var autocompleteStub; + beforeEach(function() { $('#testArea').append($('
')); + // horrible parameters + $('#testArea').append(''); $container = $('#shareContainer'); /* jshint camelcase:false */ - oldAppConfig = oc_appconfig.core; - loadItemStub = sinon.stub(OC.Share, 'loadItem'); + oldAppConfig = _.extend({}, oc_appconfig.core); + oc_appconfig.core.enforcePasswordForPublicLink = false; + loadItemStub = sinon.stub(OC.Share, 'loadItem'); loadItemStub.returns({ reshare: [], shares: [] @@ -89,9 +93,139 @@ describe('OC.Share tests', function() { oc_appconfig.core.defaultExpireDate = ''; // TODO: expect that default date was NOT set }); - // TODO: test password field visibility (whenever enforced or not) - // TODO: check link share field visibility based on whether it is allowed - // TODO: check public upload visibility based on config + describe('Share with link', function() { + // TODO: test ajax calls + // TODO: test password field visibility (whenever enforced or not) + // TODO: check public upload visibility based on config + it('shows share with link checkbox when allowed', function() { + $('#allowShareWithLink').val('yes'); + OC.Share.showDropDown( + 'file', + 123, + $container, + 'http://localhost/dummylink', + 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, + 'http://localhost/dummylink', + 31, + 'shared_file_name.txt' + ); + expect($('#dropdown #linkCheckbox').length).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, + 'http://localhost/dummylink', + 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 itemShares = handler.getCall(0).args[0].itemShares; + expect(itemShares).toBeDefined(); + expect(itemShares[OC.Share.SHARE_TYPE_USER]).toEqual(['user1', 'user2']); + expect(itemShares[OC.Share.SHARE_TYPE_GROUP]).not.toBeDefined(); + expect(itemShares[OC.Share.SHARE_TYPE_LINK]).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 itemShares = handler.getCall(0).args[0].itemShares; + expect(itemShares).toBeDefined(); + expect(itemShares[OC.Share.SHARE_TYPE_USER]).toEqual([]); + expect(itemShares[OC.Share.SHARE_TYPE_GROUP]).not.toBeDefined(); + expect(itemShares[OC.Share.SHARE_TYPE_LINK]).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 itemShares = handler.getCall(0).args[0].itemShares; + expect(itemShares).toBeDefined(); + expect(itemShares[OC.Share.SHARE_TYPE_USER]).toEqual(['user1']); + expect(itemShares[OC.Share.SHARE_TYPE_GROUP]).not.toBeDefined(); + expect(itemShares[OC.Share.SHARE_TYPE_LINK]).toEqual(true); + + 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); + itemShares = handler.getCall(0).args[0].itemShares; + expect(itemShares).toBeDefined(); + expect(itemShares[OC.Share.SHARE_TYPE_USER]).toEqual(['user1']); + expect(itemShares[OC.Share.SHARE_TYPE_GROUP]).not.toBeDefined(); + // currently inconsistent, removing share with link sets it to false + // instead of delete + expect(itemShares[OC.Share.SHARE_TYPE_LINK]).toBeFalsy(); + //expect(itemShares[OC.Share.SHARE_TYPE_LINK]).not.toBeDefined(); + }); + }); }); }); -- cgit v1.2.3 From 640fbc016dd81f6ffffb4eb404717a9dd2c2465a Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Mon, 2 Jun 2014 18:52:31 +0200 Subject: Now using shareOwner for the share owner Makes it consistent with the regular file list fileData --- apps/files_sharing/js/sharedfilelist.js | 4 ++-- apps/files_sharing/tests/js/appSpec.js | 4 +--- apps/files_sharing/tests/js/sharedfilelistSpec.js | 10 +++++++++- 3 files changed, 12 insertions(+), 6 deletions(-) (limited to 'apps/files_sharing/tests/js') diff --git a/apps/files_sharing/js/sharedfilelist.js b/apps/files_sharing/js/sharedfilelist.js index 8a6c3169f9e..ecac5b33667 100644 --- a/apps/files_sharing/js/sharedfilelist.js +++ b/apps/files_sharing/js/sharedfilelist.js @@ -46,7 +46,7 @@ $tr.find('td.filename input:checkbox').remove(); $tr.attr('data-share-id', _.pluck(fileData.shares, 'id').join(',')); if (this._sharedWithUser) { - $tr.attr('data-share-owner', fileData.shares[0].ownerDisplayName); + $tr.attr('data-share-owner', fileData.shareOwner); } return $tr; }, @@ -159,7 +159,7 @@ stime: share.stime * 1000, }; if (self._sharedWithUser) { - file.share.ownerDisplayName = share.displayname_owner; + file.shareOwner = share.displayname_owner; file.name = OC.basename(share.file_target); file.path = OC.dirname(share.file_target); file.permissions = share.permissions; diff --git a/apps/files_sharing/tests/js/appSpec.js b/apps/files_sharing/tests/js/appSpec.js index ad95ee53942..6a30f18ea40 100644 --- a/apps/files_sharing/tests/js/appSpec.js +++ b/apps/files_sharing/tests/js/appSpec.js @@ -122,9 +122,7 @@ describe('OCA.Sharing.App tests', function() { type: 'dir', path: '/somewhere/inside/subdir', counterParts: ['user2'], - shares: [{ - ownerDisplayName: 'user2' - }] + shareOwner: 'user2' }]); fileListIn.findFileEl('testdir').find('td a.name').click(); diff --git a/apps/files_sharing/tests/js/sharedfilelistSpec.js b/apps/files_sharing/tests/js/sharedfilelistSpec.js index 7aec8322a44..f53e79e277f 100644 --- a/apps/files_sharing/tests/js/sharedfilelistSpec.js +++ b/apps/files_sharing/tests/js/sharedfilelistSpec.js @@ -9,7 +9,8 @@ */ describe('OCA.Sharing.FileList tests', function() { - var testFiles, alertStub, notificationStub, fileList; + var testFiles, alertStub, notificationStub, fileList, fileActions; + var oldFileListPrototype; beforeEach(function() { alertStub = sinon.stub(OC.dialogs, 'alert'); @@ -45,10 +46,17 @@ describe('OCA.Sharing.FileList tests', function() { '
Empty content message
' + '' ); + // back up prototype, as it will be extended by + // the sharing code + oldFileListPrototype = _.extend({}, OCA.Files.FileList.prototype); + fileActions = new OCA.Files.FileActions(); + OCA.Sharing.Util.initialize(fileActions); }); afterEach(function() { + OCA.Files.FileList.prototype = oldFileListPrototype; testFiles = undefined; fileList = undefined; + fileActions = undefined; notificationStub.restore(); alertStub.restore(); -- cgit v1.2.3 From 1297b2b883c447c5183a79eb6c0e0061b69aeadf Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Tue, 3 Jun 2014 11:04:57 +0200 Subject: Multiple fixes to sharing overview - scrolling to next page now correctly renders share icons - reshareing/unsharing a reshare will now still keep "Shared by" label --- apps/files_sharing/js/share.js | 60 ++++++++-------- apps/files_sharing/js/sharedfilelist.js | 7 ++ apps/files_sharing/tests/js/shareSpec.js | 114 ++++++++++++++++++++++++++++--- core/js/share.js | 10 +-- 4 files changed, 150 insertions(+), 41 deletions(-) (limited to 'apps/files_sharing/tests/js') diff --git a/apps/files_sharing/js/share.js b/apps/files_sharing/js/share.js index 66def1fe73f..8a63593883b 100644 --- a/apps/files_sharing/js/share.js +++ b/apps/files_sharing/js/share.js @@ -14,7 +14,6 @@ } OCA.Sharing.Util = { initialize: function(fileActions) { - // TODO: make a separate class for this or a hook or jQuery event ? if (OCA.Files.FileList) { var oldCreateRow = OCA.Files.FileList.prototype._createRow; OCA.Files.FileList.prototype._createRow = function(fileData) { @@ -32,37 +31,43 @@ } return tr; }; + + var oldRenderRow = OCA.Files.FileList.prototype._renderRow; + OCA.Files.FileList.prototype._renderRow = function(fileData) { + var $tr = oldRenderRow.apply(this, arguments); + // if the statuses are loaded already, use them for the icon + // (needed when scrolling to the next page) + var shareStatus = OC.Share.statuses[fileData.id]; + if (fileData.shareOwner || fileData.recipientsDisplayName || shareStatus) { + var permissions = $tr.data('permissions'); + var hasLink = !!(shareStatus && shareStatus.link); + if (permissions & OC.PERMISSION_SHARE) { + OC.Share.markFileAsShared($tr, true, hasLink); + } else { + // if no share action exists because the admin disabled sharing for this user + // we create a share notification action to inform the user about files + // shared with him otherwise we just update the existing share action. + // TODO: make this work like/with OC.Share.markFileAsShared() + var shareNotification = '
' + + ' '; + $tr.find('.fileactions').append(function() { + var shareBy = t('files_sharing', 'Shared by {owner}', {owner: fileData.shareOwner}); + var $result = $(shareNotification + ' ' + shareBy + ''); + $result.on('click', function() { + return false; + }); + return $result; + }); + } + } + return $tr; + }; } // use delegate to catch the case with multiple file lists $('#content').delegate('#fileList', 'fileActionsReady',function(ev){ - // if no share action exists because the admin disabled sharing for this user - // we create a share notification action to inform the user about files - // shared with him otherwise we just update the existing share action. var fileList = ev.fileList; - var $fileList = $(this); - $fileList.find('[data-share-owner]').each(function() { - var $tr = $(this); - var permissions = $tr.data('permissions'); - if(permissions & OC.PERMISSION_SHARE) { - OC.Share.markFileAsShared($tr, true); - } else { - // TODO: make this work like/with OC.Share.markFileAsShared() - var shareNotification = '' + - ' '; - $tr.find('.fileactions').append(function() { - var owner = $(this).closest('tr').attr('data-share-owner'); - var shareBy = t('files_sharing', 'Shared by {owner}', {owner: owner}); - var $result = $(shareNotification + ' ' + shareBy + ''); - $result.on('click', function() { - return false; - }); - return $result; - }); - } - }); - if (!OCA.Sharing.sharesLoaded){ OC.Share.loadIcons('file', fileList); // assume that we got all shares, so switching directories @@ -70,6 +75,7 @@ OCA.Sharing.sharesLoaded = true; } else{ + // this will update the icons for all the visible elements OC.Share.updateIcons('file', fileList); } }); diff --git a/apps/files_sharing/js/sharedfilelist.js b/apps/files_sharing/js/sharedfilelist.js index ecac5b33667..97fabf87131 100644 --- a/apps/files_sharing/js/sharedfilelist.js +++ b/apps/files_sharing/js/sharedfilelist.js @@ -38,6 +38,13 @@ } }, + _renderRow: function() { + // HACK: needed to call the overridden _renderRow + // this is because at the time this class is created + // the overriding hasn't been done yet... + return OCA.Files.FileList.prototype._renderRow.apply(this, arguments); + }, + _createRow: function(fileData) { // TODO: hook earlier and render the whole row here var $tr = OCA.Files.FileList.prototype._createRow.apply(this, arguments); diff --git a/apps/files_sharing/tests/js/shareSpec.js b/apps/files_sharing/tests/js/shareSpec.js index d2697beeb0b..1b75ad7a9fd 100644 --- a/apps/files_sharing/tests/js/shareSpec.js +++ b/apps/files_sharing/tests/js/shareSpec.js @@ -55,7 +55,7 @@ describe('OCA.Sharing.Util tests', function() { path: '/subdir', mimetype: 'text/plain', size: 12, - permissions: 31, + permissions: OC.PERMISSION_ALL, etag: 'abc', shareOwner: 'User One', isShareMountPoint: false @@ -87,7 +87,7 @@ describe('OCA.Sharing.Util tests', function() { path: '/subdir', mimetype: 'text/plain', size: 12, - permissions: 31, + permissions: OC.PERMISSION_ALL, etag: 'abc' }]); $tr = fileList.$el.find('tbody tr:first'); @@ -106,7 +106,7 @@ describe('OCA.Sharing.Util tests', function() { path: '/subdir', mimetype: 'text/plain', size: 12, - permissions: 31, + permissions: OC.PERMISSION_ALL, etag: 'abc' }]); $tr = fileList.$el.find('tbody tr:first'); @@ -127,7 +127,7 @@ describe('OCA.Sharing.Util tests', function() { path: '/subdir', mimetype: 'text/plain', size: 12, - permissions: 31, + permissions: OC.PERMISSION_ALL, etag: 'abc' }]); $tr = fileList.$el.find('tbody tr:first'); @@ -147,7 +147,7 @@ describe('OCA.Sharing.Util tests', function() { path: '/subdir', mimetype: 'text/plain', size: 12, - permissions: 31, + permissions: OC.PERMISSION_ALL, shareOwner: 'User One', etag: 'abc' }]); @@ -167,7 +167,7 @@ describe('OCA.Sharing.Util tests', function() { path: '/subdir', mimetype: 'text/plain', size: 12, - permissions: 31, + permissions: OC.PERMISSION_ALL, recipientsDisplayName: 'User One, User Two', etag: 'abc' }]); @@ -179,6 +179,28 @@ describe('OCA.Sharing.Util tests', function() { expect(OC.basename(getImageUrl($tr.find('.filename')))).toEqual('folder-shared.svg'); expect($action.find('img').length).toEqual(1); }); + it('shows static share text when file shared with user that has no share permission', function() { + var $action, $tr; + fileList.setFiles([{ + id: 1, + type: 'dir', + name: 'One', + path: '/subdir', + mimetype: 'text/plain', + size: 12, + permissions: OC.PERMISSION_CREATE, + etag: 'abc', + shareOwner: 'User One' + }]); + $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('Shared by User One'); + expect(OC.basename($action.find('img').attr('src'))).toEqual('share.svg'); + expect(OC.basename(getImageUrl($tr.find('.filename')))).toEqual('folder-shared.svg'); + expect($action.find('img').length).toEqual(1); + }); }); describe('Share action', function() { var showDropDownStub; @@ -201,7 +223,7 @@ describe('OCA.Sharing.Util tests', function() { path: '/subdir', mimetype: 'text/plain', size: 12, - permissions: 31, + permissions: OC.PERMISSION_ALL, etag: 'abc' }]); $action = fileList.$el.find('tbody tr:first .action-share'); @@ -237,7 +259,7 @@ describe('OCA.Sharing.Util tests', function() { path: '/subdir', mimetype: 'text/plain', size: 12, - permissions: 31, + permissions: OC.PERMISSION_ALL, etag: 'abc' }]); $action = fileList.$el.find('tbody tr:first .action-share'); @@ -273,7 +295,7 @@ describe('OCA.Sharing.Util tests', function() { path: '/subdir', mimetype: 'text/plain', size: 12, - permissions: 31, + permissions: OC.PERMISSION_ALL, etag: 'abc', recipients: 'User One, User Two' }]); @@ -296,6 +318,80 @@ describe('OCA.Sharing.Util tests', function() { OC.Share.updateIcon('file', 1); expect($action.hasClass('permanent')).toEqual(false); }); + it('keep share text after updating reshare', function() { + var $action, $tr; + OC.Share.statuses = {1: {link: false, path: '/subdir'}}; + fileList.setFiles([{ + id: 1, + type: 'file', + name: 'One.txt', + path: '/subdir', + mimetype: 'text/plain', + size: 12, + permissions: OC.PERMISSION_ALL, + etag: 'abc', + shareOwner: 'User One' + }]); + $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 itemShares = {}; + itemShares[OC.Share.SHARE_TYPE_USER] = ['User Two']; + OC.Share.itemShares = itemShares; + $('#dropdown').trigger(new $.Event('sharesChanged', {itemShares: itemShares})); + + 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()).toEqual('Shared by User One'); + expect(OC.basename($action.find('img').attr('src'))).toEqual('share.svg'); + }); + it('keep share text after unsharing reshare', function() { + var $action, $tr; + OC.Share.statuses = {1: {link: false, path: '/subdir'}}; + fileList.setFiles([{ + id: 1, + type: 'file', + name: 'One.txt', + path: '/subdir', + mimetype: 'text/plain', + size: 12, + permissions: OC.PERMISSION_ALL, + etag: 'abc', + shareOwner: 'User One', + recipients: 'User Two' + }]); + $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 itemShares = {}; + OC.Share.itemShares = itemShares; + $('#dropdown').trigger(new $.Event('sharesChanged', {itemShares: itemShares})); + + expect($tr.attr('data-share-recipients')).not.toBeDefined(); + + OC.Share.updateIcon('file', 1); + + expect($action.hasClass('permanent')).toEqual(true); + expect($action.find('>span').text()).toEqual('Shared by User One'); + expect(OC.basename($action.find('img').attr('src'))).toEqual('share.svg'); + }); }); describe('formatRecipients', function() { it('returns a single recipient when one passed', function() { diff --git a/core/js/share.js b/core/js/share.js index 4e12f35d19e..a46a17102fd 100644 --- a/core/js/share.js +++ b/core/js/share.js @@ -151,7 +151,7 @@ OC.Share={ var img = action.find('img'); var message; var recipients; - var owner; + var owner = $tr.attr('data-share-owner'); var shareFolderIcon; var image = OC.imagePath('core', 'actions/share'); // update folder icon @@ -168,16 +168,16 @@ OC.Share={ $tr.children('.filename').css('background-image', 'url(' + shareFolderIcon + ')'); } // update share action text / icon - if (hasShares) { + if (hasShares || owner) { recipients = $tr.attr('data-share-recipients'); - owner = $tr.attr('data-share-owner'); action.addClass('permanent'); message = t('core', 'Shared'); - if (owner && !recipients) { + // even if reshared, only show "Shared by" + if (owner) { message = t('files_sharing', 'Shared by {owner}', {owner: owner}); } - if (recipients) { + else if (recipients) { message = t('core', 'Shared with {recipients}', {recipients: recipients}); } action.html(' '+ message + '').prepend(img); -- cgit v1.2.3 From 07f1b263c99b67730fb054482eb241bfad280d57 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Wed, 4 Jun 2014 10:37:04 +0200 Subject: Use recipient display names when updating shares in the UI Since OC.Share didn't have any array containing the list of shares for the current file, OC.Share.currentShares has been introduced to contain the full share item structure instead of the reduced one OC.Share.itemShares. The event "sharesChanged" is now passing OC.Share.currentShares, which itself includes the display name to be displayed for the recipients in the action icon. --- apps/files_sharing/js/share.js | 10 ++++--- apps/files_sharing/tests/js/shareSpec.js | 48 ++++++++++++++++++-------------- core/js/share.js | 39 +++++++++++++++++++++++--- core/js/tests/specs/shareSpec.js | 40 +++++++++++--------------- 4 files changed, 85 insertions(+), 52 deletions(-) (limited to 'apps/files_sharing/tests/js') diff --git a/apps/files_sharing/js/share.js b/apps/files_sharing/js/share.js index 8a63593883b..e833612fb51 100644 --- a/apps/files_sharing/js/share.js +++ b/apps/files_sharing/js/share.js @@ -75,7 +75,9 @@ OCA.Sharing.sharesLoaded = true; } else{ - // this will update the icons for all the visible elements + // this will update the icons for all the currently visible elements + // additionally added elements when scrolling down will be + // updated in the _renderRow override OC.Share.updateIcons('file', fileList); } }); @@ -113,11 +115,11 @@ OC.Share.showDropDown(itemType, $tr.data('id'), appendTo, true, possiblePermissions, filename); } $('#dropdown').on('sharesChanged', function(ev) { + var recipients = _.pluck(ev.shares[OC.Share.SHARE_TYPE_USER], 'share_with_displayname'); + var groupRecipients = _.pluck(ev.shares[OC.Share.SHARE_TYPE_GROUP], 'share_with_displayname'); + recipients = recipients.concat(groupRecipients); // note: we only update the data attribute because updateIcon() // is called automatically after this event - var userShares = ev.itemShares[OC.Share.SHARE_TYPE_USER] || []; - var groupShares = ev.itemShares[OC.Share.SHARE_TYPE_GROUP] || []; - var recipients = _.union(userShares, groupShares); if (recipients.length) { $tr.attr('data-share-recipients', OCA.Sharing.Util.formatRecipients(recipients)); } diff --git a/apps/files_sharing/tests/js/shareSpec.js b/apps/files_sharing/tests/js/shareSpec.js index 1b75ad7a9fd..455addaccc0 100644 --- a/apps/files_sharing/tests/js/shareSpec.js +++ b/apps/files_sharing/tests/js/shareSpec.js @@ -71,6 +71,7 @@ describe('OCA.Sharing.Util tests', function() { delete OCA.Sharing.sharesLoaded; delete OC.Share.droppedDown; OC.Share.statuses = {}; + OC.Share.currentShares = {}; }); describe('Sharing data in table row', function() { @@ -205,6 +206,12 @@ describe('OCA.Sharing.Util tests', function() { describe('Share action', function() { var showDropDownStub; + function makeDummyShareItem(displayName) { + return { + share_with_displayname: displayName + }; + } + beforeEach(function() { showDropDownStub = sinon.stub(OC.Share, 'showDropDown', function() { $('#testArea').append($('')); @@ -236,11 +243,12 @@ describe('OCA.Sharing.Util tests', function() { expect(showDropDownStub.calledOnce).toEqual(true); // simulate what the dropdown does - var itemShares = {}; - itemShares[OC.Share.SHARE_TYPE_USER] = ['User One', 'User Two']; - itemShares[OC.Share.SHARE_TYPE_GROUP] = ['Group One', 'Group Two']; - OC.Share.itemShares = itemShares; - $('#dropdown').trigger(new $.Event('sharesChanged', {itemShares: itemShares})); + 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})); expect($tr.attr('data-share-recipients')).toEqual('Group One, Group Two, User One, User Two'); @@ -249,7 +257,7 @@ describe('OCA.Sharing.Util tests', function() { expect($action.find('>span').text()).toEqual('Shared with Group One, Group Two, User One, User Two'); expect(OC.basename($action.find('img').attr('src'))).toEqual('share.svg'); }); - it('removes share icon after unsharing a shared file', function() { + it('updates share icon after updating shares of a file', function() { var $action, $tr; OC.Share.statuses = {1: {link: false, path: '/subdir'}}; fileList.setFiles([{ @@ -272,10 +280,10 @@ describe('OCA.Sharing.Util tests', function() { expect(showDropDownStub.calledOnce).toEqual(true); // simulate what the dropdown does - var itemShares = {}; - itemShares[OC.Share.SHARE_TYPE_USER] = ['User One', 'User Two', 'User Three']; - OC.Share.itemShares = itemShares; - $('#dropdown').trigger(new $.Event('sharesChanged', {itemShares: itemShares})); + 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})); expect($tr.attr('data-share-recipients')).toEqual('User One, User Three, User Two'); @@ -285,7 +293,7 @@ describe('OCA.Sharing.Util tests', function() { expect($action.find('>span').text()).toEqual('Shared with User One, User Three, User Two'); expect(OC.basename($action.find('img').attr('src'))).toEqual('share.svg'); }); - it('updates share icon after modifying shares on a shared file', function() { + it('removes share icon after removing all shares from a file', function() { var $action, $tr; OC.Share.statuses = {1: {link: false, path: '/subdir'}}; fileList.setFiles([{ @@ -309,9 +317,8 @@ describe('OCA.Sharing.Util tests', function() { expect(showDropDownStub.calledOnce).toEqual(true); // simulate what the dropdown does - var itemShares = {}; - OC.Share.itemShares = itemShares; - $('#dropdown').trigger(new $.Event('sharesChanged', {itemShares: itemShares})); + OC.Share.itemShares = {}; + $('#dropdown').trigger(new $.Event('sharesChanged', {shares: {}})); expect($tr.attr('data-share-recipients')).not.toBeDefined(); @@ -342,10 +349,10 @@ describe('OCA.Sharing.Util tests', function() { expect(showDropDownStub.calledOnce).toEqual(true); // simulate what the dropdown does - var itemShares = {}; - itemShares[OC.Share.SHARE_TYPE_USER] = ['User Two']; - OC.Share.itemShares = itemShares; - $('#dropdown').trigger(new $.Event('sharesChanged', {itemShares: itemShares})); + 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})); expect($tr.attr('data-share-recipients')).toEqual('User Two'); @@ -380,9 +387,8 @@ describe('OCA.Sharing.Util tests', function() { expect(showDropDownStub.calledOnce).toEqual(true); // simulate what the dropdown does - var itemShares = {}; - OC.Share.itemShares = itemShares; - $('#dropdown').trigger(new $.Event('sharesChanged', {itemShares: itemShares})); + OC.Share.itemShares = {}; + $('#dropdown').trigger(new $.Event('sharesChanged', {shares: {}})); expect($tr.attr('data-share-recipients')).not.toBeDefined(); diff --git a/core/js/share.js b/core/js/share.js index a46a17102fd..a478b08cd6b 100644 --- a/core/js/share.js +++ b/core/js/share.js @@ -3,8 +3,25 @@ OC.Share={ SHARE_TYPE_GROUP:1, SHARE_TYPE_LINK:3, SHARE_TYPE_EMAIL:4, + /** + * @deprecated use OC.Share.currentShares instead + */ itemShares:[], + /** + * Full list of all share statuses + */ statuses:{}, + /** + * Shares for the currently selected file. + * (for which the dropdown is open) + * + * Key is item type and value is an array or + * shares of the given item type. + */ + currentShares: {}, + /** + * Whether the share dropdown is opened. + */ droppedDown:false, /** * Loads ALL share statuses from server, stores them in OC.Share.statuses then @@ -345,6 +362,7 @@ OC.Share={ dropDownEl = dropDownEl.appendTo(appendTo); // Reset item shares OC.Share.itemShares = []; + OC.Share.currentShares = {}; if (data.shares) { $.each(data.shares, function(index, share) { if (share.share_type == OC.Share.SHARE_TYPE_LINK) { @@ -418,7 +436,7 @@ OC.Share={ OC.Share.share(itemType, itemSource, shareType, shareWith, permissions, itemSourceName, expirationDate, function() { OC.Share.addShareWith(shareType, shareWith, selected.item.label, permissions, possiblePermissions); $('#shareWith').val(''); - $('#dropdown').trigger(new $.Event('sharesChanged', {itemShares: OC.Share.itemShares})); + $('#dropdown').trigger(new $.Event('sharesChanged', {shares: OC.Share.currentShares})); OC.Share.updateIcon(itemType, itemSource); }); return false; @@ -475,6 +493,7 @@ OC.Share={ $('#shareWith').focus(); }, hideDropDown:function(callback) { + OC.Share.currentShares = null; $('#dropdown').hide('blind', function() { OC.Share.droppedDown = false; $('#dropdown').remove(); @@ -487,6 +506,12 @@ 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 === 1) { shareWithDisplayName = shareWithDisplayName + " (" + t('core', 'group') + ')'; } @@ -565,6 +590,10 @@ OC.Share={ html.find('.cruds').before(showCrudsButton); } $('#expiration').show(); + if (!OC.Share.currentShares[shareType]) { + OC.Share.currentShares[shareType] = []; + } + OC.Share.currentShares[shareType].push(shareItem); } }, showLink:function(token, password, itemSource) { @@ -700,7 +729,9 @@ $(document).ready(function() { $li.remove(); var index = OC.Share.itemShares[shareType].indexOf(shareWith); OC.Share.itemShares[shareType].splice(index, 1); - $('#dropdown').trigger(new $.Event('sharesChanged', {itemShares: OC.Share.itemShares})); + // updated list of shares + OC.Share.currentShares[shareType].splice(index, 1); + $('#dropdown').trigger(new $.Event('sharesChanged', {shares: OC.Share.currentShares})); OC.Share.updateIcon(itemType, itemSource); if (typeof OC.Share.statuses[itemSource] === 'undefined') { $('#expiration').hide('blind'); @@ -759,7 +790,7 @@ $(document).ready(function() { if (oc_appconfig.core.enforcePasswordForPublicLink === false) { OC.Share.share(itemType, itemSource, OC.Share.SHARE_TYPE_LINK, '', OC.PERMISSION_READ, itemSourceName, expirationDate, function(data) { OC.Share.showLink(data.token, null, itemSource); - $('#dropdown').trigger(new $.Event('sharesChanged', {itemShares: OC.Share.itemShares})); + $('#dropdown').trigger(new $.Event('sharesChanged', {shares: OC.Share.currentShares})); OC.Share.updateIcon(itemType, itemSource); }); } else { @@ -772,7 +803,7 @@ $(document).ready(function() { if ($('#linkText').val() !== '') { OC.Share.unshare(itemType, itemSource, OC.Share.SHARE_TYPE_LINK, '', function() { OC.Share.itemShares[OC.Share.SHARE_TYPE_LINK] = false; - $('#dropdown').trigger(new $.Event('sharesChanged', {itemShares: OC.Share.itemShares})); + $('#dropdown').trigger(new $.Event('sharesChanged', {shares: OC.Share.currentShares})); OC.Share.updateIcon(itemType, itemSource); if (typeof OC.Share.statuses[itemSource] === 'undefined') { $('#expiration').hide('blind'); diff --git a/core/js/tests/specs/shareSpec.js b/core/js/tests/specs/shareSpec.js index a8d9225649c..458bc41b6a1 100644 --- a/core/js/tests/specs/shareSpec.js +++ b/core/js/tests/specs/shareSpec.js @@ -170,11 +170,11 @@ describe('OC.Share tests', function() { JSON.stringify({status: 'success'}) ); expect(handler.calledOnce).toEqual(true); - var itemShares = handler.getCall(0).args[0].itemShares; - expect(itemShares).toBeDefined(); - expect(itemShares[OC.Share.SHARE_TYPE_USER]).toEqual(['user1', 'user2']); - expect(itemShares[OC.Share.SHARE_TYPE_GROUP]).not.toBeDefined(); - expect(itemShares[OC.Share.SHARE_TYPE_LINK]).not.toBeDefined(); + 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(); @@ -184,11 +184,10 @@ describe('OC.Share tests', function() { JSON.stringify({status: 'success'}) ); expect(handler.calledOnce).toEqual(true); - var itemShares = handler.getCall(0).args[0].itemShares; - expect(itemShares).toBeDefined(); - expect(itemShares[OC.Share.SHARE_TYPE_USER]).toEqual([]); - expect(itemShares[OC.Share.SHARE_TYPE_GROUP]).not.toBeDefined(); - expect(itemShares[OC.Share.SHARE_TYPE_LINK]).not.toBeDefined(); + 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 @@ -199,11 +198,10 @@ describe('OC.Share tests', function() { JSON.stringify({status: 'success', data: { token: 'abc' }}) ); expect(handler.calledOnce).toEqual(true); - var itemShares = handler.getCall(0).args[0].itemShares; - expect(itemShares).toBeDefined(); - expect(itemShares[OC.Share.SHARE_TYPE_USER]).toEqual(['user1']); - expect(itemShares[OC.Share.SHARE_TYPE_GROUP]).not.toBeDefined(); - expect(itemShares[OC.Share.SHARE_TYPE_LINK]).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(); @@ -216,14 +214,10 @@ describe('OC.Share tests', function() { ); expect(handler.calledOnce).toEqual(true); - itemShares = handler.getCall(0).args[0].itemShares; - expect(itemShares).toBeDefined(); - expect(itemShares[OC.Share.SHARE_TYPE_USER]).toEqual(['user1']); - expect(itemShares[OC.Share.SHARE_TYPE_GROUP]).not.toBeDefined(); - // currently inconsistent, removing share with link sets it to false - // instead of delete - expect(itemShares[OC.Share.SHARE_TYPE_LINK]).toBeFalsy(); - //expect(itemShares[OC.Share.SHARE_TYPE_LINK]).not.toBeDefined(); + 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(); }); }); }); -- cgit v1.2.3