aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/files/js/breadcrumb.js23
-rw-r--r--apps/files/js/filelist.js27
-rw-r--r--apps/files_sharing/appinfo/app.php2
-rw-r--r--apps/files_sharing/css/sharebreadcrumb.css34
-rw-r--r--apps/files_sharing/js/share.js38
-rw-r--r--apps/files_sharing/js/sharebreadcrumbview.js103
-rw-r--r--apps/files_sharing/js/sharetabview.js4
-rw-r--r--apps/files_sharing/tests/js/sharedbreadcrumviewSpec.js224
-rw-r--r--core/css/styles.css5
-rw-r--r--tests/karma.config.js2
10 files changed, 440 insertions, 22 deletions
diff --git a/apps/files/js/breadcrumb.js b/apps/files/js/breadcrumb.js
index 98de7aa374c..ff9700456da 100644
--- a/apps/files/js/breadcrumb.js
+++ b/apps/files/js/breadcrumb.js
@@ -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);
diff --git a/apps/files/js/filelist.js b/apps/files/js/filelist.js
index 18534db3ee9..c53fa4f3d66 100644
--- a/apps/files/js/filelist.js
+++ b/apps/files/js/filelist.js
@@ -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);
+ }
}
};
diff --git a/apps/files_sharing/appinfo/app.php b/apps/files_sharing/appinfo/app.php
index 7da295afddd..850c2c05977 100644
--- a/apps/files_sharing/appinfo/app.php
+++ b/apps/files_sharing/appinfo/app.php
@@ -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');
}
);
diff --git a/apps/files_sharing/css/sharebreadcrumb.css b/apps/files_sharing/css/sharebreadcrumb.css
new file mode 100644
index 00000000000..792c8a31b7c
--- /dev/null
+++ b/apps/files_sharing/css/sharebreadcrumb.css
@@ -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;
+}
diff --git a/apps/files_sharing/js/share.js b/apps/files_sharing/js/share.js
index 64fc7ef7296..073cc77e5aa 100644
--- a/apps/files_sharing/js/share.js
+++ b/apps/files_sharing/js/share.js
@@ -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;
}
};
})();
diff --git a/apps/files_sharing/js/sharebreadcrumbview.js b/apps/files_sharing/js/sharebreadcrumbview.js
new file mode 100644
index 00000000000..36bbddfaec4
--- /dev/null
+++ b/apps/files_sharing/js/sharebreadcrumbview.js
@@ -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;
+})();
diff --git a/apps/files_sharing/js/sharetabview.js b/apps/files_sharing/js/sharetabview.js
index 2c7070aa3d5..7bb1f1229d0 100644
--- a/apps/files_sharing/js/sharetabview.js
+++ b/apps/files_sharing/js/sharetabview.js
@@ -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',
diff --git a/apps/files_sharing/tests/js/sharedbreadcrumviewSpec.js b/apps/files_sharing/tests/js/sharedbreadcrumviewSpec.js
new file mode 100644
index 00000000000..b2193f4458b
--- /dev/null
+++ b/apps/files_sharing/tests/js/sharedbreadcrumviewSpec.js
@@ -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);
+ });
+ });
+});
diff --git a/core/css/styles.css b/core/css/styles.css
index d94a31c4cf1..84d3229bda9 100644
--- a/core/css/styles.css
+++ b/core/css/styles.css
@@ -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;
diff --git a/tests/karma.config.js b/tests/karma.config.js
index 111af7a1559..d80b5bbd759 100644
--- a/tests/karma.config.js
+++ b/tests/karma.config.js
@@ -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'
],