From 289f302cd74192929e6737ddf97ef274681d98ea Mon Sep 17 00:00:00 2001 From: Felix Heidecke Date: Tue, 8 Oct 2019 14:10:40 +0200 Subject: [PATCH 1/2] Add dir/path sharing info and indicator --- apps/files/css/files.css | 24 +- apps/files/js/filelist.js | 247 +++++++++++++++++- apps/files/tests/js/filelistSpec.js | 18 ++ apps/files_sharing/css/sharetabview.css | 40 +++ apps/files_sharing/js/share.js | 3 + changelog/unreleased/36572 | 8 + core/js/apps.js | 11 + core/js/share.js | 3 +- core/js/sharedialogview.js | 11 +- core/js/tests/specs/sharedialogviewSpec.js | 49 +++- .../features/bootstrap/WebUIFilesContext.php | 35 +++ .../bootstrap/WebUISharingContext.php | 44 ++++ tests/acceptance/features/lib/FilesPage.php | 13 + .../features/lib/FilesPageBasic.php | 4 +- .../features/lib/FilesPageElement/FileRow.php | 20 +- .../lib/FilesPageElement/SharingDialog.php | 22 ++ .../features/lib/SharedWithYouPage.php | 12 +- .../federationSharing.feature | 48 ++++ .../shareWithGroups.feature | 78 ++++++ .../shareWithUsers.feature | 138 ++++++++++ .../shareByPublicLink.feature | 85 +++++- 21 files changed, 875 insertions(+), 38 deletions(-) create mode 100644 changelog/unreleased/36572 diff --git a/apps/files/css/files.css b/apps/files/css/files.css index e63e3012f24e..f9c47737a0b1 100644 --- a/apps/files/css/files.css +++ b/apps/files/css/files.css @@ -512,7 +512,7 @@ table td.filename .uploadtext { } /* File checkboxes */ -html:not(.ie8) #fileList tr td.filename > .selectCheckBox + label:before { +#fileList tr td.filename > .selectCheckBox + label:before { opacity: 0; position: absolute; bottom: 4px; @@ -520,13 +520,21 @@ html:not(.ie8) #fileList tr td.filename > .selectCheckBox + label:before { z-index: 10; } -html.ie8 #fileList tr td.filename > .selectCheckBox { - filter: alpha(opacity=0); - opacity: 0; - float: left; - top: 0; - margin: 32px 0 4px 32px; - /* bigger clickable area doesn’t work in FF width:2.8em; height:2.4em;*/ +#fileList tr:not(:hover):not(.selected) td.filename .thumbnail.sharetree-item:after { + position: absolute; + display: block; + content: ''; + bottom: 0px; + right: -5px; + z-index: 10; + background-image: url(../../../core/img/actions/shared.svg); + width: 14px; + height: 14px; + background-repeat: no-repeat; + background-position: center; + background-size: 10px; + background-color: #bfbfbf; + border-radius: 50%; } /* Show checkbox when hovering, checked, or selected */ diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js index e76008dc344b..b425c039b5b5 100644 --- a/apps/files/js/filelist.js +++ b/apps/files/js/filelist.js @@ -168,6 +168,13 @@ */ _sortComparator: null, + /** + * Stores shareTree items and infos + * + * @type Array + */ + _shareTreeCache: {}, + /** * Whether to do a client side sort. * When false, clicking on a table header will call reload(). @@ -293,11 +300,11 @@ breadcrumbOptions.onDrop = _.bind(this._onDropOnBreadCrumb, this); breadcrumbOptions.onOver = function() { self.$el.find('td.filename.ui-droppable').droppable('disable'); - } + }; breadcrumbOptions.onOut = function() { self.$el.find('td.filename.ui-droppable').droppable('enable'); + }; } - } this.breadcrumb = new OCA.Files.BreadCrumb(breadcrumbOptions); var $controls = this.$el.find('#controls'); @@ -379,6 +386,7 @@ // remove summary this.$el.find('tfoot tr.summary').remove(); this.$fileList.empty(); + }, /** @@ -1048,6 +1056,7 @@ $(window).scrollTop(0); this.$fileList.trigger(jQuery.Event('updated')); + _.defer(function() { self.$el.closest('#app-content').trigger(jQuery.Event('apprendered')); }); @@ -1389,6 +1398,10 @@ tr.addClass('hidden-file'); } + if(_.keys(this._shareTreeCache).length) { + filenameTd.find('.thumbnail:not(.sharetree-item)').addClass('sharetree-item'); + } + // display actions this.fileActions.display(filenameTd, !options.silent, this); @@ -1440,21 +1453,32 @@ * @param {boolean} [changeUrl=true] if the URL must not be changed (defaults to true) * @param {boolean} [force=false] set to true to force changing directory * @param {string} [fileId] optional file id, if known, to be appended in the URL + * @return {Promise} empty */ changeDirectory: function(targetDir, changeUrl, force, fileId) { var self = this; var currentDir = this.getCurrentDirectory(); targetDir = targetDir || '/'; - if (!force && currentDir === targetDir) { - return; - } - this._setCurrentDir(targetDir, changeUrl, fileId); - // discard finished uploads list, we'll get it through a regular reload - this._uploads = {}; - this.reload().then(function(success){ - if (!success) { - self.changeDirectory(currentDir, true); + + return new Promise(function(resolve, reject) { + + if (!force && currentDir === targetDir) { + resolve(); + return; } + self._setCurrentDir(targetDir, changeUrl, fileId); + // discard finished uploads list, we'll get it through a regular reload + self._uploads = {}; + self.reload().then(function(success){ + if (!success) { + self.changeDirectory(currentDir, true); + reject(); + } + self._updateShareTree().then(function() { + self._setShareTreeIcons(); + }); + resolve(); + }); }); }, linkTo: function(dir) { @@ -1723,6 +1747,207 @@ return OC.linkToRemoteBase('dav') + '/files/' + uid + encodedPath; }, + /** + * Fetch shareTypes of a certain directory + * @param {String} dir directory string + * @return {Promise} object with name and shareTypes + */ + getDirShareInfo: function(dir) { + // Dir can't be empty + if (typeof dir !== 'string' || dir.length === 0) { + return Promise.reject('getDirShareInfo(). param must be typeof string and can not be empty!'); + } + + // avoiding a unnessesary API calls + if (typeof this._shareTreeCache[dir] !== 'undefined' || dir === '/') { + return Promise.resolve(); + } + + // trim trailing slashes + dir = dir.replace(/\/$/, ""); + + var self = this; + var client = this.filesClient; + var options = { + properties : ['{' + OC.Files.Client.NS_OWNCLOUD + '}share-types'] + }; + + return new Promise( function(resolve, reject) { + client.getFileInfo(dir, options).done(function(s, dir) { + + var path = OC.joinPaths(dir.path, dir.name); + + if (dir.shareTypes !== undefined) { + // Fetch all shares for directory in question + $.get( OC.linkToOCS('apps/files_sharing/api/v1', 2) + 'shares?' + OC.buildQueryString({format: 'json', path: path, reshares : true }), function(e) { + self._shareTreeCache[path] = { + name : dir.name, + shares : e.ocs.data + }; + resolve(); + }); + } else { + resolve(); + } + }).fail(function(error) { + reject(error); + }); + }); + }, + + /** + * Fetch shareInfos recursively from current + * directory down to root + * @param {String} dir directory string + * @return {Promise} array of objects with name and shareTypes + */ + getPathShareInfo: function(path) { + if (typeof path !== 'string') { + console.error('getDirShareInfo(). param must be typeof string!'); + return Promise.reject() + } + + var crumbs = []; + var pathToHere = ''; + var parts = (path === '/' || path.length === 0) ? ['/'] : path.split('/'); + + for (var i = 0; i < parts.length; i++) { + var part = parts[i]; + pathToHere += part + '/'; + + crumbs.push(this.getDirShareInfo(pathToHere)); + } + + return Promise.all(crumbs); + }, + + _updateShareTree: function() { + var self = this; + var dir = this.getCurrentDirectory(); + + // Purge shareTreeCache in root dir + if (dir === '/') { + this._purgeShareTreeCache(); + return Promise.resolve(); + } + + return this.getPathShareInfo(dir).then(function () { + var breadcrumbs = dir.split('/'); + + // Diff keys in shareTreeCache agains the current dir + // removing deeper nested shares + var cache = _.omit(self._shareTreeCache, function(value, key) { + var diffs = _.difference(key.split('/'), breadcrumbs); + return diffs.length > 0; + }); + + self._setShareTreeCache(cache); + }); + }, + + _setShareTreeCache: function(data) { + this._shareTreeCache = data; + }, + + _purgeShareTreeCache: function() { + this._setShareTreeCache({}); + }, + + _setShareTreeIcons: function() { + // Add share-tree icon to files and folders + // each per in the table + + if (_.keys(this._shareTreeCache).length > 0) { + this.$fileList.find('tr td.filename .thumbnail:not(.sharetree-item)').addClass('sharetree-item'); + } else { + this.$fileList.find('tr td.filename .thumbnail.sharetree-item').removeClass('sharetree-item'); + } + }, + + _setShareTreeUserGroupView: function() { + var self = this; + var $list = $('