diff options
author | Vincent Petry <pvince81@owncloud.com> | 2014-05-21 12:54:34 +0200 |
---|---|---|
committer | Vincent Petry <pvince81@owncloud.com> | 2014-05-30 10:06:29 +0200 |
commit | 1d9129eac35b49a4e8d0d642a68d7d634f31c905 (patch) | |
tree | 513bdd46bb2e557bbac678aff73d1766b8e3c9c6 | |
parent | 6fbf4d8548133dff4419e5e2e0c649f49e177669 (diff) | |
download | nextcloud-server-1d9129eac35b49a4e8d0d642a68d7d634f31c905.tar.gz nextcloud-server-1d9129eac35b49a4e8d0d642a68d7d634f31c905.zip |
Sharing overview fixes and unit tests
- Fixed renaming and fileActionsReady event
- Added unit tests for shares list
- Fixed public page with defer
- Fixed file actions in sharing overview
- Fixed sharing counterpart list (10 entries max)
- Fixed file path attribute to be used in download action
- Fix sharing list headers
- OC.Share icons now operate on fileList instance
- Fix OC.Share.updateIcon when more than one list in DOM
-rw-r--r-- | apps/files/js/app.js | 8 | ||||
-rw-r--r-- | apps/files/js/fileactions.js | 15 | ||||
-rw-r--r-- | apps/files/js/filelist.js | 19 | ||||
-rw-r--r-- | apps/files/tests/js/fileactionsSpec.js | 29 | ||||
-rw-r--r-- | apps/files/tests/js/filelistSpec.js | 2 | ||||
-rw-r--r-- | apps/files_sharing/js/app.js | 29 | ||||
-rw-r--r-- | apps/files_sharing/js/public.js | 5 | ||||
-rw-r--r-- | apps/files_sharing/js/share.js | 9 | ||||
-rw-r--r-- | apps/files_sharing/js/sharedfilelist.js | 56 | ||||
-rw-r--r-- | apps/files_sharing/templates/list.php | 4 | ||||
-rw-r--r-- | apps/files_sharing/tests/js/appSpec.js | 140 | ||||
-rw-r--r-- | apps/files_sharing/tests/js/sharedfilelistSpec.js | 417 | ||||
-rw-r--r-- | core/js/share.js | 45 | ||||
-rw-r--r-- | tests/karma.config.js | 43 |
14 files changed, 751 insertions, 70 deletions
diff --git a/apps/files/js/app.js b/apps/files/js/app.js index 6ccf5135000..71802948a5c 100644 --- a/apps/files/js/app.js +++ b/apps/files/js/app.js @@ -73,6 +73,14 @@ }, /** + * Returns the view id of the currently active view + * @return view id + */ + getActiveView: function() { + return this.navigation.getActiveItem(); + }, + + /** * Setup events based on URL changes */ _setupEvents: function() { diff --git a/apps/files/js/fileactions.js b/apps/files/js/fileactions.js index 8cee037e294..3df62f37518 100644 --- a/apps/files/js/fileactions.js +++ b/apps/files/js/fileactions.js @@ -232,7 +232,7 @@ } if (triggerEvent){ - fileList.$fileList.trigger(jQuery.Event("fileActionsReady")); + fileList.$fileList.trigger(jQuery.Event("fileActionsReady", {fileList: fileList})); } }, getCurrentFile: function () { @@ -252,8 +252,6 @@ * Register the actions that are used by default for the files app. */ registerDefaultActions: function() { - // TODO: try to find a way to not make it depend on fileList, - // maybe get a handler or listener to trigger events on this.register('all', 'Delete', OC.PERMISSION_DELETE, function () { return OC.imagePath('core', 'actions/delete'); }, function (filename, context) { @@ -287,7 +285,8 @@ this.register(downloadScope, 'Download', OC.PERMISSION_READ, function () { return OC.imagePath('core', 'actions/download'); }, function (filename, context) { - var url = context.fileList.getDownloadUrl(filename, context.fileList.getCurrentDirectory()); + var dir = context.dir || context.fileList.getCurrentDirectory(); + var url = context.fileList.getDownloadUrl(filename, dir); if (url) { OC.redirect(url); } @@ -309,11 +308,13 @@ // through window.FileActions will be limited to the main file list. window.FileActions = OCA.Files.legacyFileActions; window.FileActions.register = function (mime, name, permissions, icon, action, displayName) { - console.warn('FileActions.register() is deprecated, please use OCA.Files.fileActions.register() instead'); - OCA.Files.FileActions.prototype.register.call(window.FileActions, mime, name, permissions, icon, action, displayName); + console.warn('FileActions.register() is deprecated, please use OCA.Files.fileActions.register() instead', arguments); + OCA.Files.FileActions.prototype.register.call( + window.FileActions, mime, name, permissions, icon, action, displayName + ); }; window.FileActions.setDefault = function (mime, name) { - console.warn('FileActions.setDefault() is deprecated, please use OCA.Files.fileActions.setDefault() instead'); + console.warn('FileActions.setDefault() is deprecated, please use OCA.Files.fileActions.setDefault() instead', mime, name); OCA.Files.FileActions.prototype.setDefault.call(window.FileActions, mime, name); }; })(); diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 52d4c8ba3fe..68b22207144 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -508,7 +508,7 @@ this.$el.find('thead').after(this.$fileList); this.updateEmptyContent(); - this.$fileList.trigger(jQuery.Event("fileActionsReady")); + this.$fileList.trigger($.Event('fileActionsReady', {fileList: this})); this.fileSummary.calculate(filesArray); @@ -530,7 +530,7 @@ type = fileData.type || 'file', mtime = parseInt(fileData.mtime, 10) || new Date().getTime(), mime = fileData.mimetype, - path = fileData.path || this.getCurrentDirectory(), + path = fileData.path, linkUrl; options = options || {}; @@ -550,6 +550,13 @@ "data-permissions": fileData.permissions || this.getDirectoryPermissions() }); + if (!_.isUndefined(path)) { + tr.attr('data-path', path); + } + else { + path = this.getCurrentDirectory(); + } + if (type === 'dir') { // use default folder icon icon = icon || OC.imagePath('core', 'filetypes/folder'); @@ -1224,16 +1231,16 @@ // reinsert row self.files.splice(tr.index(), 1); tr.remove(); - self.add(fileInfo, {updateSummary: false}); - self.$fileList.trigger($.Event('fileActionsReady')); + self.add(fileInfo, {updateSummary: false, silent: true}); + self.$fileList.trigger($.Event('fileActionsReady', {fileList: self})); } }); } else { // add back the old file info when cancelled self.files.splice(tr.index(), 1); tr.remove(); - self.add(oldFileInfo, {updateSummary: false}); - self.$fileList.trigger($.Event('fileActionsReady')); + self.add(oldFileInfo, {updateSummary: false, silent: true}); + self.$fileList.trigger($.Event('fileActionsReady', {fileList: self})); } } catch (error) { input.attr('title', error); diff --git a/apps/files/tests/js/fileactionsSpec.js b/apps/files/tests/js/fileactionsSpec.js index fa634da08a2..490594a1773 100644 --- a/apps/files/tests/js/fileactionsSpec.js +++ b/apps/files/tests/js/fileactionsSpec.js @@ -104,7 +104,34 @@ describe('OCA.Files.FileActions tests', function() { $tr.find('.action-download').click(); expect(redirectStub.calledOnce).toEqual(true); - expect(redirectStub.getCall(0).args[0]).toEqual(OC.webroot + '/index.php/apps/files/ajax/download.php?dir=%2Fsubdir&files=testName.txt'); + expect(redirectStub.getCall(0).args[0]).toEqual( + OC.webroot + + '/index.php/apps/files/ajax/download.php' + + '?dir=%2Fsubdir&files=testName.txt'); + redirectStub.restore(); + }); + it('takes the file\'s path into account when clicking download', function() { + var redirectStub = sinon.stub(OC, 'redirect'); + var fileData = { + id: 18, + type: 'file', + name: 'testName.txt', + path: '/anotherpath/there', + mimetype: 'text/plain', + size: '1234', + etag: 'a01234c', + mtime: '123456' + }; + var $tr = fileList.add(fileData); + FileActions.display($tr.find('td.filename'), true, fileList); + + $tr.find('.action-download').click(); + + expect(redirectStub.calledOnce).toEqual(true); + expect(redirectStub.getCall(0).args[0]).toEqual( + OC.webroot + '/index.php/apps/files/ajax/download.php' + + '?dir=%2Fanotherpath%2Fthere&files=testName.txt' + ); redirectStub.restore(); }); it('deletes file when clicking delete', function() { diff --git a/apps/files/tests/js/filelistSpec.js b/apps/files/tests/js/filelistSpec.js index a197fd5722c..3e9950dfe19 100644 --- a/apps/files/tests/js/filelistSpec.js +++ b/apps/files/tests/js/filelistSpec.js @@ -483,7 +483,7 @@ describe('OCA.Files.FileList tests', function() { var $input, request; for (var i = 0; i < testFiles.length; i++) { - fileList.add(testFiles[i]); + fileList.add(testFiles[i], {silent: true}); } // trigger rename prompt diff --git a/apps/files_sharing/js/app.js b/apps/files_sharing/js/app.js index 7a71684f1a1..3764328a5d0 100644 --- a/apps/files_sharing/js/app.js +++ b/apps/files_sharing/js/app.js @@ -16,7 +16,7 @@ OCA.Sharing.App = { initSharingIn: function($el) { if (this._inFileList) { - return; + return this._inFileList; } this._inFileList = new OCA.Sharing.FileList( @@ -31,11 +31,12 @@ OCA.Sharing.App = { this._extendFileList(this._inFileList); this._inFileList.appName = t('files_sharing', 'Shared with you'); this._inFileList.$el.find('#emptycontent').text(t('files_sharing', 'No files have been shared with you yet.')); + return this._inFileList; }, initSharingOut: function($el) { if (this._outFileList) { - return; + return this._outFileList; } this._outFileList = new OCA.Sharing.FileList( $el, @@ -49,6 +50,19 @@ OCA.Sharing.App = { this._extendFileList(this._outFileList); this._outFileList.appName = t('files_sharing', 'Shared with others'); this._outFileList.$el.find('#emptycontent').text(t('files_sharing', 'You haven\'t shared any files yet.')); + return this._outFileList; + }, + + removeSharingIn: function() { + if (this._inFileList) { + this._inFileList.$fileList.empty(); + } + }, + + removeSharingOut: function() { + if (this._outFileList) { + this._outFileList.$fileList.empty(); + } }, _createFileActions: function() { @@ -56,6 +70,7 @@ OCA.Sharing.App = { var fileActions = new OCA.Files.FileActions(); // note: not merging the legacy actions because legacy apps are not // compatible with the sharing overview and need to be adapted first + fileActions.registerDefaultActions(); fileActions.merge(OCA.Files.fileActions); // when the user clicks on a folder, redirect to the corresponding @@ -75,11 +90,17 @@ OCA.Sharing.App = { }; $(document).ready(function() { - $('#app-content-sharingin').one('show', function(e) { + $('#app-content-sharingin').on('show', function(e) { OCA.Sharing.App.initSharingIn($(e.target)); }); - $('#app-content-sharingout').one('show', function(e) { + $('#app-content-sharingin').on('hide', function() { + OCA.Sharing.App.removeSharingIn(); + }); + $('#app-content-sharingout').on('show', function(e) { OCA.Sharing.App.initSharingOut($(e.target)); }); + $('#app-content-sharingout').on('hide', function() { + OCA.Sharing.App.removeSharingOut(); + }); }); diff --git a/apps/files_sharing/js/public.js b/apps/files_sharing/js/public.js index 446f3f2442b..27e8d361ff9 100644 --- a/apps/files_sharing/js/public.js +++ b/apps/files_sharing/js/public.js @@ -166,7 +166,10 @@ OCA.Sharing.PublicApp = { $(document).ready(function() { var App = OCA.Sharing.PublicApp; - App.initialize($('#preview')); + // defer app init, to give a chance to plugins to register file actions + _.defer(function() { + App.initialize($('#preview')); + }); if (window.Files) { // HACK: for oc-dialogs previews that depends on Files: diff --git a/apps/files_sharing/js/share.js b/apps/files_sharing/js/share.js index 1fcb1f088bf..5a42604c866 100644 --- a/apps/files_sharing/js/share.js +++ b/apps/files_sharing/js/share.js @@ -28,10 +28,11 @@ $(document).ready(function() { } // use delegate to catch the case with multiple file lists - $('#content').delegate('#fileList', 'fileActionsReady',function(){ + $('#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); @@ -59,16 +60,16 @@ $(document).ready(function() { return $result; }); } - }) + }); if (!OCA.Sharing.sharesLoaded){ - OC.Share.loadIcons('file', $fileList); + 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); + OC.Share.updateIcons('file', fileList); } }); diff --git a/apps/files_sharing/js/sharedfilelist.js b/apps/files_sharing/js/sharedfilelist.js index 4508de47dcc..b941722d0cf 100644 --- a/apps/files_sharing/js/sharedfilelist.js +++ b/apps/files_sharing/js/sharedfilelist.js @@ -43,10 +43,9 @@ var $tr = OCA.Files.FileList.prototype._createRow.apply(this, arguments); $tr.find('.filesize').remove(); var $sharedWith = $('<td class="sharedWith"></td>') - .text(fileData.shareColumnInfo); + .text(fileData.counterParts.join(', ')); $tr.find('td.date').before($sharedWith); $tr.find('td.filename input:checkbox').remove(); - $tr.attr('data-path', fileData.path); $tr.attr('data-share-id', _.pluck(fileData.shares, 'id').join(',')); return $tr; }, @@ -74,7 +73,12 @@ }, getDirectoryPermissions: function() { - return OC.PERMISSION_READ; + return OC.PERMISSION_READ | OC.PERMISSION_DELETE; + }, + + updateStorageStatistics: function() { + // no op because it doesn't have + // storage info like free space / used space }, reload: function() { @@ -128,16 +132,16 @@ var self = this; // OCS API uses non-camelcased names var files = _.chain(data) - // cOnvert share data to file data + // convert share data to file data .map(function(share) { /* jshint camelcase: false */ var file = { id: share.file_source, - mtime: share.stime * 1000, mimetype: share.mimetype }; if (share.item_type === 'folder') { file.type = 'dir'; + file.mimetype = 'httpd/unix-directory'; } else { file.type = 'file'; @@ -150,7 +154,8 @@ file.share = { id: share.id, type: share.share_type, - target: share.share_with + target: share.share_with, + stime: share.stime * 1000, }; if (self._sharedWithUser) { file.share.ownerDisplayName = share.displayname_owner; @@ -173,28 +178,49 @@ // inside the same file object (by file id). .reduce(function(memo, file) { var data = memo[file.id]; + var counterPart = file.share.ownerDisplayName || file.share.targetDisplayName; if (!data) { data = memo[file.id] = file; data.shares = [file.share]; + // using a hash to make them unique, + // this is only a list to be displayed + data.counterParts = {}; + // counter is cheaper than calling _.keys().length + data.counterPartsCount = 0; + data.mtime = file.share.stime; } else { + // always take the most recent stime + if (file.share.stime > data.mtime) { + data.mtime = file.share.stime; + } data.shares.push(file.share); } - // format the share column info output string - if (!data.shareColumnInfo) { - data.shareColumnInfo = ''; - } - else { - data.shareColumnInfo += ', '; + + if (file.share.type === OC.Share.SHARE_TYPE_LINK) { + data.hasLinkShare = true; + } else if (counterPart && data.counterPartsCount < 10) { + // limit counterparts for output + data.counterParts[counterPart] = true; + data.counterPartsCount++; } - // TODO. more accurate detection of name based on type - // TODO: maybe better formatting, like "link + 3 users" when more than 1 user - data.shareColumnInfo += (file.share.ownerDisplayName || file.share.targetDisplayName || 'link'); + delete file.share; return memo; }, {}) // Retrieve only the values of the returned hash .values() + // Clean up + .each(function(data) { + // convert the counterParts map to a flat + // array of sorted names + data.counterParts = _.chain(data.counterParts).keys().sort().value(); + if (data.hasLinkShare) { + data.counterParts.unshift(t('files_sharing', 'link')); + delete data.hasLinkShare; + } + delete data.counterPartsCount; + }) // Sort by expected sort comparator .sortBy(this._sortComparator) // Finish the chain by getting the result diff --git a/apps/files_sharing/templates/list.php b/apps/files_sharing/templates/list.php index c688dcf8764..b07222cfe28 100644 --- a/apps/files_sharing/templates/list.php +++ b/apps/files_sharing/templates/list.php @@ -16,11 +16,11 @@ <a class="name sort columntitle" data-sort="name"><span><?php p($l->t( 'Name' )); ?></span><span class="sort-indicator"></span></a> </div> </th> - <th id="headerSharedWith" class="hidden column-mtime"> + <th id="headerSharedWith" class="hidden column-counterpart"> <a id="sharedwith" class="columntitle" data-sort="shareWith"><span><?php p($l->t( 'Shared with' )); ?></span><span class="sort-indicator"></span></a> </th> <th id="headerDate" class="hidden column-mtime"> - <a id="modified" class="columntitle" data-sort="mtime"><span><?php p($l->t( 'Shared since' )); ?></span><span class="sort-indicator"></span></a> + <a id="modified" class="columntitle" data-sort="mtime"><span><?php p($l->t( 'Share time' )); ?></span><span class="sort-indicator"></span></a> </th> </tr> </thead> diff --git a/apps/files_sharing/tests/js/appSpec.js b/apps/files_sharing/tests/js/appSpec.js new file mode 100644 index 00000000000..09c48a6305c --- /dev/null +++ b/apps/files_sharing/tests/js/appSpec.js @@ -0,0 +1,140 @@ +/** +* ownCloud +* +* @author Vincent Petry +* @copyright 2014 Vincent Petry <pvince81@owncloud.com> +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE +* License as published by the Free Software Foundation; either +* version 3 of the License, or any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU AFFERO GENERAL PUBLIC LICENSE for more details. +* +* You should have received a copy of the GNU Affero General Public +* License along with this library. If not, see <http://www.gnu.org/licenses/>. +* +*/ + +describe('OCA.Sharing.App tests', function() { + var App = OCA.Sharing.App; + var fileListIn; + var fileListOut; + + beforeEach(function() { + $('#testArea').append( + '<div id="app-navigation">' + + '<ul><li data-id="files"><a>Files</a></li>' + + '<li data-id="sharingin"><a></a></li>' + + '<li data-id="sharingout"><a></a></li>' + + '</ul></div>' + + '<div id="app-content">' + + '<div id="app-content-files" class="hidden">' + + '</div>' + + '<div id="app-content-sharingin" class="hidden">' + + '</div>' + + '<div id="app-content-sharingout" class="hidden">' + + '</div>' + + '</div>' + + '</div>' + ); + fileListIn = App.initSharingIn($('#app-content-sharingin')); + fileListOut = App.initSharingOut($('#app-content-sharingout')); + }); + afterEach(function() { + App._inFileList = null; + App._outFileList = null; + fileListIn = null; + fileListOut = null; + }); + + describe('initialization', function() { + it('inits sharing-in list on show', function() { + expect(fileListIn._sharedWithUser).toEqual(true); + }); + it('inits sharing-out list on show', function() { + expect(fileListOut._sharedWithUser).toBeFalsy(); + }); + }); + describe('file actions', function() { + it('provides default file actions', function() { + _.each([fileListIn, fileListOut], function(fileList) { + var fileActions = fileList.fileActions; + + expect(fileActions.actions.all).toBeDefined(); + expect(fileActions.actions.all.Delete).toBeDefined(); + expect(fileActions.actions.all.Rename).toBeDefined(); + expect(fileActions.actions.file.Download).toBeDefined(); + + expect(fileActions.defaults.dir).toEqual('Open'); + }); + }); + it('provides custom file actions', function() { + var actionStub = sinon.stub(); + // regular file action + OCA.Files.fileActions.register( + 'all', + 'RegularTest', + OC.PERMISSION_READ, + OC.imagePath('core', 'actions/shared'), + actionStub + ); + + App._inFileList = null; + fileListIn = App.initSharingIn($('#app-content-sharingin')); + + expect(fileListIn.fileActions.actions.all.RegularTest).toBeDefined(); + }); + it('does not provide legacy file actions', function() { + var actionStub = sinon.stub(); + // legacy file action + window.FileActions.register( + 'all', + 'LegacyTest', + OC.PERMISSION_READ, + OC.imagePath('core', 'actions/shared'), + actionStub + ); + + App._inFileList = null; + fileListIn = App.initSharingIn($('#app-content-sharingin')); + + expect(fileListIn.fileActions.actions.all.LegacyTest).not.toBeDefined(); + }); + it('redirects to files app when opening a directory', function() { + var oldList = OCA.Files.App.fileList; + // dummy new list to make sure it exists + OCA.Files.App.fileList = new OCA.Files.FileList($('<table><thead></thead><tbody></tbody></table>')); + + var setActiveViewStub = sinon.stub(OCA.Files.App, 'setActiveView'); + // create dummy table so we can click the dom + var $table = '<table><thead></thead><tbody id="fileList"></tbody></table>'; + $('#app-content-sharingin').append($table); + + App._inFileList = null; + fileListIn = App.initSharingIn($('#app-content-sharingin')); + + fileListIn.setFiles([{ + name: 'testdir', + type: 'dir', + path: '/somewhere/inside/subdir', + counterParts: ['user2'] + }]); + + fileListIn.findFileEl('testdir').find('td a.name').click(); + + expect(OCA.Files.App.fileList.getCurrentDirectory()).toEqual('/somewhere/inside/subdir/testdir'); + + expect(setActiveViewStub.calledOnce).toEqual(true); + expect(setActiveViewStub.calledWith('files')).toEqual(true); + + setActiveViewStub.restore(); + + // restore old list + OCA.Files.App.fileList = oldList; + }); + }); +}); diff --git a/apps/files_sharing/tests/js/sharedfilelistSpec.js b/apps/files_sharing/tests/js/sharedfilelistSpec.js new file mode 100644 index 00000000000..ddcd746afe0 --- /dev/null +++ b/apps/files_sharing/tests/js/sharedfilelistSpec.js @@ -0,0 +1,417 @@ +/* + * Copyright (c) 2014 Vincent Petry <pvince81@owncloud.com> + * + * This file is licensed under the Affero General Public License version 3 + * or later. + * + * See the COPYING-README file. + * + */ + +describe('OCA.Sharing.FileList tests', function() { + var testFiles, alertStub, notificationStub, fileList; + + beforeEach(function() { + alertStub = sinon.stub(OC.dialogs, 'alert'); + notificationStub = sinon.stub(OC.Notification, 'show'); + + // init parameters and test table elements + $('#testArea').append( + '<div id="app-content-container">' + + // init horrible parameters + '<input type="hidden" id="dir" value="/"></input>' + + '<input type="hidden" id="permissions" value="31"></input>' + + // dummy controls + '<div id="controls">' + + ' <div class="actions creatable"></div>' + + ' <div class="notCreatable"></div>' + + '</div>' + + // dummy table + // TODO: at some point this will be rendered by the fileList class itself! + '<table id="filestable">' + + '<thead><tr>' + + '<th id="headerName" class="hidden column-name">' + + '<input type="checkbox" id="select_all_files" class="select-all">' + + '<a class="name columntitle" data-sort="name"><span>Name</span><span class="sort-indicator"></span></a>' + + '<span class="selectedActions hidden">' + + '</th>' + + '<th class="hidden column-mtime">' + + '<a class="columntitle" data-sort="mtime"><span class="sort-indicator"></span></a>' + + '</th>' + + '</tr></thead>' + + '<tbody id="fileList"></tbody>' + + '<tfoot></tfoot>' + + '</table>' + + '<div id="emptycontent">Empty content message</div>' + + '</div>' + ); + }); + afterEach(function() { + testFiles = undefined; + fileList = undefined; + + notificationStub.restore(); + alertStub.restore(); + }); + + describe('loading file list for incoming shares', function() { + var ocsResponse; + + beforeEach(function() { + fileList = new OCA.Sharing.FileList( + $('#app-content-container'), { + sharedWithUser: true + } + ); + + fileList.reload(); + + /* jshint camelcase: false */ + ocsResponse = { + ocs: { + meta: { + status: 'ok', + statuscode: 100, + message: null + }, + data: [{ + id: 7, + item_type: 'file', + item_source: 49, + item_target: '/49', + file_source: 49, + file_target: '/local path/local name.txt', + path: 'files/something shared.txt', + permissions: 31, + stime: 11111, + share_type: OC.Share.SHARE_TYPE_USER, + share_with: 'user1', + share_with_displayname: 'User One', + mimetype: 'text/plain', + uid_owner: 'user2', + displayname_owner: 'User Two' + }] + } + }; + }); + it('render file shares', function() { + var request; + + expect(fakeServer.requests.length).toEqual(1); + request = fakeServer.requests[0]; + expect(request.url).toEqual( + OC.linkToOCS('apps/files_sharing/api/v1') + + 'shares?format=json&shared_with_me=true' + ); + + fakeServer.requests[0].respond( + 200, + { 'Content-Type': 'application/json' }, + JSON.stringify(ocsResponse) + ); + + var $rows = fileList.$el.find('tbody tr'); + var $tr = $rows.eq(0); + expect($rows.length).toEqual(1); + expect($tr.attr('data-id')).toEqual('49'); + expect($tr.attr('data-type')).toEqual('file'); + expect($tr.attr('data-file')).toEqual('local name.txt'); + expect($tr.attr('data-path')).toEqual('/local path'); + expect($tr.attr('data-size')).not.toBeDefined(); + expect($tr.attr('data-permissions')).toEqual('31'); // read and delete + expect($tr.attr('data-mime')).toEqual('text/plain'); + expect($tr.attr('data-mtime')).toEqual('11111000'); + expect($tr.attr('data-share-id')).toEqual('7'); + expect($tr.find('a.name').attr('href')).toEqual( + OC.webroot + + '/index.php/apps/files/ajax/download.php' + + '?dir=%2Flocal%20path&files=local%20name.txt' + ); + expect($tr.find('td.sharedWith').text()).toEqual('User Two'); + + expect($tr.find('.nametext').text().trim()).toEqual('local name.txt'); + }); + it('render folder shares', function() { + /* jshint camelcase: false */ + var request; + ocsResponse.ocs.data[0] = _.extend(ocsResponse.ocs.data[0], { + item_type: 'folder', + file_target: '/local path/local name', + path: 'files/something shared', + }); + + expect(fakeServer.requests.length).toEqual(1); + request = fakeServer.requests[0]; + expect(request.url).toEqual( + OC.linkToOCS('apps/files_sharing/api/v1') + + 'shares?format=json&shared_with_me=true' + ); + + fakeServer.requests[0].respond( + 200, + { 'Content-Type': 'application/json' }, + JSON.stringify(ocsResponse) + ); + + var $rows = fileList.$el.find('tbody tr'); + var $tr = $rows.eq(0); + expect($rows.length).toEqual(1); + expect($tr.attr('data-id')).toEqual('49'); + expect($tr.attr('data-type')).toEqual('dir'); + expect($tr.attr('data-file')).toEqual('local name'); + expect($tr.attr('data-path')).toEqual('/local path'); + expect($tr.attr('data-size')).not.toBeDefined(); + expect($tr.attr('data-permissions')).toEqual('31'); // read and delete + expect($tr.attr('data-mime')).toEqual('httpd/unix-directory'); + expect($tr.attr('data-mtime')).toEqual('11111000'); + expect($tr.attr('data-share-id')).toEqual('7'); + expect($tr.find('a.name').attr('href')).toEqual( + OC.webroot + + '/index.php/apps/files' + + '?dir=/local%20path/local%20name' + ); + expect($tr.find('td.sharedWith').text()).toEqual('User Two'); + + expect($tr.find('.nametext').text().trim()).toEqual('local name'); + }); + }); + describe('loading file list for outgoing shares', function() { + var ocsResponse; + + beforeEach(function() { + fileList = new OCA.Sharing.FileList( + $('#app-content-container'), { + sharedWithUser: false + } + ); + + fileList.reload(); + + /* jshint camelcase: false */ + ocsResponse = { + ocs: { + meta: { + status: 'ok', + statuscode: 100, + message: null + }, + data: [{ + id: 7, + item_type: 'file', + item_source: 49, + file_source: 49, + path: '/local path/local name.txt', + permissions: 27, + stime: 11111, + share_type: OC.Share.SHARE_TYPE_USER, + share_with: 'user2', + share_with_displayname: 'User Two', + mimetype: 'text/plain', + uid_owner: 'user1', + displayname_owner: 'User One' + }] + } + }; + }); + it('render file shares', function() { + var request; + + expect(fakeServer.requests.length).toEqual(1); + request = fakeServer.requests[0]; + expect(request.url).toEqual( + OC.linkToOCS('apps/files_sharing/api/v1') + + 'shares?format=json&shared_with_me=false' + ); + + fakeServer.requests[0].respond( + 200, + { 'Content-Type': 'application/json' }, + JSON.stringify(ocsResponse) + ); + + var $rows = fileList.$el.find('tbody tr'); + var $tr = $rows.eq(0); + expect($rows.length).toEqual(1); + expect($tr.attr('data-id')).toEqual('49'); + expect($tr.attr('data-type')).toEqual('file'); + expect($tr.attr('data-file')).toEqual('local name.txt'); + expect($tr.attr('data-path')).toEqual('/local path'); + expect($tr.attr('data-size')).not.toBeDefined(); + expect($tr.attr('data-permissions')).toEqual('31'); // read and delete + expect($tr.attr('data-mime')).toEqual('text/plain'); + expect($tr.attr('data-mtime')).toEqual('11111000'); + expect($tr.attr('data-share-id')).toEqual('7'); + expect($tr.find('a.name').attr('href')).toEqual( + OC.webroot + + '/index.php/apps/files/ajax/download.php' + + '?dir=%2Flocal%20path&files=local%20name.txt' + ); + expect($tr.find('td.sharedWith').text()).toEqual('User Two'); + + expect($tr.find('.nametext').text().trim()).toEqual('local name.txt'); + }); + it('render folder shares', function() { + var request; + /* jshint camelcase: false */ + ocsResponse.ocs.data[0] = _.extend(ocsResponse.ocs.data[0], { + item_type: 'folder', + path: '/local path/local name', + }); + + expect(fakeServer.requests.length).toEqual(1); + request = fakeServer.requests[0]; + expect(request.url).toEqual( + OC.linkToOCS('apps/files_sharing/api/v1') + + 'shares?format=json&shared_with_me=false' + ); + + fakeServer.requests[0].respond( + 200, + { 'Content-Type': 'application/json' }, + JSON.stringify(ocsResponse) + ); + + var $rows = fileList.$el.find('tbody tr'); + var $tr = $rows.eq(0); + expect($rows.length).toEqual(1); + expect($tr.attr('data-id')).toEqual('49'); + expect($tr.attr('data-type')).toEqual('dir'); + expect($tr.attr('data-file')).toEqual('local name'); + expect($tr.attr('data-path')).toEqual('/local path'); + expect($tr.attr('data-size')).not.toBeDefined(); + expect($tr.attr('data-permissions')).toEqual('31'); // read and delete + expect($tr.attr('data-mime')).toEqual('httpd/unix-directory'); + expect($tr.attr('data-mtime')).toEqual('11111000'); + expect($tr.attr('data-share-id')).toEqual('7'); + expect($tr.find('a.name').attr('href')).toEqual( + OC.webroot + + '/index.php/apps/files' + + '?dir=/local%20path/local%20name' + ); + expect($tr.find('td.sharedWith').text()).toEqual('User Two'); + + expect($tr.find('.nametext').text().trim()).toEqual('local name'); + }); + it('render link shares', function() { + /* jshint camelcase: false */ + var request; + ocsResponse.ocs.data[0] = { + id: 7, + item_type: 'file', + item_source: 49, + file_source: 49, + path: '/local path/local name.txt', + permissions: 1, + stime: 11111, + share_type: OC.Share.SHARE_TYPE_LINK, + share_with: null, + token: 'abc', + mimetype: 'text/plain', + uid_owner: 'user1', + displayname_owner: 'User One' + }; + expect(fakeServer.requests.length).toEqual(1); + request = fakeServer.requests[0]; + expect(request.url).toEqual( + OC.linkToOCS('apps/files_sharing/api/v1') + + 'shares?format=json&shared_with_me=false' + ); + + fakeServer.requests[0].respond( + 200, + { 'Content-Type': 'application/json' }, + JSON.stringify(ocsResponse) + ); + + var $rows = fileList.$el.find('tbody tr'); + var $tr = $rows.eq(0); + expect($rows.length).toEqual(1); + expect($tr.attr('data-id')).toEqual('49'); + expect($tr.attr('data-type')).toEqual('file'); + expect($tr.attr('data-file')).toEqual('local name.txt'); + expect($tr.attr('data-path')).toEqual('/local path'); + expect($tr.attr('data-size')).not.toBeDefined(); + expect($tr.attr('data-permissions')).toEqual('31'); // read and delete + expect($tr.attr('data-mime')).toEqual('text/plain'); + expect($tr.attr('data-mtime')).toEqual('11111000'); + expect($tr.attr('data-share-id')).toEqual('7'); + expect($tr.find('a.name').attr('href')).toEqual( + OC.webroot + + '/index.php/apps/files/ajax/download.php' + + '?dir=%2Flocal%20path&files=local%20name.txt'); + expect($tr.find('td.sharedWith').text()).toEqual('link'); + + expect($tr.find('.nametext').text().trim()).toEqual('local name.txt'); + }); + it('groups link shares with regular shares', function() { + /* jshint camelcase: false */ + var request; + // link share + ocsResponse.ocs.data.push({ + id: 8, + item_type: 'file', + item_source: 49, + file_source: 49, + path: '/local path/local name.txt', + permissions: 1, + stime: 11111, + share_type: OC.Share.SHARE_TYPE_LINK, + share_with: null, + token: 'abc', + mimetype: 'text/plain', + uid_owner: 'user1', + displayname_owner: 'User One' + }); + // another share of the same file + ocsResponse.ocs.data.push({ + id: 9, + item_type: 'file', + item_source: 49, + file_source: 49, + path: '/local path/local name.txt', + permissions: 27, + stime: 22222, + share_type: OC.Share.SHARE_TYPE_USER, + share_with: 'user3', + share_with_displayname: 'User Three', + mimetype: 'text/plain', + uid_owner: 'user1', + displayname_owner: 'User One' + }); + expect(fakeServer.requests.length).toEqual(1); + request = fakeServer.requests[0]; + expect(request.url).toEqual( + OC.linkToOCS('apps/files_sharing/api/v1') + + 'shares?format=json&shared_with_me=false' + ); + + fakeServer.requests[0].respond( + 200, + { 'Content-Type': 'application/json' }, + JSON.stringify(ocsResponse) + ); + + var $rows = fileList.$el.find('tbody tr'); + var $tr = $rows.eq(0); + expect($rows.length).toEqual(1); + expect($tr.attr('data-id')).toEqual('49'); + expect($tr.attr('data-type')).toEqual('file'); + expect($tr.attr('data-file')).toEqual('local name.txt'); + expect($tr.attr('data-path')).toEqual('/local path'); + expect($tr.attr('data-size')).not.toBeDefined(); + expect($tr.attr('data-permissions')).toEqual('31'); // read and delete + expect($tr.attr('data-mime')).toEqual('text/plain'); + // always use the most recent stime + expect($tr.attr('data-mtime')).toEqual('22222000'); + expect($tr.attr('data-share-id')).toEqual('7,8,9'); + expect($tr.find('a.name').attr('href')).toEqual( + OC.webroot + + '/index.php/apps/files/ajax/download.php' + + '?dir=%2Flocal%20path&files=local%20name.txt' + ); + expect($tr.find('td.sharedWith').text()).toEqual('link, User Three, User Two'); + + expect($tr.find('.nametext').text().trim()).toEqual('local name.txt'); + }); + }); +}); diff --git a/core/js/share.js b/core/js/share.js index 279b1d11663..894f0d488f4 100644 --- a/core/js/share.js +++ b/core/js/share.js @@ -32,27 +32,26 @@ OC.Share={ * (not reloaded from server) * * @param itemType item type - * @param fileList file list instance or file list jQuery element, + * @param fileList file list instance * defaults to OCA.Files.App.fileList */ updateIcons:function(itemType, fileList){ var item; - var $fileList = (fileList || OCA.Files.App.fileList); - // in case the jQuery element was passed instead - if ($fileList.$fileList) { - $fileList = $fileList.$fileList; - } + fileList = fileList || OCA.Files.App.fileList; + var $fileList = fileList.$fileList; + var currentDir = fileList.getCurrentDirectory(); for (item in OC.Share.statuses){ + var image; var data = OC.Share.statuses[item]; - var hasLink = data['link']; + var hasLink = data.link; // Links override shared in terms of icon display if (hasLink) { - var image = OC.imagePath('core', 'actions/public'); + image = OC.imagePath('core', 'actions/public'); } else { - var image = OC.imagePath('core', 'actions/shared'); + image = OC.imagePath('core', 'actions/shared'); } - if (itemType != 'file' && itemType != 'folder') { + if (itemType !== 'file' && itemType !== 'folder') { $fileList.find('a.share[data-item="'+item+'"]').css('background', 'url('+image+') no-repeat center'); } else { var file = $fileList.find('tr[data-id="'+item+'"]'); @@ -62,17 +61,17 @@ OC.Share={ action.addClass('permanent'); action.html(' <span>'+t('core', 'Shared')+'</span>').prepend(img); } else { - var dir = $('#dir').val(); + var dir = currentDir; if (dir.length > 1) { var last = ''; var path = dir; // Search for possible parent folders that are shared while (path != last) { - if (path == data['path'] && !data['link']) { + if (path === data.path && !data.link) { var actions = $fileList.find('.fileactions .action[data-action="Share"]'); $.each(actions, function(index, action) { var img = $(action).find('img'); - if (img.attr('src') != OC.imagePath('core', 'actions/public')) { + if (img.attr('src') !== OC.imagePath('core', 'actions/public')) { img.attr('src', image); $(action).addClass('permanent'); $(action).html(' <span>'+t('core', 'Shared')+'</span>').prepend(img); @@ -112,14 +111,18 @@ OC.Share={ var file = $('tr').filterAttr('data-id', String(itemSource)); if (file.length > 0) { var action = $(file).find('.fileactions .action').filterAttr('data-action', 'Share'); - var img = action.find('img').attr('src', image); - if (shares) { - action.addClass('permanent'); - action.html(' <span>'+ escapeHTML(t('core', 'Shared'))+'</span>').prepend(img); - } else { - action.removeClass('permanent'); - action.html(' <span>'+ escapeHTML(t('core', 'Share'))+'</span>').prepend(img); - } + // in case of multiple lists/rows, there might be more than one visible + action.each(function() { + var action = $(this); + var img = action.find('img').attr('src', image); + if (shares) { + action.addClass('permanent'); + action.html(' <span>'+ escapeHTML(t('core', 'Shared'))+'</span>').prepend(img); + } else { + action.removeClass('permanent'); + action.html(' <span>'+ escapeHTML(t('core', 'Share'))+'</span>').prepend(img); + } + }); } } if (shares) { diff --git a/tests/karma.config.js b/tests/karma.config.js index 08b49d854e0..846e8f7be91 100644 --- a/tests/karma.config.js +++ b/tests/karma.config.js @@ -43,7 +43,19 @@ module.exports = function(config) { return apps; */ // other apps tests don't run yet... needs further research / clean up - return ['files', 'files_trashbin']; + return [ + 'files', + 'files_trashbin', + { + name: 'files_sharing', + srcFiles: [ + // only test these files, others are not ready and mess + // up with the global namespace/classes/state + 'apps/files_sharing/js/app.js', + 'apps/files_sharing/js/sharedfilelist.js' + ], + testFiles: ['apps/files_sharing/tests/js/*.js'] + }]; } // respect NOCOVERAGE env variable @@ -110,15 +122,30 @@ module.exports = function(config) { files.push(corePath + 'tests/specs/*.js'); } - for ( var i = 0; i < appsToTest.length; i++ ) { - // add app JS - var srcFile = 'apps/' + appsToTest[i] + '/js/*.js'; - files.push(srcFile); + function addApp(app) { + // if only a string was specified, expand to structure + if (typeof(app) === 'string') { + app = { + srcFiles: 'apps/' + app + '/js/*.js', + testFiles: 'apps/' + app + '/tests/js/*.js' + }; + } + + // add source files/patterns + files = files.concat(app.srcFiles || []); + // add test files/patterns + files = files.concat(app.testFiles || []); if (enableCoverage) { - preprocessors[srcFile] = 'coverage'; + // add coverage entry for each file/pattern + for (var i = 0; i < app.srcFiles.length; i++) { + preprocessors[app.srcFiles[i]] = 'coverage'; + } } - // add test specs - files.push('apps/' + appsToTest[i] + '/tests/js/*.js'); + } + + // add source files for apps to test + for ( var i = 0; i < appsToTest.length; i++ ) { + addApp(appsToTest[i]); } // serve images to avoid warnings |