diff options
author | Lukas Reschke <lukas@statuscode.ch> | 2014-05-30 13:42:24 +0200 |
---|---|---|
committer | Lukas Reschke <lukas@statuscode.ch> | 2014-05-30 13:42:24 +0200 |
commit | 517501ffbf369b24191d8b9a9f2ce44a9891fb97 (patch) | |
tree | f638ba7e2f3554b7b1a31f628496c7d0740fd2ac /apps/files_sharing/js | |
parent | 929882a32a020b6c05605f416fa55024b9a60d33 (diff) | |
parent | 7fac2b62e954b0f8a693516da1151c97efa2ee99 (diff) | |
download | nextcloud-server-517501ffbf369b24191d8b9a9f2ce44a9891fb97.tar.gz nextcloud-server-517501ffbf369b24191d8b9a9f2ce44a9891fb97.zip |
Merge pull request #8417 from owncloud/share-overview
Sharing overview page
Diffstat (limited to 'apps/files_sharing/js')
-rw-r--r-- | apps/files_sharing/js/app.js | 106 | ||||
-rw-r--r-- | apps/files_sharing/js/public.js | 23 | ||||
-rw-r--r-- | apps/files_sharing/js/share.js | 50 | ||||
-rw-r--r-- | apps/files_sharing/js/sharedfilelist.js | 235 |
4 files changed, 384 insertions, 30 deletions
diff --git a/apps/files_sharing/js/app.js b/apps/files_sharing/js/app.js new file mode 100644 index 00000000000..3764328a5d0 --- /dev/null +++ b/apps/files_sharing/js/app.js @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2014 Vincent Petry <pvince81@owncloud.com> + * + * This file is licensed under the Affero General Public License version 3 + * or later. + * + * See the COPYING-README file. + * + */ + +OCA.Sharing = {}; +OCA.Sharing.App = { + + _inFileList: null, + _outFileList: null, + + initSharingIn: function($el) { + if (this._inFileList) { + return this._inFileList; + } + + this._inFileList = new OCA.Sharing.FileList( + $el, + { + scrollContainer: $('#app-content'), + sharedWithUser: true, + fileActions: this._createFileActions() + } + ); + + 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 this._outFileList; + } + this._outFileList = new OCA.Sharing.FileList( + $el, + { + scrollContainer: $('#app-content'), + sharedWithUser: false, + fileActions: this._createFileActions() + } + ); + + 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() { + // 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.registerDefaultActions(); + fileActions.merge(OCA.Files.fileActions); + + // when the user clicks on a folder, redirect to the corresponding + // 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); + }); + fileActions.setDefault('dir', 'Open'); + return fileActions; + }, + + _extendFileList: function(fileList) { + // remove size column from summary + fileList.fileSummary.$el.find('.filesize').remove(); + } +}; + +$(document).ready(function() { + $('#app-content-sharingin').on('show', function(e) { + OCA.Sharing.App.initSharingIn($(e.target)); + }); + $('#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 d825ee9de15..27e8d361ff9 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); @@ -158,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 84c5bf57b38..5a42604c866 100644 --- a/apps/files_sharing/js/share.js +++ b/apps/files_sharing/js/share.js @@ -8,12 +8,8 @@ * */ -/* global FileList, FileActions */ $(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; @@ -31,10 +27,12 @@ $(document).ready(function() { }; } - $('#fileList').on('fileActionsReady',function(){ + // use delegate to catch the case with multiple file lists + $('#content').delegate('#fileList', 'fileActionsReady',function(ev){ // if no share action exists because the admin disabled sharing for this user // we create a share notification action to inform the user about files // shared with him otherwise we just update the existing share action. + var fileList = ev.fileList; var $fileList = $(this); $fileList.find('[data-share-owner]').each(function() { var $tr = $(this); @@ -62,46 +60,50 @@ $(document).ready(function() { return $result; }); } - }) + }); - // FIXME: these calls are also working on hard-coded - // list selectors... - if (!sharesLoaded){ - OC.Share.loadIcons('file'); + 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'); + 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 new file mode 100644 index 00000000000..ef1034ecfdc --- /dev/null +++ b/apps/files_sharing/js/sharedfilelist.js @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2014 Vincent Petry <pvince81@owncloud.com> + * + * This file is licensed under the Affero General Public License version 3 + * or later. + * + * See the COPYING-README file. + * + */ +(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', + + /** + * 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; + } + }, + + _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(); + $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) { + $tr.attr('data-share-owner', fileData.shares[0].ownerDisplayName); + } + 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; + }, + + 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 | OC.PERMISSION_DELETE; + }, + + updateStorageStatistics: function() { + // no op because it doesn't have + // storage info like free space / used space + }, + + 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 + } + }, + + /** + * 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 + var files = _.chain(data) + // convert share data to file data + .map(function(share) { + /* jshint camelcase: false */ + var file = { + id: share.file_source, + mimetype: share.mimetype + }; + if (share.item_type === 'folder') { + file.type = 'dir'; + file.mimetype = 'httpd/unix-directory'; + } + 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, + stime: share.stime * 1000, + }; + if (self._sharedWithUser) { + 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; + }) + // 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]; + 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); + } + + 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++; + } + + 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 + .value(); + + return files; + } + }); + + OCA.Sharing.FileList = FileList; +})(); |