From dbbb6c5945f9c4d73695fa210be88d35ff35f026 Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Mon, 5 May 2014 15:02:49 +0200 Subject: [PATCH 01/21] extend OCS Share API to allow the user to ask for files shared with him --- apps/files_sharing/lib/api.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/apps/files_sharing/lib/api.php b/apps/files_sharing/lib/api.php index 21fd5d00a4c..10b3f76db8f 100644 --- a/apps/files_sharing/lib/api.php +++ b/apps/files_sharing/lib/api.php @@ -31,6 +31,9 @@ class Api { * @return \OC_OCS_Result share information */ public static function getAllShares($params) { + if (isset($_GET['shared_with_me']) && $_GET['shared_with_me'] !== 'false') { + return self::getFilesSharedWithMe(); + } // if a file is specified, get the share for this file if (isset($_GET['path'])) { $params['itemSource'] = self::getFileId($_GET['path']); @@ -195,6 +198,22 @@ class Api { return new \OC_OCS_Result($result); } + /** + * get files shared with the user + * @return \OC_OCS_Result + */ + private static function getFilesSharedWithMe() { + try { + $shares = \OCP\Share::getItemsSharedWith('file'); + $result = new \OC_OCS_Result($shares); + } catch (\Exception $e) { + $result = new \OC_OCS_Result(null, 403, $e->getMessage()); + } + + return $result; + + } + /** * create a new share * @param array $params From 6ebc43650554f41eee2ae715b99a178b9c75c532 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Wed, 30 Apr 2014 17:42:35 +0200 Subject: [PATCH 02/21] Added sharing overview page (WIP) - added sharing overview entries in the sidebar - use OCS Share API to get the list of files --- apps/files/js/filelist.js | 10 +- apps/files_sharing/appinfo/app.php | 20 +++ apps/files_sharing/css/sharedfilelist.css | 3 + apps/files_sharing/js/app.js | 61 +++++++ apps/files_sharing/js/sharedfilelist.js | 190 ++++++++++++++++++++++ apps/files_sharing/list.php | 11 ++ apps/files_sharing/templates/list.php | 43 +++++ core/js/js.js | 11 +- 8 files changed, 344 insertions(+), 5 deletions(-) create mode 100644 apps/files_sharing/css/sharedfilelist.css create mode 100644 apps/files_sharing/js/app.js create mode 100644 apps/files_sharing/js/sharedfilelist.js create mode 100644 apps/files_sharing/list.php create mode 100644 apps/files_sharing/templates/list.php diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 72e1a688041..7887bdcda55 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -515,6 +515,7 @@ type = fileData.type || 'file', mtime = parseInt(fileData.mtime, 10) || new Date().getTime(), mime = fileData.mimetype, + path = fileData.path || this.getCurrentDirectory(), linkUrl; options = options || {}; @@ -550,10 +551,10 @@ // linkUrl if (type === 'dir') { - linkUrl = this.linkTo(this.getCurrentDirectory() + '/' + name); + linkUrl = this.linkTo(path + '/' + name); } else { - linkUrl = this.getDownloadUrl(name, this.getCurrentDirectory()); + linkUrl = this.getDownloadUrl(name, path); } td.append(''); @@ -693,6 +694,7 @@ options = options || {}; var type = fileData.type || 'file', mime = fileData.mimetype, + path = fileData.path || this.getCurrentDirectory(), permissions = parseInt(fileData.permissions, 10) || 0; if (fileData.isShareMountPoint) { @@ -729,7 +731,7 @@ // lazy load / newly inserted td ? if (!fileData.icon) { this.lazyLoadPreview({ - path: this.getCurrentDirectory() + '/' + fileData.name, + path: path + '/' + fileData.name, mime: mime, etag: fileData.etag, callback: function(url) { @@ -740,7 +742,7 @@ else { // set the preview URL directly var urlSpec = { - file: this.getCurrentDirectory() + '/' + fileData.name, + file: path + '/' + fileData.name, c: fileData.etag }; var previewUrl = this.generatePreviewUrl(urlSpec); diff --git a/apps/files_sharing/appinfo/app.php b/apps/files_sharing/appinfo/app.php index fa43f33721c..1756fc2f50e 100644 --- a/apps/files_sharing/appinfo/app.php +++ b/apps/files_sharing/appinfo/app.php @@ -1,4 +1,5 @@ add( + array( + "id" => 'sharingin', + "appname" => 'files_sharing', + "script" => 'list.php', + "order" => 3, + "name" => $l->t('Shared with you') + ) +); +\OCA\Files\App::getNavigationManager()->add( + array( + "id" => 'sharingout', + "appname" => 'files_sharing', + "script" => 'list.php', + "order" => 4, + "name" => $l->t('Shared with others') + ) +); diff --git a/apps/files_sharing/css/sharedfilelist.css b/apps/files_sharing/css/sharedfilelist.css new file mode 100644 index 00000000000..6b0c7d2226e --- /dev/null +++ b/apps/files_sharing/css/sharedfilelist.css @@ -0,0 +1,3 @@ +#filestable.shareList .summary .filesize { + display: none; +} diff --git a/apps/files_sharing/js/app.js b/apps/files_sharing/js/app.js new file mode 100644 index 00000000000..887575193d0 --- /dev/null +++ b/apps/files_sharing/js/app.js @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2014 Vincent Petry + * + * This file is licensed under the Affero General Public License version 3 + * or later. + * + * See the COPYING-README file. + * + */ + +OCA.Sharing = {}; +OCA.Sharing.App = { + + _inFileList: null, + _outFileList: null, + + initSharingIn: function($el) { + if (this._inFileList) { + return; + } + + this._inFileList = new OCA.Sharing.FileList( + $el, + { + scrollContainer: $('#app-content'), + sharedWithUser: true + } + ); + + var fileActions = _.extend({}, OCA.Files.FileActions); + fileActions.registerDefaultActions(this._inFileList); + this._inFileList.setFileActions(fileActions); + }, + + initSharingOut: function($el) { + if (this._outFileList) { + return; + } + this._outFileList = new OCA.Sharing.FileList( + $el, + { + scrollContainer: $('#app-content'), + sharedWithUser: false + } + ); + + var fileActions = _.extend({}, OCA.Files.FileActions); + fileActions.registerDefaultActions(this._outFileList); + this._outFileList.setFileActions(fileActions); + } +}; + +$(document).ready(function() { + $('#app-content-sharingin').one('show', function(e) { + OCA.Sharing.App.initSharingIn($(e.target)); + }); + $('#app-content-sharingout').one('show', function(e) { + OCA.Sharing.App.initSharingOut($(e.target)); + }); +}); + diff --git a/apps/files_sharing/js/sharedfilelist.js b/apps/files_sharing/js/sharedfilelist.js new file mode 100644 index 00000000000..7db4f629501 --- /dev/null +++ b/apps/files_sharing/js/sharedfilelist.js @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2014 Vincent Petry + * + * This file is licensed under the Affero General Public License version 3 + * or later. + * + * See the COPYING-README file. + * + */ +(function() { + + /** + * Sharing file list + * + * Contains both "shared with others" and "shared with you" modes. + */ + var FileList = function($el, options) { + this.initialize($el, options); + }; + + FileList.prototype = _.extend({}, OCA.Files.FileList.prototype, { + appName: 'Shares', + + SHARE_TYPE_TEXT: [ + t('files_sharing', 'User'), + t('files_sharing', 'Group'), + t('files_sharing', 'Unknown'), + t('files_sharing', 'Public') + ], + + /** + * Whether the list shows the files shared with the user (true) or + * the files that the user shared with others (false). + */ + _sharedWithUser: false, + + initialize: function($el, options) { + OCA.Files.FileList.prototype.initialize.apply(this, arguments); + if (this.initialized) { + return; + } + + if (options && options.sharedWithUser) { + this._sharedWithUser = true; + } + }, + + /** + * Compare two shares + * @param share1 first share + * @param share2 second share + * @return 1 if share2 should come before share1, -1 + * if share1 should come before share2, 0 if they + * are identical. + */ + _shareCompare: function(share1, share2) { + var result = OCA.Files.FileList.Comparators.name(share1, share2); + if (result === 0) { + return share2.shareType - share1.shareType; + } + return result; + }, + + _createRow: function(fileData) { + // TODO: hook earlier and render the whole row here + var $tr = OCA.Files.FileList.prototype._createRow.apply(this, arguments); + $tr.find('.filesize').remove(); + var $sharedWith = $('').text(fileData.shareWithDisplayName); + var $shareType = $('').text(this.SHARE_TYPE_TEXT[fileData.shareType] || + t('files_sharing', 'Unkown')); + $tr.find('td.date').before($sharedWith).before($shareType); + $tr.find('td.filename input:checkbox').remove(); + $tr.attr('data-path', fileData.path); + return $tr; + }, + + /** + * Set whether the list should contain outgoing shares + * or incoming shares. + * + * @param state true for incoming shares, false otherwise + */ + setSharedWithUser: function(state) { + this._sharedWithUser = !!state; + }, + + reload: function() { + var self = this; + this.showMask(); + if (this._reloadCall) { + this._reloadCall.abort(); + } + this._reloadCall = $.ajax({ + url: OC.linkToOCS('apps/files_sharing/api/v1') + 'shares', + /* jshint camelcase: false */ + data: { + format: 'json', + shared_with_me: !!this._sharedWithUser + }, + type: 'GET', + beforeSend: function(xhr) { + xhr.setRequestHeader('OCS-APIREQUEST', 'true'); + }, + error: function(result) { + self.reloadCallback(result); + }, + success: function(result) { + self.reloadCallback(result); + } + }); + }, + + reloadCallback: function(result) { + delete this._reloadCall; + this.hideMask(); + + this.$el.find('#headerSharedWith').text( + t('files_sharing', this._sharedWithUser ? 'Shared by' : 'Shared with') + ); + if (result.ocs && result.ocs.data) { + this.setFiles(this._makeFilesFromShares(result.ocs.data)); + } + else { + // TODO: error handling + } + }, + + render: function() { + // FIXME + /* + var $el = $('' + + '' + t('files', 'Name') + '' + + '' + t('files', 'Shared with') + '' + + '' + t('files', 'Type') + '' + + '' + t('files', 'Shared since') + '' + + '' + + '' + + ''); + this.$el.empty().append($el); + this.$fileList = this.$el.find('tbody'); + */ + }, + + /** + * Converts the OCS API share response data to a file info + * list + * @param OCS API share array + * @return array of file info maps + */ + _makeFilesFromShares: function(data) { + var self = this; + // OCS API uses non-camelcased names + /* jshint camelcase: false */ + var files = _.map(data, function(share) { + var file = { + id: share.id, + mtime: share.stime * 1000, + permissions: share.permissions + }; + if (share.item_type === 'folder') { + file.type = 'dir'; + } + else { + file.type = 'file'; + // force preview retrieval as we don't have mime types, + // the preview endpoint will fall back to the mime type + // icon if no preview exists + file.isPreviewAvailable = true; + file.icon = true; + } + file.shareType = share.share_type; + file.shareWith = share.share_with; + if (self._sharedWithUser) { + file.shareWithDisplayName = share.displayname_owner; + file.name = OC.basename(share.file_target); + file.path = OC.dirname(share.file_target); + } + else { + file.shareWithDisplayName = share.share_with_displayname; + file.name = OC.basename(share.path); + file.path = OC.dirname(share.path); + } + return file; + }); + return files.sort(this._shareCompare); + } + }); + + OCA.Sharing.FileList = FileList; +})(); diff --git a/apps/files_sharing/list.php b/apps/files_sharing/list.php new file mode 100644 index 00000000000..bad690ea95f --- /dev/null +++ b/apps/files_sharing/list.php @@ -0,0 +1,11 @@ +printPage(); diff --git a/apps/files_sharing/templates/list.php b/apps/files_sharing/templates/list.php new file mode 100644 index 00000000000..276dc9da775 --- /dev/null +++ b/apps/files_sharing/templates/list.php @@ -0,0 +1,43 @@ + +
+
+
+
+ + + + + + + + + + + + + + + + + + +
diff --git a/core/js/js.js b/core/js/js.js index 3c3efc469bf..44b0a1a39af 100644 --- a/core/js/js.js +++ b/core/js/js.js @@ -211,7 +211,16 @@ var OC={ linkToRemote:function(service) { return window.location.protocol + '//' + window.location.host + OC.linkToRemoteBase(service); }, - + + /** + * Gets the base path for the given OCS API service. + * @param {string} service name + * @return {string} OCS API base path + */ + linkToOCS: function(service) { + return window.location.protocol + '//' + window.location.host + OC.webroot + '/ocs/v1.php/' + service + '/'; + }, + /** * Generates the absolute url for the given relative url, which can contain parameters. * @param {string} url From 9baf47c2b457e497914e6ecfe96efb7d92530079 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Thu, 15 May 2014 19:51:15 +0200 Subject: [PATCH 03/21] Group shares by type --- apps/files_sharing/js/sharedfilelist.js | 151 ++++++++++++------------ apps/files_sharing/templates/list.php | 3 - 2 files changed, 75 insertions(+), 79 deletions(-) diff --git a/apps/files_sharing/js/sharedfilelist.js b/apps/files_sharing/js/sharedfilelist.js index 7db4f629501..b2a05f585bb 100644 --- a/apps/files_sharing/js/sharedfilelist.js +++ b/apps/files_sharing/js/sharedfilelist.js @@ -21,13 +21,6 @@ FileList.prototype = _.extend({}, OCA.Files.FileList.prototype, { appName: 'Shares', - SHARE_TYPE_TEXT: [ - t('files_sharing', 'User'), - t('files_sharing', 'Group'), - t('files_sharing', 'Unknown'), - t('files_sharing', 'Public') - ], - /** * Whether the list shows the files shared with the user (true) or * the files that the user shared with others (false). @@ -45,30 +38,13 @@ } }, - /** - * Compare two shares - * @param share1 first share - * @param share2 second share - * @return 1 if share2 should come before share1, -1 - * if share1 should come before share2, 0 if they - * are identical. - */ - _shareCompare: function(share1, share2) { - var result = OCA.Files.FileList.Comparators.name(share1, share2); - if (result === 0) { - return share2.shareType - share1.shareType; - } - return result; - }, - _createRow: function(fileData) { // TODO: hook earlier and render the whole row here var $tr = OCA.Files.FileList.prototype._createRow.apply(this, arguments); $tr.find('.filesize').remove(); - var $sharedWith = $('').text(fileData.shareWithDisplayName); - var $shareType = $('').text(this.SHARE_TYPE_TEXT[fileData.shareType] || - t('files_sharing', 'Unkown')); - $tr.find('td.date').before($sharedWith).before($shareType); + var $sharedWith = $('') + .text(fileData.shareColumnInfo); + $tr.find('td.date').before($sharedWith); $tr.find('td.filename input:checkbox').remove(); $tr.attr('data-path', fileData.path); return $tr; @@ -125,22 +101,6 @@ } }, - render: function() { - // FIXME - /* - var $el = $('' + - '' + t('files', 'Name') + '' + - '' + t('files', 'Shared with') + '' + - '' + t('files', 'Type') + '' + - '' + t('files', 'Shared since') + '' + - '' + - '' + - ''); - this.$el.empty().append($el); - this.$fileList = this.$el.find('tbody'); - */ - }, - /** * Converts the OCS API share response data to a file info * list @@ -150,39 +110,78 @@ _makeFilesFromShares: function(data) { var self = this; // OCS API uses non-camelcased names - /* jshint camelcase: false */ - var files = _.map(data, function(share) { - var file = { - id: share.id, - mtime: share.stime * 1000, - permissions: share.permissions - }; - if (share.item_type === 'folder') { - file.type = 'dir'; - } - else { - file.type = 'file'; - // force preview retrieval as we don't have mime types, - // the preview endpoint will fall back to the mime type - // icon if no preview exists - file.isPreviewAvailable = true; - file.icon = true; - } - file.shareType = share.share_type; - file.shareWith = share.share_with; - if (self._sharedWithUser) { - file.shareWithDisplayName = share.displayname_owner; - file.name = OC.basename(share.file_target); - file.path = OC.dirname(share.file_target); - } - else { - file.shareWithDisplayName = share.share_with_displayname; - file.name = OC.basename(share.path); - file.path = OC.dirname(share.path); - } - return file; - }); - return files.sort(this._shareCompare); + var files = _.chain(data) + // cOnvert share data to file data + .map(function(share) { + /* jshint camelcase: false */ + var file = { + id: share.file_source, + mtime: share.stime * 1000, + permissions: share.permissions + }; + if (share.item_type === 'folder') { + file.type = 'dir'; + } + else { + file.type = 'file'; + // force preview retrieval as we don't have mime types, + // the preview endpoint will fall back to the mime type + // icon if no preview exists + file.isPreviewAvailable = true; + file.icon = true; + } + file.share = { + id: share.id, + type: share.share_type, + target: share.share_with + }; + if (self._sharedWithUser) { + file.share.ownerDisplayName = share.displayname_owner; + file.name = OC.basename(share.file_target); + file.path = OC.dirname(share.file_target); + } + else { + file.share.targetDisplayName = share.share_with_displayname; + file.name = OC.basename(share.path); + file.path = OC.dirname(share.path); + } + return file; + }) + // Group all files and have a "shares" array with + // the share info for each file. + // + // This uses a hash memo to cumulate share information + // inside the same file object (by file id). + .reduce(function(memo, file) { + var data = memo[file.id]; + if (!data) { + data = memo[file.id] = file; + data.shares = [file.share]; + } + else { + data.shares.push(file.share); + } + // format the share column info output string + if (!data.shareColumnInfo) { + data.shareColumnInfo = ''; + } + else { + data.shareColumnInfo += ', '; + } + // 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() + // Sort by expected sort comparator + .sortBy(this._sortComparator) + // Finish the chain by getting the result + .value(); + + return files; } }); diff --git a/apps/files_sharing/templates/list.php b/apps/files_sharing/templates/list.php index 276dc9da775..acc9ffbb2c4 100644 --- a/apps/files_sharing/templates/list.php +++ b/apps/files_sharing/templates/list.php @@ -28,9 +28,6 @@ t( 'Shared with' )); ?> - - t( 'Type' )); ?> - t( 'Shared since' )); ?> From 279ede33af09c43e03f97458c7fa673ff2b7982a Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Mon, 19 May 2014 13:18:44 +0200 Subject: [PATCH 04/21] Improved FileActions with context A context hash is now passed to file action handlers which makes it possible to have file list specific file actions. --- apps/files/js/fileactions.js | 13 +++++++++++-- apps/files/js/filelist.js | 8 ++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/apps/files/js/fileactions.js b/apps/files/js/fileactions.js index 085195e961d..9e4aeb2f338 100644 --- a/apps/files/js/fileactions.js +++ b/apps/files/js/fileactions.js @@ -98,8 +98,13 @@ * @param parent "td" element of the file for which to display actions * @param triggerEvent if true, triggers the fileActionsReady on the file * list afterwards (false by default) + * @param fileList OCA.Files.FileList instance on which the action is + * done, defaults to OCA.Files.App.fileList */ - display: function (parent, triggerEvent) { + display: function (parent, triggerEvent, fileList) { + if (!fileList) { + console.warn('FileActions.display() MUST be called with a OCA.Files.FileList instance'); + } this.currentFile = parent; var self = this; var actions = this.getActions(this.getCurrentMimeType(), this.getCurrentType(), this.getCurrentPermissions()); @@ -122,7 +127,11 @@ self.currentFile = event.data.elem; var file = self.getCurrentFile(); - event.data.actionFunc(file); + event.data.actionFunc(file, { + $file: $(this).closest('tr'), + fileList: fileList || OCA.Files.App.fileList, + fileActions: self + }); }; var addAction = function (name, action, displayName) { diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 7887bdcda55..08f640f6bff 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -248,7 +248,11 @@ var action = this.fileActions.getDefault(mime,type, permissions); if (action) { event.preventDefault(); - action(filename); + action(filename, { + $file: $tr, + fileList: this, + fileActions: this.fileActions + }); } } } @@ -725,7 +729,7 @@ } // display actions - this.fileActions.display(filenameTd, false); + this.fileActions.display(filenameTd, false, this); if (fileData.isPreviewAvailable) { // lazy load / newly inserted td ? From 60bcdc550e95fbf0104bbc9c8028777016d73663 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Mon, 19 May 2014 15:20:44 +0200 Subject: [PATCH 05/21] Fixed file actions for sharing views FileActions can now be clone to be use for separate file list views without having the side-effect of affecting the main file list view. Added "Open" action in sharing overview file lists to redirect to the regular file list when clicking on a folder. --- apps/files/js/app.js | 12 ++++++-- apps/files/js/fileactions.js | 34 ++++++++++++++-------- apps/files/js/filelist.js | 11 ++++--- apps/files_sharing/js/app.js | 19 +++++++++---- apps/files_sharing/js/share.js | 38 ++++++++++++++----------- apps/files_sharing/js/sharedfilelist.js | 17 +++++++++++ apps/files_trashbin/js/app.js | 6 ++-- core/js/share.js | 24 ++++++++++++---- 8 files changed, 112 insertions(+), 49 deletions(-) diff --git a/apps/files/js/app.js b/apps/files/js/app.js index 9155fb38cdb..6d8a9788d97 100644 --- a/apps/files/js/app.js +++ b/apps/files/js/app.js @@ -25,7 +25,7 @@ this.navigation = new OCA.Files.Navigation($('#app-navigation')); // TODO: ideally these should be in a separate class / app (the embedded "all files" app) - this.fileActions = OCA.Files.FileActions; + this.fileActions = OCA.Files.FileActions.clone(); this.files = OCA.Files.Files; this.fileList = new OCA.Files.FileList( @@ -36,7 +36,7 @@ } ); this.files.initialize(); - this.fileActions.registerDefaultActions(this.fileList); + this.fileActions.registerDefaultActions(); this.fileList.setFileActions(this.fileActions); // for backward compatibility, the global FileList will @@ -57,6 +57,14 @@ return this.navigation.getActiveContainer(); }, + /** + * Sets the currently active view + * @param viewId view id + */ + setActiveView: function(viewId, options) { + this.navigation.setActiveItem(viewId, options); + }, + /** * Setup events based on URL changes */ diff --git a/apps/files/js/fileactions.js b/apps/files/js/fileactions.js index 9e4aeb2f338..1242fea7f99 100644 --- a/apps/files/js/fileactions.js +++ b/apps/files/js/fileactions.js @@ -31,6 +31,18 @@ this.actions[mime][name]['displayName'] = displayName; this.icons[name] = icon; }, + /** + * Clones the current file actions handler including the already + * registered actions. + */ + clone: function() { + var fileActions = _.extend({}, this); + // need to deep copy the actions as well + fileActions.actions = _.extend({}, this.actions); + fileActions.defaults = _.extend({}, this.defaults); + //fileActions.icons = _.extend({}, this.icons); + return fileActions; + }, clear: function() { this.actions = {}; this.defaults = {}; @@ -217,29 +229,29 @@ /** * Register the actions that are used by default for the files app. */ - registerDefaultActions: function(fileList) { + 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) { - fileList.do_delete(filename); + }, function (filename, context) { + context.fileList.do_delete(filename); $('.tipsy').remove(); }); // t('files', 'Rename') this.register('all', 'Rename', OC.PERMISSION_UPDATE, function () { return OC.imagePath('core', 'actions/rename'); - }, function (filename) { - fileList.rename(filename); + }, function (filename, context) { + context.fileList.rename(filename); }); - this.register('dir', 'Open', OC.PERMISSION_READ, '', function (filename) { - var dir = fileList.getCurrentDirectory(); + this.register('dir', 'Open', OC.PERMISSION_READ, '', function (filename, context) { + var dir = context.fileList.getCurrentDirectory(); if (dir !== '/') { dir = dir + '/'; } - fileList.changeDirectory(dir + filename); + context.fileList.changeDirectory(dir + filename); }); this.setDefault('dir', 'Open'); @@ -252,14 +264,12 @@ this.register(downloadScope, 'Download', OC.PERMISSION_READ, function () { return OC.imagePath('core', 'actions/download'); - }, function (filename) { - var url = fileList.getDownloadUrl(filename, fileList.getCurrentDirectory()); + }, function (filename, context) { + var url = context.fileList.getDownloadUrl(filename, context.fileList.getCurrentDirectory()); if (url) { OC.redirect(url); } }); - - fileList.$fileList.trigger(jQuery.Event("fileActionsReady")); } }; diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index 08f640f6bff..d969cb57c56 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -452,7 +452,7 @@ while (count > 0 && index < this.files.length) { fileData = this.files[index]; - tr = this._renderRow(fileData, {updateSummary: false}); + tr = this._renderRow(fileData, {updateSummary: false, silent: true}); this.$fileList.append(tr); if (isAllSelected || this._selectedFiles[fileData.id]) { tr.addClass('selected'); @@ -626,7 +626,8 @@ * * @param fileData map of file attributes * @param options map of attributes: - * - "updateSummary" true to update the summary after adding (default), false otherwise + * - "updateSummary": true to update the summary after adding (default), false otherwise + * - "silent": true to prevent firing events like "fileActionsReady" * @return new tr element (not appended to the table) */ add: function(fileData, options) { @@ -729,7 +730,7 @@ } // display actions - this.fileActions.display(filenameTd, false, this); + this.fileActions.display(filenameTd, !options.silent, this); if (fileData.isPreviewAvailable) { // lazy load / newly inserted td ? @@ -790,7 +791,9 @@ }, /** - * Sets the file actions handler + * Sets the file actions handler. + * + * @param fileActions FileActions handler */ setFileActions: function(fileActions) { this.fileActions = fileActions; diff --git a/apps/files_sharing/js/app.js b/apps/files_sharing/js/app.js index 887575193d0..9808e069784 100644 --- a/apps/files_sharing/js/app.js +++ b/apps/files_sharing/js/app.js @@ -27,9 +27,7 @@ OCA.Sharing.App = { } ); - var fileActions = _.extend({}, OCA.Files.FileActions); - fileActions.registerDefaultActions(this._inFileList); - this._inFileList.setFileActions(fileActions); + this._initFileActions(this._inFileList); }, initSharingOut: function($el) { @@ -44,9 +42,18 @@ OCA.Sharing.App = { } ); - var fileActions = _.extend({}, OCA.Files.FileActions); - fileActions.registerDefaultActions(this._outFileList); - this._outFileList.setFileActions(fileActions); + this._initFileActions(this._outFileList); + }, + + _initFileActions: function(fileList) { + var fileActions = OCA.Files.FileActions.clone(); + // when the user clicks on a folder, redirect to the corresponding + // folder in the files app + fileActions.register('dir', 'Open', OC.PERMISSION_READ, '', function (filename, context) { + OCA.Files.App.setActiveView('files', {silent: true}); + OCA.Files.App.fileList.changeDirectory(context.$file.attr('data-path') + '/' + filename, true, true); + }); + fileList.setFileActions(fileActions); } }; diff --git a/apps/files_sharing/js/share.js b/apps/files_sharing/js/share.js index 84c5bf57b38..28586a179a4 100644 --- a/apps/files_sharing/js/share.js +++ b/apps/files_sharing/js/share.js @@ -8,7 +8,6 @@ * */ -/* global FileList, FileActions */ $(document).ready(function() { var sharesLoaded = false; @@ -31,7 +30,8 @@ $(document).ready(function() { }; } - $('#fileList').on('fileActionsReady',function(){ + // use delegate to catch the case with multiple file lists + $('#content').delegate('#fileList', 'fileActionsReady',function(){ // 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. @@ -64,44 +64,48 @@ $(document).ready(function() { } }) - // FIXME: these calls are also working on hard-coded - // list selectors... if (!sharesLoaded){ - OC.Share.loadIcons('file'); + OC.Share.loadIcons('file', $fileList); // assume that we got all shares, so switching directories // will not invalidate that list sharesLoaded = true; } else{ - OC.Share.updateIcons('file'); + OC.Share.updateIcons('file', $fileList); } }); - FileActions.register('all', 'Share', OC.PERMISSION_SHARE, OC.imagePath('core', 'actions/share'), function(filename) { - var tr = FileList.findFileEl(filename); + OCA.Files.FileActions.register( + 'all', + 'Share', + OC.PERMISSION_SHARE, + OC.imagePath('core', 'actions/share'), + function(filename, context) { + + var $tr = context.$file; var itemType = 'file'; - if ($(tr).data('type') == 'dir') { + if ($tr.data('type') === 'dir') { itemType = 'folder'; } - var possiblePermissions = $(tr).data('reshare-permissions'); + var possiblePermissions = $tr.data('reshare-permissions'); if (_.isUndefined(possiblePermissions)) { - possiblePermissions = $(tr).data('permissions'); + possiblePermissions = $tr.data('permissions'); } - var appendTo = $(tr).find('td.filename'); + 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')) { + 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); + $tr.addClass('mouseOver'); + OC.Share.showDropDown(itemType, $tr.data('id'), appendTo, true, possiblePermissions, filename); }); } else { OC.Share.hideDropDown(); } } else { - $(tr).addClass('mouseOver'); - OC.Share.showDropDown(itemType, $(tr).data('id'), appendTo, true, possiblePermissions, filename); + $tr.addClass('mouseOver'); + OC.Share.showDropDown(itemType, $tr.data('id'), appendTo, true, possiblePermissions, filename); } }); } diff --git a/apps/files_sharing/js/sharedfilelist.js b/apps/files_sharing/js/sharedfilelist.js index b2a05f585bb..9d8c9e38358 100644 --- a/apps/files_sharing/js/sharedfilelist.js +++ b/apps/files_sharing/js/sharedfilelist.js @@ -47,6 +47,7 @@ $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; }, @@ -60,6 +61,22 @@ this._sharedWithUser = !!state; }, + updateEmptyContent: function() { + var dir = this.getCurrentDirectory(); + if (dir === '/') { + // root has special permissions + this.$el.find('#emptycontent').toggleClass('hidden', !this.isEmpty); + this.$el.find('#filestable thead th').toggleClass('hidden', this.isEmpty); + } + else { + OCA.Files.FileList.prototype.updateEmptyContent.apply(this, arguments); + } + }, + + getDirectoryPermissions: function() { + return OC.PERMISSION_READ; + }, + reload: function() { var self = this; this.showMask(); diff --git a/apps/files_trashbin/js/app.js b/apps/files_trashbin/js/app.js index aa499ae1791..cf3fb1d0d16 100644 --- a/apps/files_trashbin/js/app.js +++ b/apps/files_trashbin/js/app.js @@ -39,7 +39,8 @@ OCA.Trashbin.App = { fileActions.setDefault('dir', 'Open'); - fileActions.register('all', 'Restore', OC.PERMISSION_READ, OC.imagePath('core', 'actions/history'), function(filename) { + fileActions.register('all', 'Restore', OC.PERMISSION_READ, OC.imagePath('core', 'actions/history'), function(filename, context) { + var fileList = context.fileList; var tr = fileList.findFileEl(filename); var deleteAction = tr.children("td.date").children(".action.delete"); deleteAction.removeClass('delete-icon').addClass('progress-icon'); @@ -54,7 +55,8 @@ OCA.Trashbin.App = { fileActions.register('all', 'Delete', OC.PERMISSION_READ, function() { return OC.imagePath('core', 'actions/delete'); - }, function(filename) { + }, function(filename, context) { + var fileList = context.fileList; $('.tipsy').remove(); var tr = fileList.findFileEl(filename); var deleteAction = tr.children("td.date").children(".action.delete"); diff --git a/core/js/share.js b/core/js/share.js index d013f257579..279b1d11663 100644 --- a/core/js/share.js +++ b/core/js/share.js @@ -10,8 +10,11 @@ OC.Share={ * Loads ALL share statuses from server, stores them in OC.Share.statuses then * calls OC.Share.updateIcons() to update the files "Share" icon to "Shared" * according to their share status and share type. + * + * @param itemType item type + * @param fileList file list instance, defaults to OCA.Files.App.fileList */ - loadIcons:function(itemType) { + loadIcons:function(itemType, fileList) { // Load all share icons $.get(OC.filePath('core', 'ajax', 'share.php'), { fetch: 'getItemsSharedStatuses', itemType: itemType }, function(result) { if (result && result.status === 'success') { @@ -19,7 +22,7 @@ OC.Share={ $.each(result.data, function(item, data) { OC.Share.statuses[item] = data; }); - OC.Share.updateIcons(itemType); + OC.Share.updateIcons(itemType, fileList); } }); }, @@ -27,9 +30,18 @@ OC.Share={ * Updates the files' "Share" icons according to the known * sharing states stored in OC.Share.statuses. * (not reloaded from server) + * + * @param itemType item type + * @param fileList file list instance or file list jQuery element, + * defaults to OCA.Files.App.fileList */ - updateIcons:function(itemType){ + 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; + } for (item in OC.Share.statuses){ var data = OC.Share.statuses[item]; @@ -41,9 +53,9 @@ OC.Share={ var image = OC.imagePath('core', 'actions/shared'); } if (itemType != 'file' && itemType != 'folder') { - $('a.share[data-item="'+item+'"]').css('background', 'url('+image+') no-repeat center'); + $fileList.find('a.share[data-item="'+item+'"]').css('background', 'url('+image+') no-repeat center'); } else { - var file = $('tr[data-id="'+item+'"]'); + var file = $fileList.find('tr[data-id="'+item+'"]'); if (file.length > 0) { var action = $(file).find('.fileactions .action[data-action="Share"]'); var img = action.find('img').attr('src', image); @@ -57,7 +69,7 @@ OC.Share={ // Search for possible parent folders that are shared while (path != last) { if (path == data['path'] && !data['link']) { - var actions = $('.fileactions .action[data-action="Share"]'); + 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')) { From 85648def56e7ddea8c2c334a5399d0e5090d5208 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Mon, 19 May 2014 18:33:43 +0200 Subject: [PATCH 06/21] Fixed sharing permissions --- apps/files_sharing/js/sharedfilelist.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/files_sharing/js/sharedfilelist.js b/apps/files_sharing/js/sharedfilelist.js index 9d8c9e38358..f3a6d669760 100644 --- a/apps/files_sharing/js/sharedfilelist.js +++ b/apps/files_sharing/js/sharedfilelist.js @@ -133,8 +133,7 @@ /* jshint camelcase: false */ var file = { id: share.file_source, - mtime: share.stime * 1000, - permissions: share.permissions + mtime: share.stime * 1000 }; if (share.item_type === 'folder') { file.type = 'dir'; @@ -156,11 +155,13 @@ file.share.ownerDisplayName = share.displayname_owner; file.name = OC.basename(share.file_target); file.path = OC.dirname(share.file_target); + file.permissions = share.permissions; } else { file.share.targetDisplayName = share.share_with_displayname; file.name = OC.basename(share.path); file.path = OC.dirname(share.path); + file.permissions = OC.PERMISSION_ALL; } return file; }) From 0f105ec747dbe67cb7663d6e62aa33192a1d3fe5 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Mon, 19 May 2014 18:38:41 +0200 Subject: [PATCH 07/21] Removed selection checkboxes --- apps/files_sharing/templates/list.php | 9 --------- 1 file changed, 9 deletions(-) diff --git a/apps/files_sharing/templates/list.php b/apps/files_sharing/templates/list.php index acc9ffbb2c4..6671e674089 100644 --- a/apps/files_sharing/templates/list.php +++ b/apps/files_sharing/templates/list.php @@ -13,16 +13,7 @@ From 8bdce6698cb6a9e71d7bab72e649b63cb9f60bd8 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Mon, 19 May 2014 18:47:14 +0200 Subject: [PATCH 08/21] Added unit test for file action context argument --- apps/files/tests/js/fileactionsSpec.js | 36 +++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/apps/files/tests/js/fileactionsSpec.js b/apps/files/tests/js/fileactionsSpec.js index 9152dbb58c3..519a31b3fce 100644 --- a/apps/files/tests/js/fileactionsSpec.js +++ b/apps/files/tests/js/fileactionsSpec.js @@ -78,8 +78,8 @@ describe('OCA.Files.FileActions tests', function() { }; var $tr = fileList.add(fileData); - FileActions.display($tr.find('td.filename'), true); - FileActions.display($tr.find('td.filename'), true); + FileActions.display($tr.find('td.filename'), true, fileList); + FileActions.display($tr.find('td.filename'), true, fileList); // actions defined after cal expect($tr.find('.action.action-download').length).toEqual(1); @@ -98,7 +98,7 @@ describe('OCA.Files.FileActions tests', function() { mtime: '123456' }; var $tr = fileList.add(fileData); - FileActions.display($tr.find('td.filename'), true); + FileActions.display($tr.find('td.filename'), true, fileList); $tr.find('.action-download').click(); @@ -118,11 +118,39 @@ describe('OCA.Files.FileActions tests', function() { mtime: '123456' }; var $tr = fileList.add(fileData); - FileActions.display($tr.find('td.filename'), true); + FileActions.display($tr.find('td.filename'), true, fileList); $tr.find('.action.delete').click(); expect(deleteStub.calledOnce).toEqual(true); deleteStub.restore(); }); + it('passes context to action handler', function() { + var actionStub = sinon.stub(); + var fileData = { + id: 18, + type: 'file', + name: 'testName.txt', + mimetype: 'text/plain', + size: '1234', + etag: 'a01234c', + mtime: '123456' + }; + var $tr = fileList.add(fileData); + FileActions.register( + 'all', + 'Test', + OC.PERMISSION_READ, + OC.imagePath('core', 'actions/test'), + actionStub + ); + FileActions.display($tr.find('td.filename'), true, fileList); + $tr.find('.action-test').click(); + expect(actionStub.calledOnce).toEqual(true); + expect(actionStub.getCall(0).args[0]).toEqual('testName.txt'); + var context = actionStub.getCall(0).args[1]; + expect(context.$file.is($tr)).toEqual(true); + expect(context.fileList).toBeDefined(); + expect(context.fileActions).toBeDefined(); + }); }); From 89ff2857fc713d4ff5a635d87aef1a213d30d8e9 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Mon, 19 May 2014 18:48:48 +0200 Subject: [PATCH 09/21] Add unit tests for fileActionsReady --- apps/files/tests/js/filelistSpec.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/apps/files/tests/js/filelistSpec.js b/apps/files/tests/js/filelistSpec.js index a3dc5b255a1..3dd715254e4 100644 --- a/apps/files/tests/js/filelistSpec.js +++ b/apps/files/tests/js/filelistSpec.js @@ -753,6 +753,20 @@ describe('OCA.Files.FileList tests', function() { fileList.setFiles(testFiles); expect(handler.calledOnce).toEqual(true); }); + it('triggers "fileActionsReady" event after single add', function() { + var handler = sinon.stub(); + fileList.setFiles(testFiles); + fileList.$fileList.on('fileActionsReady', handler); + fileList.add({name: 'test.txt'}); + expect(handler.calledOnce).toEqual(true); + }); + it('does not trigger "fileActionsReady" event after single add with silent argument', function() { + var handler = sinon.stub(); + fileList.setFiles(testFiles); + fileList.$fileList.on('fileActionsReady', handler); + fileList.add({name: 'test.txt'}, {silent: true}); + expect(handler.notCalled).toEqual(true); + }); it('triggers "updated" event after update', function() { var handler = sinon.stub(); fileList.$fileList.on('updated', handler); From d21b8108c88b33dbef465c7afc07613ab6466751 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Tue, 20 May 2014 11:06:09 +0200 Subject: [PATCH 10/21] Fixed navigation order --- apps/files/index.php | 5 +++++ apps/files_sharing/appinfo/app.php | 4 ++-- apps/files_trashbin/appinfo/app.php | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/apps/files/index.php b/apps/files/index.php index e24c535cb20..95ae7977ecc 100644 --- a/apps/files/index.php +++ b/apps/files/index.php @@ -74,7 +74,12 @@ if (OC_App::isEnabled('files_encryption')) { $nav = new OCP\Template('files', 'appnavigation', ''); +function sortNavigationItems($item1, $item2) { + return $item1['order'] - $item2['order']; +} + $navItems = \OCA\Files\App::getNavigationManager()->getAll(); +usort($navItems, 'sortNavigationItems'); $nav->assign('navigationItems', $navItems); $contentItems = array(); diff --git a/apps/files_sharing/appinfo/app.php b/apps/files_sharing/appinfo/app.php index 1756fc2f50e..21b2646c5ea 100644 --- a/apps/files_sharing/appinfo/app.php +++ b/apps/files_sharing/appinfo/app.php @@ -28,7 +28,7 @@ OC_FileProxy::register(new OCA\Files\Share\Proxy()); "id" => 'sharingin', "appname" => 'files_sharing', "script" => 'list.php', - "order" => 3, + "order" => 10, "name" => $l->t('Shared with you') ) ); @@ -37,7 +37,7 @@ OC_FileProxy::register(new OCA\Files\Share\Proxy()); "id" => 'sharingout', "appname" => 'files_sharing', "script" => 'list.php', - "order" => 4, + "order" => 15, "name" => $l->t('Shared with others') ) ); diff --git a/apps/files_trashbin/appinfo/app.php b/apps/files_trashbin/appinfo/app.php index b8900ee0de3..383115b8e63 100644 --- a/apps/files_trashbin/appinfo/app.php +++ b/apps/files_trashbin/appinfo/app.php @@ -9,7 +9,7 @@ array( "id" => 'trashbin', "appname" => 'files_trashbin', "script" => 'list.php', - "order" => 1, + "order" => 50, "name" => $l->t('Deleted files') ) ); From 06e53b359ac806b9103cb617e7a6bad8cbadb519 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Tue, 20 May 2014 11:44:18 +0200 Subject: [PATCH 11/21] Added "dir" in file actions handler context and fixed versions Added "dir" in file actions handler context so that handlers can know what the path of the file was without having to look it up from the file list. Fixed versions app to use the context.dir instead of the old $('#dir') element. This makes the versions popup work in the sharing overview. --- apps/files/js/fileactions.js | 8 ++++--- apps/files/js/filelist.js | 3 ++- apps/files/tests/js/fileactionsSpec.js | 8 +++++++ apps/files/tests/js/filelistSpec.js | 26 ++++++++++++++++++++ apps/files_versions/js/versions.js | 33 ++++++++++++++++---------- 5 files changed, 62 insertions(+), 16 deletions(-) diff --git a/apps/files/js/fileactions.js b/apps/files/js/fileactions.js index 1242fea7f99..a12b1f03423 100644 --- a/apps/files/js/fileactions.js +++ b/apps/files/js/fileactions.js @@ -138,11 +138,13 @@ self.currentFile = event.data.elem; var file = self.getCurrentFile(); + var $tr = $(this).closest('tr'); event.data.actionFunc(file, { - $file: $(this).closest('tr'), + $file: $tr, fileList: fileList || OCA.Files.App.fileList, - fileActions: self + fileActions: self, + dir: $tr.attr('data-path') || fileList.getCurrentDirectory() }); }; @@ -210,7 +212,7 @@ } if (triggerEvent){ - $('#fileList').trigger(jQuery.Event("fileActionsReady")); + fileList.$fileList.trigger(jQuery.Event("fileActionsReady")); } }, getCurrentFile: function () { diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index d969cb57c56..e1cbfc38566 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -251,7 +251,8 @@ action(filename, { $file: $tr, fileList: this, - fileActions: this.fileActions + fileActions: this.fileActions, + dir: $tr.attr('data-path') || this.getCurrentDirectory() }); } } diff --git a/apps/files/tests/js/fileactionsSpec.js b/apps/files/tests/js/fileactionsSpec.js index 519a31b3fce..edd7e343884 100644 --- a/apps/files/tests/js/fileactionsSpec.js +++ b/apps/files/tests/js/fileactionsSpec.js @@ -152,5 +152,13 @@ describe('OCA.Files.FileActions tests', function() { expect(context.$file.is($tr)).toEqual(true); expect(context.fileList).toBeDefined(); expect(context.fileActions).toBeDefined(); + expect(context.dir).toEqual('/subdir'); + + // when data-path is defined + actionStub.reset(); + $tr.attr('data-path', '/somepath'); + $tr.find('.action-test').click(); + context = actionStub.getCall(0).args[1]; + expect(context.dir).toEqual('/somepath'); }); }); diff --git a/apps/files/tests/js/filelistSpec.js b/apps/files/tests/js/filelistSpec.js index 3dd715254e4..739ae599c65 100644 --- a/apps/files/tests/js/filelistSpec.js +++ b/apps/files/tests/js/filelistSpec.js @@ -1526,6 +1526,32 @@ describe('OCA.Files.FileList tests', function() { expect(fileList.getSelectedFiles()).toEqual([]); }); }); + describe('File actions', function() { + it('Clicking on a file name will trigger default action', function() { + var actionStub = sinon.stub(); + fileList.setFiles(testFiles); + fileList.fileActions.register( + 'text/plain', + 'Test', + OC.PERMISSION_ALL, + function() { + // Specify icon for hitory button + return OC.imagePath('core','actions/history'); + }, + actionStub + ); + fileList.fileActions.setDefault('text/plain', 'Test'); + var $tr = fileList.findFileEl('One.txt'); + $tr.find('td.filename>a.name').click(); + expect(actionStub.calledOnce).toEqual(true); + expect(actionStub.getCall(0).args[0]).toEqual('One.txt'); + var context = actionStub.getCall(0).args[1]; + expect(context.$file.is($tr)).toEqual(true); + expect(context.fileList).toBeDefined(); + expect(context.fileActions).toBeDefined(); + expect(context.dir).toEqual('/subdir'); + }); + }); describe('Sorting files', function() { it('Sorts by name by default', function() { fileList.reload(); diff --git a/apps/files_versions/js/versions.js b/apps/files_versions/js/versions.js index b452bc25b13..a23935495d7 100644 --- a/apps/files_versions/js/versions.js +++ b/apps/files_versions/js/versions.js @@ -1,3 +1,14 @@ +/* + * Copyright (c) 2014 + * + * This file is licensed under the Affero General Public License version 3 + * or later. + * + * See the COPYING-README file. + * + */ + +/* global FileActions, scanFiles, escapeHTML, formatDate */ $(document).ready(function(){ if ($('#isPublic').val()){ @@ -10,18 +21,17 @@ $(document).ready(function(){ if (typeof FileActions !== 'undefined') { // Add versions button to 'files/index.php' FileActions.register( - 'file' - , 'Versions' - , OC.PERMISSION_UPDATE - , function() { + 'file', + 'Versions', + OC.PERMISSION_UPDATE, + function() { // Specify icon for hitory button return OC.imagePath('core','actions/history'); - } - ,function(filename){ + }, function(filename, context){ // Action to perform when clicked if (scanFiles.scanning){return;}//workaround to prevent additional http request block scanning feedback - var file = $('#dir').val().replace(/(?!<=\/)$|\/$/, '/' + filename); + var file = context.dir.replace(/(?!<=\/)$|\/$/, '/' + filename); var createDropDown = true; // Check if drop down is already visible for a different file if (($('#dropdown').length > 0) ) { @@ -33,10 +43,9 @@ $(document).ready(function(){ } if(createDropDown === true) { - createVersionsDropdown(filename, file); + createVersionsDropdown(filename, file, context.fileList); } - } - , t('files_versions', 'Versions') + }, t('files_versions', 'Versions') ); } @@ -75,7 +84,7 @@ function goToVersionPage(url){ window.location.assign(url); } -function createVersionsDropdown(filename, files) { +function createVersionsDropdown(filename, files, fileList) { var start = 0; var fileEl; @@ -88,7 +97,7 @@ function createVersionsDropdown(filename, files) { html += ''; if (filename) { - fileEl = FileList.findFileEl(filename); + fileEl = fileList.findFileEl(filename); fileEl.addClass('mouseOver'); $(html).appendTo(fileEl.find('td.filename')); } else { From fa32243d84e1801c379a5e8956ba2f3de236c718 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Tue, 20 May 2014 12:00:42 +0200 Subject: [PATCH 12/21] Small fixes to sharing overview - Removed file size from file summary in sharing overview - Fixed document title - Fixed empty content text for shared overview --- apps/files_sharing/js/app.js | 11 +++++++++++ apps/files_sharing/templates/list.php | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/apps/files_sharing/js/app.js b/apps/files_sharing/js/app.js index 9808e069784..57551237a34 100644 --- a/apps/files_sharing/js/app.js +++ b/apps/files_sharing/js/app.js @@ -28,6 +28,9 @@ OCA.Sharing.App = { ); this._initFileActions(this._inFileList); + 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.')); }, initSharingOut: function($el) { @@ -43,6 +46,9 @@ OCA.Sharing.App = { ); this._initFileActions(this._outFileList); + 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.')); }, _initFileActions: function(fileList) { @@ -54,6 +60,11 @@ OCA.Sharing.App = { OCA.Files.App.fileList.changeDirectory(context.$file.attr('data-path') + '/' + filename, true, true); }); fileList.setFileActions(fileActions); + }, + + _extendFileList: function(fileList) { + // remove size column from summary + fileList.fileSummary.$el.find('.filesize').remove(); } }; diff --git a/apps/files_sharing/templates/list.php b/apps/files_sharing/templates/list.php index 6671e674089..c688dcf8764 100644 --- a/apps/files_sharing/templates/list.php +++ b/apps/files_sharing/templates/list.php @@ -4,7 +4,7 @@
- + From ef59c69dc822c9ff69c564c41e0dfdce142b9cdf Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Tue, 20 May 2014 16:01:34 +0200 Subject: [PATCH 13/21] Distinguish legacy file actions from regular file actions Legacy file actions are registered by legacy apps through window.FileActions.register(). These actions can only be used by the main file list ("all files") because legacy apps can only deal with a single list / container. New file actions of compatible apps must be registered through OCA.Files.fileActions. These will be used for other lists like the sharing overview. Fixed versions and sharing actions to use OCA.Files.fileActions, which makes them available in the sharing overview list. --- apps/files/js/app.js | 19 ++++-- apps/files/js/fileactions.js | 69 +++++++++++++++----- apps/files/js/filelist.js | 21 +++--- apps/files/tests/js/appSpec.js | 53 ++++++++++++++- apps/files/tests/js/fileactionsSpec.js | 7 +- apps/files/tests/js/filelistSpec.js | 5 -- apps/files_sharing/js/app.js | 22 ++++--- apps/files_sharing/js/public.js | 18 +++-- apps/files_sharing/js/share.js | 11 ++-- apps/files_trashbin/js/app.js | 18 +++-- apps/files_trashbin/js/filelist.js | 4 +- apps/files_trashbin/tests/js/filelistSpec.js | 11 ++-- apps/files_versions/js/versions.js | 6 +- 13 files changed, 183 insertions(+), 81 deletions(-) diff --git a/apps/files/js/app.js b/apps/files/js/app.js index 6d8a9788d97..6ccf5135000 100644 --- a/apps/files/js/app.js +++ b/apps/files/js/app.js @@ -24,20 +24,27 @@ initialize: function() { this.navigation = new OCA.Files.Navigation($('#app-navigation')); - // TODO: ideally these should be in a separate class / app (the embedded "all files" app) - this.fileActions = OCA.Files.FileActions.clone(); + var fileActions = new OCA.Files.FileActions(); + // default actions + fileActions.registerDefaultActions(); + // legacy actions + fileActions.merge(window.FileActions); + // regular actions + fileActions.merge(OCA.Files.fileActions); + this.files = OCA.Files.Files; + // TODO: ideally these should be in a separate class / app (the embedded "all files" app) this.fileList = new OCA.Files.FileList( $('#app-content-files'), { scrollContainer: $('#app-content'), dragOptions: dragOptions, - folderDropOptions: folderDropOptions + folderDropOptions: folderDropOptions, + fileActions: fileActions, + allowLegacyActions: true } ); this.files.initialize(); - this.fileActions.registerDefaultActions(); - this.fileList.setFileActions(this.fileActions); // for backward compatibility, the global FileList will // refer to the one of the "files" view @@ -146,7 +153,7 @@ })(); $(document).ready(function() { - // wait for other apps/extensions to register their event handlers + // wait for other apps/extensions to register their event handlers and file actions // in the "ready" clause _.defer(function() { OCA.Files.App.initialize(); diff --git a/apps/files/js/fileactions.js b/apps/files/js/fileactions.js index a12b1f03423..8cee037e294 100644 --- a/apps/files/js/fileactions.js +++ b/apps/files/js/fileactions.js @@ -11,11 +11,40 @@ /* global trashBinApp */ (function() { - var FileActions = { + /** + * Construct a new FileActions instance + */ + var FileActions = function() { + this.initialize(); + } + FileActions.prototype = { actions: {}, defaults: {}, icons: {}, currentFile: null, + initialize: function() { + this.clear(); + }, + /** + * Merges the actions from the given fileActions into + * this instance. + * + * @param fileActions instance of OCA.Files.FileActions + */ + merge: function(fileActions) { + var self = this; + // merge first level to avoid unintended overwriting + _.each(fileActions.actions, function(sourceMimeData, mime) { + var targetMimeData = self.actions[mime]; + if (!targetMimeData) { + targetMimeData = {}; + } + self.actions[mime] = _.extend(targetMimeData, sourceMimeData); + }); + + this.defaults = _.extend(this.defaults, fileActions.defaults); + this.icons = _.extend(this.icons, fileActions.icons); + }, register: function (mime, name, permissions, icon, action, displayName) { if (!this.actions[mime]) { this.actions[mime] = {}; @@ -31,18 +60,6 @@ this.actions[mime][name]['displayName'] = displayName; this.icons[name] = icon; }, - /** - * Clones the current file actions handler including the already - * registered actions. - */ - clone: function() { - var fileActions = _.extend({}, this); - // need to deep copy the actions as well - fileActions.actions = _.extend({}, this.actions); - fileActions.defaults = _.extend({}, this.defaults); - //fileActions.icons = _.extend({}, this.icons); - return fileActions; - }, clear: function() { this.actions = {}; this.defaults = {}; @@ -137,6 +154,9 @@ event.preventDefault(); self.currentFile = event.data.elem; + // also set on global object for legacy apps + window.FileActions.currentFile = self.currentFile; + var file = self.getCurrentFile(); var $tr = $(this).closest('tr'); @@ -276,8 +296,25 @@ }; OCA.Files.FileActions = FileActions; -})(); -// for backward compatibility -window.FileActions = OCA.Files.FileActions; + // global file actions to be used by all lists + OCA.Files.fileActions = new OCA.Files.FileActions(); + OCA.Files.legacyFileActions = new OCA.Files.FileActions(); + + // for backward compatibility + // + // legacy apps are expecting a stateful global FileActions object to register + // their actions on. Since legacy apps are very likely to break with other + // FileList views than the main one ("All files"), actions registered + // 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); + }; + window.FileActions.setDefault = function (mime, name) { + console.warn('FileActions.setDefault() is deprecated, please use OCA.Files.fileActions.setDefault() instead'); + 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 e1cbfc38566..52d4c8ba3fe 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -125,7 +125,7 @@ this.$container = options.scrollContainer || $(window); this.$table = $el.find('table:first'); this.$fileList = $el.find('#fileList'); - this.fileActions = OCA.Files.FileActions; + this._initFileActions(options.fileActions); this.files = []; this._selectedFiles = {}; this._selectionSummary = new OCA.Files.FileSummary(); @@ -168,6 +168,14 @@ this.$container.on('scroll', _.bind(this._onScroll, this)); }, + _initFileActions: function(fileActions) { + this.fileActions = fileActions; + if (!this.fileActions) { + this.fileActions = new OCA.Files.FileActions(); + this.fileActions.registerDefaultActions(); + } + }, + /** * Event handler for when the URL changed */ @@ -248,6 +256,8 @@ var action = this.fileActions.getDefault(mime,type, permissions); if (action) { event.preventDefault(); + // also set on global object for legacy apps + window.FileActions.currentFile = this.fileActions.currentFile; action(filename, { $file: $tr, fileList: this, @@ -791,15 +801,6 @@ return OC.linkTo('files', 'index.php')+"?dir="+ encodeURIComponent(dir).replace(/%2F/g, '/'); }, - /** - * Sets the file actions handler. - * - * @param fileActions FileActions handler - */ - setFileActions: function(fileActions) { - this.fileActions = fileActions; - }, - /** * Sets the current directory name and updates the breadcrumb. * @param targetDir directory to display diff --git a/apps/files/tests/js/appSpec.js b/apps/files/tests/js/appSpec.js index 0e9abad6989..a9bbab03ecb 100644 --- a/apps/files/tests/js/appSpec.js +++ b/apps/files/tests/js/appSpec.js @@ -41,6 +41,10 @@ describe('OCA.Files.App tests', function() { '' ); + window.FileActions = new OCA.Files.FileActions(); + OCA.Files.legacyFileActions = window.FileActions; + OCA.Files.fileActions = new OCA.Files.FileActions(); + pushStateStub = sinon.stub(OC.Util.History, 'pushState'); parseUrlQueryStub = sinon.stub(OC.Util.History, 'parseUrlQuery'); parseUrlQueryStub.returns({}); @@ -51,8 +55,6 @@ describe('OCA.Files.App tests', function() { App.navigation = null; App.fileList = null; App.files = null; - App.fileActions.clear(); - App.fileActions = null; pushStateStub.restore(); parseUrlQueryStub.restore(); @@ -64,6 +66,53 @@ describe('OCA.Files.App tests', function() { expect(App.fileList.fileActions.actions.all).toBeDefined(); expect(App.fileList.$el.is('#app-content-files')).toEqual(true); }); + it('merges the legacy file actions with the default ones', function() { + var legacyActionStub = sinon.stub(); + var actionStub = sinon.stub(); + // legacy action + window.FileActions.register( + 'all', + 'LegacyTest', + OC.PERMISSION_READ, + OC.imagePath('core', 'actions/test'), + legacyActionStub + ); + // legacy action to be overwritten + window.FileActions.register( + 'all', + 'OverwriteThis', + OC.PERMISSION_READ, + OC.imagePath('core', 'actions/test'), + legacyActionStub + ); + + // regular file actions + OCA.Files.fileActions.register( + 'all', + 'RegularTest', + OC.PERMISSION_READ, + OC.imagePath('core', 'actions/test'), + actionStub + ); + + // overwrite + OCA.Files.fileActions.register( + 'all', + 'OverwriteThis', + OC.PERMISSION_READ, + OC.imagePath('core', 'actions/test'), + actionStub + ); + + App.initialize(); + + var actions = App.fileList.fileActions.actions; + expect(actions.all.OverwriteThis.action).toBe(actionStub); + expect(actions.all.LegacyTest.action).toBe(legacyActionStub); + expect(actions.all.RegularTest.action).toBe(actionStub); + // default one still there + expect(actions.dir.Open.action).toBeDefined(); + }); }); describe('URL handling', function() { diff --git a/apps/files/tests/js/fileactionsSpec.js b/apps/files/tests/js/fileactionsSpec.js index edd7e343884..fa634da08a2 100644 --- a/apps/files/tests/js/fileactionsSpec.js +++ b/apps/files/tests/js/fileactionsSpec.js @@ -21,7 +21,7 @@ describe('OCA.Files.FileActions tests', function() { var $filesTable, fileList; - var FileActions = OCA.Files.FileActions; + var FileActions; beforeEach(function() { // init horrible parameters @@ -31,10 +31,11 @@ describe('OCA.Files.FileActions tests', function() { // dummy files table $filesTable = $body.append('
'); fileList = new OCA.Files.FileList($('#testArea')); - FileActions.registerDefaultActions(fileList); + FileActions = new OCA.Files.FileActions(); + FileActions.registerDefaultActions(); }); afterEach(function() { - FileActions.clear(); + FileActions = null; fileList = undefined; $('#dir, #permissions, #filestable').remove(); }); diff --git a/apps/files/tests/js/filelistSpec.js b/apps/files/tests/js/filelistSpec.js index 739ae599c65..a197fd5722c 100644 --- a/apps/files/tests/js/filelistSpec.js +++ b/apps/files/tests/js/filelistSpec.js @@ -21,7 +21,6 @@ describe('OCA.Files.FileList tests', function() { var testFiles, alertStub, notificationStub, fileList; - var FileActions = OCA.Files.FileActions; /** * Generate test file data @@ -117,15 +116,11 @@ describe('OCA.Files.FileList tests', function() { }]; fileList = new OCA.Files.FileList($('#app-content-files')); - FileActions.clear(); - FileActions.registerDefaultActions(fileList); - fileList.setFileActions(FileActions); }); afterEach(function() { testFiles = undefined; fileList = undefined; - FileActions.clear(); notificationStub.restore(); alertStub.restore(); }); diff --git a/apps/files_sharing/js/app.js b/apps/files_sharing/js/app.js index 57551237a34..7a71684f1a1 100644 --- a/apps/files_sharing/js/app.js +++ b/apps/files_sharing/js/app.js @@ -23,11 +23,11 @@ OCA.Sharing.App = { $el, { scrollContainer: $('#app-content'), - sharedWithUser: true + sharedWithUser: true, + fileActions: this._createFileActions() } ); - this._initFileActions(this._inFileList); 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.')); @@ -41,25 +41,31 @@ OCA.Sharing.App = { $el, { scrollContainer: $('#app-content'), - sharedWithUser: false + sharedWithUser: false, + fileActions: this._createFileActions() } ); - this._initFileActions(this._outFileList); 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.')); }, - _initFileActions: function(fileList) { - var fileActions = OCA.Files.FileActions.clone(); + _createFileActions: function() { + // inherit file actions from the files 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.merge(OCA.Files.fileActions); + // when the user clicks on a folder, redirect to the corresponding - // folder in the files app + // folder in the files app instead of opening it directly fileActions.register('dir', 'Open', OC.PERMISSION_READ, '', function (filename, context) { OCA.Files.App.setActiveView('files', {silent: true}); OCA.Files.App.fileList.changeDirectory(context.$file.attr('data-path') + '/' + filename, true, true); }); - fileList.setFileActions(fileActions); + fileActions.setDefault('dir', 'Open'); + return fileActions; }, _extendFileList: function(fileList) { diff --git a/apps/files_sharing/js/public.js b/apps/files_sharing/js/public.js index d825ee9de15..446f3f2442b 100644 --- a/apps/files_sharing/js/public.js +++ b/apps/files_sharing/js/public.js @@ -19,9 +19,18 @@ OCA.Sharing.PublicApp = { initialize: function($el) { var self = this; + var fileActions; if (this._initialized) { return; } + fileActions = new OCA.Files.FileActions(); + // default actions + fileActions.registerDefaultActions(); + // legacy actions + fileActions.merge(window.FileActions); + // regular actions + fileActions.merge(OCA.Files.fileActions); + this._initialized = true; this.initialDir = $('#dir').val(); @@ -32,7 +41,8 @@ OCA.Sharing.PublicApp = { { scrollContainer: $(window), dragOptions: dragOptions, - folderDropOptions: folderDropOptions + folderDropOptions: folderDropOptions, + fileActions: fileActions } ); this.files = OCA.Files.Files; @@ -121,10 +131,8 @@ OCA.Sharing.PublicApp = { }; }); - this.fileActions = _.extend({}, OCA.Files.FileActions); - this.fileActions.registerDefaultActions(this.fileList); - delete this.fileActions.actions.all.Share; - this.fileList.setFileActions(this.fileActions); + // do not allow sharing from the public page + delete this.fileList.fileActions.actions.all.Share; this.fileList.changeDirectory(this.initialDir || '/', false, true); diff --git a/apps/files_sharing/js/share.js b/apps/files_sharing/js/share.js index 28586a179a4..1fcb1f088bf 100644 --- a/apps/files_sharing/js/share.js +++ b/apps/files_sharing/js/share.js @@ -9,10 +9,7 @@ */ $(document).ready(function() { - - var sharesLoaded = false; - - if (typeof OC.Share !== 'undefined' && typeof FileActions !== 'undefined') { + 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; @@ -64,18 +61,18 @@ $(document).ready(function() { } }) - if (!sharesLoaded){ + if (!OCA.Sharing.sharesLoaded){ OC.Share.loadIcons('file', $fileList); // assume that we got all shares, so switching directories // will not invalidate that list - sharesLoaded = true; + OCA.Sharing.sharesLoaded = true; } else{ OC.Share.updateIcons('file', $fileList); } }); - OCA.Files.FileActions.register( + OCA.Files.fileActions.register( 'all', 'Share', OC.PERMISSION_SHARE, diff --git a/apps/files_trashbin/js/app.js b/apps/files_trashbin/js/app.js index cf3fb1d0d16..c59a132b8c4 100644 --- a/apps/files_trashbin/js/app.js +++ b/apps/files_trashbin/js/app.js @@ -19,22 +19,20 @@ OCA.Trashbin.App = { this._initialized = true; this.fileList = new OCA.Trashbin.FileList( $('#app-content-trashbin'), { - scrollContainer: $('#app-content') + scrollContainer: $('#app-content'), + fileActions: this._createFileActions() } ); - this.registerFileActions(this.fileList); }, - registerFileActions: function(fileList) { - var self = this; - var fileActions = _.extend({}, OCA.Files.FileActions); - fileActions.clear(); - fileActions.register('dir', 'Open', OC.PERMISSION_READ, '', function (filename) { - var dir = fileList.getCurrentDirectory(); + _createFileActions: function() { + var fileActions = new OCA.Files.FileActions(); + fileActions.register('dir', 'Open', OC.PERMISSION_READ, '', function (filename, context) { + var dir = context.fileList.getCurrentDirectory(); if (dir !== '/') { dir = dir + '/'; } - fileList.changeDirectory(dir + filename); + context.fileList.changeDirectory(dir + filename); }); fileActions.setDefault('dir', 'Open'); @@ -69,7 +67,7 @@ OCA.Trashbin.App = { _.bind(fileList._removeCallback, fileList) ); }); - fileList.setFileActions(fileActions); + return fileActions; } }; diff --git a/apps/files_trashbin/js/filelist.js b/apps/files_trashbin/js/filelist.js index 205f879f335..826c1bd64d5 100644 --- a/apps/files_trashbin/js/filelist.js +++ b/apps/files_trashbin/js/filelist.js @@ -26,8 +26,8 @@ return name; } - var FileList = function($el) { - this.initialize($el); + var FileList = function($el, options) { + this.initialize($el, options); }; FileList.prototype = _.extend({}, OCA.Files.FileList.prototype, { id: 'trashbin', diff --git a/apps/files_trashbin/tests/js/filelistSpec.js b/apps/files_trashbin/tests/js/filelistSpec.js index d41c24c3cc9..11eeff68df8 100644 --- a/apps/files_trashbin/tests/js/filelistSpec.js +++ b/apps/files_trashbin/tests/js/filelistSpec.js @@ -21,7 +21,6 @@ describe('OCA.Trashbin.FileList tests', function() { var testFiles, alertStub, notificationStub, fileList; - var FileActions = OCA.Files.FileActions; beforeEach(function() { alertStub = sinon.stub(OC.dialogs, 'alert'); @@ -87,14 +86,18 @@ describe('OCA.Trashbin.FileList tests', function() { etag: '456' }]; - fileList = new OCA.Trashbin.FileList($('#app-content-trashbin')); - OCA.Trashbin.App.registerFileActions(fileList); + // register file actions like the trashbin App does + var fileActions = OCA.Trashbin.App._createFileActions(fileList); + fileList = new OCA.Trashbin.FileList( + $('#app-content-trashbin'), { + fileActions: fileActions + } + ); }); afterEach(function() { testFiles = undefined; fileList = undefined; - FileActions.clear(); $('#dir').remove(); notificationStub.restore(); alertStub.restore(); diff --git a/apps/files_versions/js/versions.js b/apps/files_versions/js/versions.js index a23935495d7..942a1a929f7 100644 --- a/apps/files_versions/js/versions.js +++ b/apps/files_versions/js/versions.js @@ -8,7 +8,7 @@ * */ -/* global FileActions, scanFiles, escapeHTML, formatDate */ +/* global scanFiles, escapeHTML, formatDate */ $(document).ready(function(){ if ($('#isPublic').val()){ @@ -18,9 +18,9 @@ $(document).ready(function(){ return; } - if (typeof FileActions !== 'undefined') { + if (OCA.Files) { // Add versions button to 'files/index.php' - FileActions.register( + OCA.Files.fileActions.register( 'file', 'Versions', OC.PERMISSION_UPDATE, From 0879a63320430b71c832c2c9fc725f3d244c9526 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Tue, 20 May 2014 13:11:06 +0200 Subject: [PATCH 14/21] Mimetype for sharing overview --- apps/files_sharing/js/sharedfilelist.js | 3 ++- apps/files_sharing/lib/api.php | 17 ++++++++++++++--- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/apps/files_sharing/js/sharedfilelist.js b/apps/files_sharing/js/sharedfilelist.js index f3a6d669760..4508de47dcc 100644 --- a/apps/files_sharing/js/sharedfilelist.js +++ b/apps/files_sharing/js/sharedfilelist.js @@ -133,7 +133,8 @@ /* jshint camelcase: false */ var file = { id: share.file_source, - mtime: share.stime * 1000 + mtime: share.stime * 1000, + mimetype: share.mimetype }; if (share.item_type === 'folder') { file.type = 'dir'; diff --git a/apps/files_sharing/lib/api.php b/apps/files_sharing/lib/api.php index 10b3f76db8f..ff56c465b29 100644 --- a/apps/files_sharing/lib/api.php +++ b/apps/files_sharing/lib/api.php @@ -52,12 +52,18 @@ class Api { return self::collectShares($params); } - $share = \OCP\Share::getItemShared('file', null); + $shares = \OCP\Share::getItemShared('file', null); - if ($share === false) { + if ($shares === false) { return new \OC_OCS_Result(null, 404, 'could not get shares'); } else { - return new \OC_OCS_Result($share); + foreach ($shares as &$share) { + if ($share['item_type'] === 'file') { + $share['mimetype'] = \OC_Helper::getFileNameMimeType($share['file_target']); + } + $newShares[] = $share; + } + return new \OC_OCS_Result($shares); } } @@ -205,6 +211,11 @@ class Api { private static function getFilesSharedWithMe() { try { $shares = \OCP\Share::getItemsSharedWith('file'); + foreach ($shares as &$share) { + if ($share['item_type'] === 'file') { + $share['mimetype'] = \OC_Helper::getFileNameMimeType($share['file_target']); + } + } $result = new \OC_OCS_Result($shares); } catch (\Exception $e) { $result = new \OC_OCS_Result(null, 403, $e->getMessage()); From c9d05c66de06290c8c6a8fde06f35652ad796f48 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Fri, 23 May 2014 16:14:02 +0200 Subject: [PATCH 15/21] Fix IE8 issue when icon is not defined Sometimes no icon file is passed to replaceSVGIcon(), it showed an error in IE8 and broke the code flow. This fix adds a check whether the file name is set. --- core/js/js.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/js/js.js b/core/js/js.js index 44b0a1a39af..e31f67cca9c 100644 --- a/core/js/js.js +++ b/core/js/js.js @@ -1250,7 +1250,7 @@ OC.Util = { * @return {string} fixed image path with png extension if SVG is not supported */ replaceSVGIcon: function(file) { - if (!OC.Util.hasSVGSupport()) { + if (file && !OC.Util.hasSVGSupport()) { var i = file.lastIndexOf('.svg'); if (i >= 0) { file = file.substr(0, i) + '.png' + file.substr(i+4); From 6fbf4d8548133dff4419e5e2e0c649f49e177669 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Mon, 26 May 2014 11:52:42 +0200 Subject: [PATCH 16/21] Fix table header colors Table headers should be 999 even when using links (introduced by the sorting feature) When selecting with checkboxes, they must appear black. --- apps/files/css/files.css | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/files/css/files.css b/apps/files/css/files.css index 731dd7a23e7..0bcea2eceaf 100644 --- a/apps/files/css/files.css +++ b/apps/files/css/files.css @@ -151,7 +151,13 @@ tr:hover span.extension { } table tr.mouseOver td { background-color:#eee; } -table th { height:24px; padding:0 8px; color:#999; } +table th { height:24px; padding:0 8px; } +table th, table th a { + color: #999; +} +table.multiselect th a { + color: #000; +} table th .columntitle { display: inline-block; padding: 15px; From 1d9129eac35b49a4e8d0d642a68d7d634f31c905 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Wed, 21 May 2014 12:54:34 +0200 Subject: [PATCH 17/21] 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 --- apps/files/js/app.js | 8 + apps/files/js/fileactions.js | 15 +- apps/files/js/filelist.js | 19 +- apps/files/tests/js/fileactionsSpec.js | 29 +- apps/files/tests/js/filelistSpec.js | 2 +- apps/files_sharing/js/app.js | 29 +- apps/files_sharing/js/public.js | 5 +- apps/files_sharing/js/share.js | 9 +- apps/files_sharing/js/sharedfilelist.js | 56 ++- apps/files_sharing/templates/list.php | 4 +- apps/files_sharing/tests/js/appSpec.js | 140 ++++++ .../tests/js/sharedfilelistSpec.js | 417 ++++++++++++++++++ core/js/share.js | 45 +- tests/karma.config.js | 43 +- 14 files changed, 751 insertions(+), 70 deletions(-) create mode 100644 apps/files_sharing/tests/js/appSpec.js create mode 100644 apps/files_sharing/tests/js/sharedfilelistSpec.js 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 @@ -72,6 +72,14 @@ this.navigation.setActiveItem(viewId, options); }, + /** + * Returns the view id of the currently active view + * @return view id + */ + getActiveView: function() { + return this.navigation.getActiveItem(); + }, + /** * Setup events based on URL changes */ 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 = $('') - .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 @@ t( 'Name' )); ?> - + t( 'Shared with' )); ?> - t( 'Shared since' )); ?> + t( 'Share time' )); ?> 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 +* +* 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 . +* +*/ + +describe('OCA.Sharing.App tests', function() { + var App = OCA.Sharing.App; + var fileListIn; + var fileListOut; + + beforeEach(function() { + $('#testArea').append( + '
' + + '
  • Files
  • ' + + '
  • ' + + '
  • ' + + '
' + + '
' + + '' + + '' + + '' + + '
' + + '' + ); + 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($('
')); + + var setActiveViewStub = sinon.stub(OCA.Files.App, 'setActiveView'); + // create dummy table so we can click the dom + var $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 + * + * 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( + '
' + + // init horrible parameters + '' + + '' + + // dummy controls + '
' + + '
' + + '
' + + '
' + + // dummy table + // TODO: at some point this will be rendered by the fileList class itself! + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
' + + '
Empty content message
' + + '
' + ); + }); + 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(' '+t('core', 'Shared')+'').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(' '+t('core', 'Shared')+'').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(' '+ escapeHTML(t('core', 'Shared'))+'').prepend(img); - } else { - action.removeClass('permanent'); - action.html(' '+ escapeHTML(t('core', 'Share'))+'').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(' '+ escapeHTML(t('core', 'Shared'))+'').prepend(img); + } else { + action.removeClass('permanent'); + action.html(' '+ escapeHTML(t('core', 'Share'))+'').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 From e084183d1e9cd180ad1f29ba1d22c99ac643d166 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Mon, 26 May 2014 12:59:44 +0200 Subject: [PATCH 18/21] Added owner display name in action --- apps/files_sharing/js/sharedfilelist.js | 3 +++ apps/files_sharing/tests/js/appSpec.js | 5 ++++- apps/files_sharing/tests/js/sharedfilelistSpec.js | 6 ++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/apps/files_sharing/js/sharedfilelist.js b/apps/files_sharing/js/sharedfilelist.js index b941722d0cf..cf5e65528e2 100644 --- a/apps/files_sharing/js/sharedfilelist.js +++ b/apps/files_sharing/js/sharedfilelist.js @@ -47,6 +47,9 @@ $tr.find('td.date').before($sharedWith); $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); + } return $tr; }, diff --git a/apps/files_sharing/tests/js/appSpec.js b/apps/files_sharing/tests/js/appSpec.js index 09c48a6305c..ad95ee53942 100644 --- a/apps/files_sharing/tests/js/appSpec.js +++ b/apps/files_sharing/tests/js/appSpec.js @@ -121,7 +121,10 @@ describe('OCA.Sharing.App tests', function() { name: 'testdir', type: 'dir', path: '/somewhere/inside/subdir', - counterParts: ['user2'] + counterParts: ['user2'], + shares: [{ + ownerDisplayName: '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 ddcd746afe0..5cfcec95d0c 100644 --- a/apps/files_sharing/tests/js/sharedfilelistSpec.js +++ b/apps/files_sharing/tests/js/sharedfilelistSpec.js @@ -121,6 +121,7 @@ describe('OCA.Sharing.FileList tests', function() { 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-owner')).toEqual('User Two'); expect($tr.attr('data-share-id')).toEqual('7'); expect($tr.find('a.name').attr('href')).toEqual( OC.webroot + @@ -164,6 +165,7 @@ describe('OCA.Sharing.FileList tests', function() { 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-owner')).toEqual('User Two'); expect($tr.attr('data-share-id')).toEqual('7'); expect($tr.find('a.name').attr('href')).toEqual( OC.webroot + @@ -240,6 +242,7 @@ describe('OCA.Sharing.FileList tests', function() { 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-owner')).not.toBeDefined(); expect($tr.attr('data-share-id')).toEqual('7'); expect($tr.find('a.name').attr('href')).toEqual( OC.webroot + @@ -282,6 +285,7 @@ describe('OCA.Sharing.FileList tests', function() { 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-owner')).not.toBeDefined(); expect($tr.attr('data-share-id')).toEqual('7'); expect($tr.find('a.name').attr('href')).toEqual( OC.webroot + @@ -334,6 +338,7 @@ describe('OCA.Sharing.FileList tests', function() { 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-owner')).not.toBeDefined(); expect($tr.attr('data-share-id')).toEqual('7'); expect($tr.find('a.name').attr('href')).toEqual( OC.webroot + @@ -403,6 +408,7 @@ describe('OCA.Sharing.FileList tests', function() { 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-owner')).not.toBeDefined(); expect($tr.attr('data-share-id')).toEqual('7,8,9'); expect($tr.find('a.name').attr('href')).toEqual( OC.webroot + From 66afd1aa3925f31f347edfc5dae7adf7603ba5c6 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Wed, 28 May 2014 16:34:00 +0200 Subject: [PATCH 19/21] Fix fileList fallback in OC.Share for outside files app OC.Share can be used in non-files apps, so the fileList callback needs to support that as well. --- core/js/share.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/core/js/share.js b/core/js/share.js index 894f0d488f4..90f6c7fdc7c 100644 --- a/core/js/share.js +++ b/core/js/share.js @@ -37,9 +37,16 @@ OC.Share={ */ updateIcons:function(itemType, fileList){ var item; - fileList = fileList || OCA.Files.App.fileList; - var $fileList = fileList.$fileList; - var currentDir = fileList.getCurrentDirectory(); + var $fileList; + var currentDir; + if (!fileList && OCA.Files) { + fileList = OCA.Files.App.fileList; + } + // fileList is usually only defined in the files app + if (fileList) { + $fileList = fileList.$fileList; + currentDir = fileList.getCurrentDirectory(); + } for (item in OC.Share.statuses){ var image; var data = OC.Share.statuses[item]; From 5e4e7734467a00bd40e3a04d72a5f49f012ced40 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Wed, 28 May 2014 16:53:42 +0200 Subject: [PATCH 20/21] Removed "Share with" column --- apps/files_sharing/js/sharedfilelist.js | 4 +--- apps/files_sharing/templates/list.php | 3 --- apps/files_sharing/tests/js/sharedfilelistSpec.js | 11 ----------- 3 files changed, 1 insertion(+), 17 deletions(-) diff --git a/apps/files_sharing/js/sharedfilelist.js b/apps/files_sharing/js/sharedfilelist.js index cf5e65528e2..ef1034ecfdc 100644 --- a/apps/files_sharing/js/sharedfilelist.js +++ b/apps/files_sharing/js/sharedfilelist.js @@ -42,9 +42,7 @@ // TODO: hook earlier and render the whole row here var $tr = OCA.Files.FileList.prototype._createRow.apply(this, arguments); $tr.find('.filesize').remove(); - var $sharedWith = $('') - .text(fileData.counterParts.join(', ')); - $tr.find('td.date').before($sharedWith); + $tr.find('td.date').before($tr.children('td:first')); $tr.find('td.filename input:checkbox').remove(); $tr.attr('data-share-id', _.pluck(fileData.shares, 'id').join(',')); if (this._sharedWithUser) { diff --git a/apps/files_sharing/templates/list.php b/apps/files_sharing/templates/list.php index b07222cfe28..a1d95ebc1f1 100644 --- a/apps/files_sharing/templates/list.php +++ b/apps/files_sharing/templates/list.php @@ -16,9 +16,6 @@ t( 'Name' )); ?> - - t( 'Shared with' )); ?> - t( 'Share time' )); ?> diff --git a/apps/files_sharing/tests/js/sharedfilelistSpec.js b/apps/files_sharing/tests/js/sharedfilelistSpec.js index 5cfcec95d0c..7aec8322a44 100644 --- a/apps/files_sharing/tests/js/sharedfilelistSpec.js +++ b/apps/files_sharing/tests/js/sharedfilelistSpec.js @@ -128,8 +128,6 @@ describe('OCA.Sharing.FileList tests', function() { '/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() { @@ -172,8 +170,6 @@ describe('OCA.Sharing.FileList tests', function() { '/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'); }); }); @@ -249,8 +245,6 @@ describe('OCA.Sharing.FileList tests', function() { '/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() { @@ -292,8 +286,6 @@ describe('OCA.Sharing.FileList tests', function() { '/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() { @@ -344,7 +336,6 @@ describe('OCA.Sharing.FileList tests', function() { 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'); }); @@ -415,8 +406,6 @@ describe('OCA.Sharing.FileList tests', function() { '/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'); }); }); From 7fac2b62e954b0f8a693516da1151c97efa2ee99 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Fri, 30 May 2014 12:35:04 +0200 Subject: [PATCH 21/21] Fixed warning when file_target is not set In some cases (like in the unit tests) "file_target" is not set yet whenever the target file system hasn't been mounted yet. --- apps/files_sharing/lib/api.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/apps/files_sharing/lib/api.php b/apps/files_sharing/lib/api.php index ff56c465b29..dc4e5cf6c49 100644 --- a/apps/files_sharing/lib/api.php +++ b/apps/files_sharing/lib/api.php @@ -58,7 +58,9 @@ class Api { return new \OC_OCS_Result(null, 404, 'could not get shares'); } else { foreach ($shares as &$share) { - if ($share['item_type'] === 'file') { + // file_target might not be set if the target user hasn't mounted + // the filesystem yet + if ($share['item_type'] === 'file' && isset($share['file_target'])) { $share['mimetype'] = \OC_Helper::getFileNameMimeType($share['file_target']); } $newShares[] = $share;