Merge pull request #1618 from nextcloud/show-folder-shared-status

make it possible to share the current folder
This commit is contained in:
Morris Jobke 2016-11-08 16:23:47 +01:00 committed by GitHub
commit 5f15020b9b
10 changed files with 440 additions and 22 deletions

View file

@ -45,6 +45,7 @@
if (options.getCrumbUrl) {
this.getCrumbUrl = options.getCrumbUrl;
}
this._detailViews = [];
};
/**
* @memberof OCA.Files
@ -52,6 +53,7 @@
BreadCrumb.prototype = {
$el: null,
dir: null,
dirInfo: null,
/**
* Total width of all breadcrumbs
@ -79,6 +81,20 @@
}
},
setDirectoryInfo: function(dirInfo) {
if (dirInfo !== this.dirInfo) {
this.dirInfo = dirInfo;
this.render();
}
},
/**
* @param {Backbone.View} detailView
*/
addDetailView: function(detailView) {
this._detailViews.push(detailView);
},
/**
* Returns the full URL to the given directory
*
@ -122,6 +138,13 @@
}
$crumb.addClass('last');
_.each(this._detailViews, function(view) {
view.render({
dirInfo: this.dirInfo
});
$crumb.append(view.$el);
}, this);
// in case svg is not supported by the browser we need to execute the fallback mechanism
if (!OC.Util.hasSVGSupport()) {
OC.Util.replaceSVG(this.$el);

View file

@ -473,7 +473,7 @@
* Displays the details view for the given file and
* selects the given tab
*
* @param {string} fileName file name for which to show details
* @param {string|OCA.Files.FileInfoModel} fileName file name or FileInfoModel for which to show details
* @param {string} [tabId] optional tab id to select
*/
showDetailsView: function(fileName, tabId) {
@ -487,7 +487,7 @@
/**
* Update the details view to display the given file
*
* @param {string} fileName file name from the current list
* @param {string|OCA.Files.FileInfoModel} fileName file name from the current list or a FileInfoModel object
* @param {boolean} [show=true] whether to open the sidebar if it was closed
*/
_updateDetailsView: function(fileName, show) {
@ -518,13 +518,16 @@
OC.Apps.showAppSidebar(this._detailsView.$el);
}
var $tr = this.findFileEl(fileName);
var model = this.getModelForFile($tr);
if (fileName instanceof OCA.Files.FileInfoModel) {
var model = fileName;
} else {
var $tr = this.findFileEl(fileName);
var model = this.getModelForFile($tr);
$tr.addClass('highlighted');
}
this._currentFileModel = model;
$tr.addClass('highlighted');
this._detailsView.setFileInfo(model);
this._detailsView.$el.scrollTop(0);
},
@ -1646,6 +1649,7 @@
// first entry is the root
this.dirInfo = result.shift();
this.breadcrumb.setDirectoryInfo(this.dirInfo);
if (this.dirInfo.permissions) {
this.setDirectoryPermissions(this.dirInfo.permissions);
@ -2021,7 +2025,7 @@
function updateInList(fileInfo) {
self.updateRow(tr, fileInfo);
self._updateDetailsView(fileInfo.name, false);
self._updateDetailsView(fileInfo, false);
}
// TODO: too many nested blocks, move parts into functions
@ -2954,6 +2958,15 @@
if (this._detailsView) {
this._detailsView.addDetailView(detailView);
}
},
/**
* Register a view to be added to the breadcrumb view
*/
registerBreadCrumbDetailView: function(detailView) {
if (this.breadcrumb) {
this.breadcrumb.addDetailView(detailView);
}
}
};

View file

@ -42,7 +42,9 @@ $eventDispatcher->addListener(
function() {
\OCP\Util::addScript('files_sharing', 'share');
\OCP\Util::addScript('files_sharing', 'sharetabview');
\OCP\Util::addScript('files_sharing', 'sharebreadcrumbview');
\OCP\Util::addStyle('files_sharing', 'sharetabview');
\OCP\Util::addStyle('files_sharing', 'sharebreadcrumb');
}
);

View file

@ -0,0 +1,34 @@
/**
* @copyright 2016 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @author 2016 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 program. If not, see <http://www.gnu.org/licenses/>.
*
*/
div.crumb span.icon-share,
div.crumb span.icon-public {
display: inline-block;
cursor: pointer;
opacity: 0.2;
margin-right: 6px;
}
div.crumb span.icon-share.shared,
div.crumb span.icon-public.shared {
opacity: 0.7;
}

View file

@ -36,19 +36,7 @@
var oldCreateRow = fileList._createRow;
fileList._createRow = function(fileData) {
var tr = oldCreateRow.apply(this, arguments);
var sharePermissions = fileData.permissions;
if (fileData.mountType && fileData.mountType === "external-root"){
// for external storages we can't use the permissions of the mountpoint
// instead we show all permissions and only use the share permissions from the mountpoint to handle resharing
sharePermissions = sharePermissions | (OC.PERMISSION_ALL & ~OC.PERMISSION_SHARE);
}
if (fileData.type === 'file') {
// files can't be shared with delete permissions
sharePermissions = sharePermissions & ~OC.PERMISSION_DELETE;
// create permissions don't mean anything for files
sharePermissions = sharePermissions & ~OC.PERMISSION_CREATE;
}
var sharePermissions = OCA.Sharing.Util.getSharePermissions(fileData);
tr.attr('data-share-permissions', sharePermissions);
if (fileData.shareOwner) {
tr.attr('data-share-owner', fileData.shareOwner);
@ -185,6 +173,9 @@
}
});
fileList.registerTabView(shareTab);
var breadCrumbSharingDetailView = new OCA.Sharing.ShareBreadCrumbView({shareTab: shareTab});
fileList.registerBreadCrumbDetailView(breadCrumbSharingDetailView);
},
/**
@ -248,6 +239,27 @@
text += ', +' + (count - maxRecipients);
}
return text;
},
/**
* @param {Array} fileData
* @returns {String}
*/
getSharePermissions: function(fileData) {
var sharePermissions = fileData.permissions;
if (fileData.mountType && fileData.mountType === "external-root"){
// for external storages we can't use the permissions of the mountpoint
// instead we show all permissions and only use the share permissions from the mountpoint to handle resharing
sharePermissions = sharePermissions | (OC.PERMISSION_ALL & ~OC.PERMISSION_SHARE);
}
if (fileData.type === 'file') {
// files can't be shared with delete permissions
sharePermissions = sharePermissions & ~OC.PERMISSION_DELETE;
// create permissions don't mean anything for files
sharePermissions = sharePermissions & ~OC.PERMISSION_CREATE;
}
return sharePermissions;
}
};
})();

View file

@ -0,0 +1,103 @@
/* global Handlebars, OC */
/**
* @copyright 2016 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @author 2016 Christoph Wurst <christoph@winzerhof-wurst.at>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 program. If not, see <http://www.gnu.org/licenses/>.
*
*/
(function() {
'use strict';
var BreadCrumbView = OC.Backbone.View.extend({
tagName: 'span',
events: {
click: '_onClick'
},
_dirInfo: undefined,
/** @type OCA.Sharing.ShareTabView */
_shareTab: undefined,
initialize: function(options) {
this._shareTab = options.shareTab;
},
render: function(data) {
this._dirInfo = data.dirInfo || null;
if (this._dirInfo !== null && (this._dirInfo.path !== '/' || this._dirInfo.name !== '')) {
var isShared = data.dirInfo && data.dirInfo.shareTypes && data.dirInfo.shareTypes.length > 0;
this.$el.removeClass('shared icon-public icon-share');
if (isShared) {
this.$el.addClass('shared');
if (data.dirInfo.shareTypes.indexOf(OC.Share.SHARE_TYPE_LINK) !== -1) {
this.$el.addClass('icon-public');
} else {
this.$el.addClass('icon-share');
}
} else {
this.$el.addClass('icon-share');
}
this.$el.show();
this.delegateEvents();
} else {
this.$el.removeClass('shared icon-public icon-share');
this.$el.hide();
}
return this;
},
_onClick: function(e) {
e.preventDefault();
var fileInfoModel = new OCA.Files.FileInfoModel(this._dirInfo);
var self = this;
fileInfoModel.on('change', function() {
self.render({
dirInfo: self._dirInfo
});
});
this._shareTab.on('sharesChanged', function(shareModel) {
var shareTypes = [];
var shares = shareModel.getSharesWithCurrentItem();
for(var i = 0; i < shares.length; i++) {
if (shareTypes.indexOf(shares[i].share_type) === -1) {
shareTypes.push(shares[i].share_type);
}
}
if (shareModel.hasLinkShare()) {
shareTypes.push(OC.Share.SHARE_TYPE_LINK);
}
// Since the dirInfo isn't updated we need to do this dark hackery
self._dirInfo.shareTypes = shareTypes;
self.render({
dirInfo: self._dirInfo
});
});
OCA.Files.App.fileList.showDetailsView(fileInfoModel, 'shareTabView');
}
});
OCA.Sharing.ShareBreadCrumbView = BreadCrumbView;
})();

View file

@ -50,6 +50,10 @@
if (this.model) {
this.$el.html(this.template());
if (_.isUndefined(this.model.get('sharePermissions'))) {
this.model.set('sharePermissions', OCA.Sharing.Util.getSharePermissions(this.model.attributes));
}
// TODO: the model should read these directly off the passed fileInfoModel
var attributes = {
itemType: this.model.isDirectory() ? 'folder' : 'file',

View file

@ -0,0 +1,224 @@
/**
* @copyright 2016, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 program. If not, see <http://www.gnu.org/licenses/>.
*
*/
describe('OCA.Sharing.ShareBreadCrumbView tests', function() {
var BreadCrumb = OCA.Files.BreadCrumb;
var SharedBreadCrum = OCA.Sharing.ShareBreadCrumbView;
describe('Rendering', function() {
var bc;
var sbc;
var shareTab;
beforeEach(function() {
bc = new BreadCrumb({
getCrumbUrl: function(part, index) {
// for testing purposes
return part.dir + '#' + index;
}
});
shareTab = new OCA.Sharing.ShareTabView();
sbc = new SharedBreadCrum({
shareTab: shareTab
});
bc.addDetailView(sbc);
});
afterEach(function() {
bc = null;
sbc = null;
shareModel = null;
});
it('Do not render in root', function() {
var dirInfo = new OC.Files.FileInfo({
id: 42,
path: '/',
type: 'dir',
name: ''
});
bc.setDirectoryInfo(dirInfo);
bc.setDirectory('');
bc.render();
expect(bc.$el.hasClass('breadcrumb')).toEqual(true);
expect(bc.$el.find('.icon-share').length).toEqual(0);
expect(bc.$el.find('.shared').length).toEqual(0);
expect(bc.$el.find('.icon-public').length).toEqual(0);
});
it('Render in dir', function() {
var dirInfo = new OC.Files.FileInfo({
id: 42,
path: '/foo',
type: 'dir'
});
bc.setDirectoryInfo(dirInfo);
bc.setDirectory('/foo');
bc.render();
expect(bc.$el.hasClass('breadcrumb')).toEqual(true);
expect(bc.$el.find('.icon-share').length).toEqual(1);
expect(bc.$el.find('.shared').length).toEqual(0);
expect(bc.$el.find('.icon-public').length).toEqual(0);
});
it('Render shared if dir is shared with user', function() {
var dirInfo = new OC.Files.FileInfo({
id: 42,
path: '/foo',
type: 'dir',
shareTypes: [OC.Share.SHARE_TYPE_USER]
});
bc.setDirectoryInfo(dirInfo);
bc.setDirectory('/foo');
bc.render();
expect(bc.$el.hasClass('breadcrumb')).toEqual(true);
expect(bc.$el.find('.icon-share').length).toEqual(1);
expect(bc.$el.find('.shared').length).toEqual(1);
expect(bc.$el.find('.icon-public').length).toEqual(0);
});
it('Render shared if dir is shared with group', function() {
var dirInfo = new OC.Files.FileInfo({
id: 42,
path: '/foo',
type: 'dir',
shareTypes: [OC.Share.SHARE_TYPE_GROUP]
});
bc.setDirectoryInfo(dirInfo);
bc.setDirectory('/foo');
bc.render();
expect(bc.$el.hasClass('breadcrumb')).toEqual(true);
expect(bc.$el.find('.icon-share').length).toEqual(1);
expect(bc.$el.find('.shared').length).toEqual(1);
expect(bc.$el.find('.icon-public').length).toEqual(0);
});
it('Render shared if dir is shared by link', function() {
var dirInfo = new OC.Files.FileInfo({
id: 42,
path: '/foo',
type: 'dir',
shareTypes: [OC.Share.SHARE_TYPE_LINK]
});
bc.setDirectoryInfo(dirInfo);
bc.setDirectory('/foo');
bc.render();
expect(bc.$el.hasClass('breadcrumb')).toEqual(true);
expect(bc.$el.find('.icon-share').length).toEqual(0);
expect(bc.$el.find('.shared').length).toEqual(1);
expect(bc.$el.find('.icon-public').length).toEqual(1);
});
it('Render shared if dir is shared with remote', function() {
var dirInfo = new OC.Files.FileInfo({
id: 42,
path: '/foo',
type: 'dir',
shareTypes: [OC.Share.SHARE_TYPE_REMOTE]
});
bc.setDirectoryInfo(dirInfo);
bc.setDirectory('/foo');
bc.render();
expect(bc.$el.hasClass('breadcrumb')).toEqual(true);
expect(bc.$el.find('.icon-share').length).toEqual(1);
expect(bc.$el.find('.shared').length).toEqual(1);
expect(bc.$el.find('.icon-public').length).toEqual(0);
});
it('Render link shared if at least one is a link share', function() {
var dirInfo = new OC.Files.FileInfo({
id: 42,
path: '/foo',
type: 'dir',
shareTypes: [
OC.Share.SHARE_TYPE_USER,
OC.Share.SHARE_TYPE_GROUP,
OC.Share.SHARE_TYPE_LINK,
OC.Share.SHARE_TYPE_EMAIL,
OC.Share.SHARE_TYPE_REMOTE
]
});
bc.setDirectoryInfo(dirInfo);
bc.setDirectory('/foo');
bc.render();
expect(bc.$el.hasClass('breadcrumb')).toEqual(true);
expect(bc.$el.find('.icon-share').length).toEqual(0);
expect(bc.$el.find('.shared').length).toEqual(1);
expect(bc.$el.find('.icon-public').length).toEqual(1);
});
it('Remove shared status from user share', function() {
var dirInfo = new OC.Files.FileInfo({
id: 42,
path: '/foo',
type: 'dir',
shareTypes: [OC.Share.SHARE_TYPE_USER]
});
bc.setDirectory('/foo');
bc.setDirectoryInfo(dirInfo);
bc.render();
var mock = sinon.createStubInstance(OCA.Files.FileList);
mock.showDetailsView = function() { };
OCA.Files.App.fileList = mock;
var spy = sinon.spy(mock, 'showDetailsView');
bc.$el.find('.icon-share').click();
expect(spy.calledOnce).toEqual(true);
var model = sinon.createStubInstance(OC.Share.ShareItemModel);
model.getSharesWithCurrentItem = function() { return [] };
model.hasLinkShare = function() { return false; };
shareTab.trigger('sharesChanged', model);
expect(bc.$el.hasClass('breadcrumb')).toEqual(true);
expect(bc.$el.find('.icon-share').length).toEqual(1);
expect(bc.$el.find('.shared').length).toEqual(0);
expect(bc.$el.find('.icon-public').length).toEqual(0);
});
it('Add link share to user share', function() {
var dirInfo = new OC.Files.FileInfo({
id: 42,
path: '/foo',
type: 'dir',
shareTypes: [OC.Share.SHARE_TYPE_USER]
});
bc.setDirectory('/foo');
bc.setDirectoryInfo(dirInfo);
bc.render();
var mock = sinon.createStubInstance(OCA.Files.FileList);
mock.showDetailsView = function() { };
OCA.Files.App.fileList = mock;
var spy = sinon.spy(mock, 'showDetailsView');
bc.$el.find('.icon-share').click();
expect(spy.calledOnce).toEqual(true);
var model = sinon.createStubInstance(OC.Share.ShareItemModel);
model.getSharesWithCurrentItem = function() { return [
{share_type: OC.Share.SHARE_TYPE_USER}
] };
model.hasLinkShare = function() { return true; };
shareTab.trigger('sharesChanged', model);
expect(bc.$el.hasClass('breadcrumb')).toEqual(true);
expect(bc.$el.find('.icon-share').length).toEqual(0);
expect(bc.$el.find('.shared').length).toEqual(1);
expect(bc.$el.find('.icon-public').length).toEqual(1);
});
});
});

View file

@ -894,12 +894,15 @@ div.crumb.hidden {
display: none;
}
div.crumb a,
div.crumb span {
div.crumb > span {
position: relative;
top: 12px;
padding: 14px 24px 14px 17px;
color: #555;
}
div.crumb.last a {
padding-right: 0px;
}
div.crumb:first-child a {
position: relative;
top: 13px;

View file

@ -54,7 +54,7 @@ module.exports = function(config) {
'apps/files_sharing/js/app.js',
'apps/files_sharing/js/sharedfilelist.js',
'apps/files_sharing/js/share.js',
'apps/files_sharing/js/external.js',
'apps/files_sharing/js/sharebreadcrumbview.js',
'apps/files_sharing/js/public.js',
'apps/files_sharing/js/sharetabview.js'
],