mirror of
https://github.com/nextcloud/server.git
synced 2026-05-13 08:59:42 -04:00
The custom handler for "URL changed" events were added to reload the file list whenever the sections for favorites and shares were opened; this was used to fix the problem of not reloading the file lists when opening them for a second time. However, besides that the handlers were not really necessary, and as the root of the bug was fixed in the previous commit those handlers are now removed. The file list for tags uses the handler for a different purpose, though, so that one was kept. Signed-off-by: Daniel Calviño Sánchez <danxuliu@gmail.com>
488 lines
14 KiB
JavaScript
488 lines
14 KiB
JavaScript
/*
|
|
* 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() {
|
|
|
|
/**
|
|
* @class OCA.Sharing.FileList
|
|
* @augments OCA.Files.FileList
|
|
*
|
|
* @classdesc Sharing file list.
|
|
* Contains both "shared with others" and "shared with you" modes.
|
|
*
|
|
* @param $el container element with existing markup for the #controls
|
|
* and a table
|
|
* @param [options] map of options, see other parameters
|
|
* @param {boolean} [options.sharedWithUser] true to return files shared with
|
|
* the current user, false to return files that the user shared with others.
|
|
* Defaults to false.
|
|
* @param {boolean} [options.linksOnly] true to return only link shares
|
|
*/
|
|
var FileList = function($el, options) {
|
|
this.initialize($el, options);
|
|
};
|
|
FileList.prototype = _.extend({}, OCA.Files.FileList.prototype,
|
|
/** @lends OCA.Sharing.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,
|
|
_linksOnly: false,
|
|
_showDeleted: false,
|
|
_clientSideSort: true,
|
|
_allowSelection: false,
|
|
_isOverview: false,
|
|
|
|
/**
|
|
* @private
|
|
*/
|
|
initialize: function($el, options) {
|
|
OCA.Files.FileList.prototype.initialize.apply(this, arguments);
|
|
if (this.initialized) {
|
|
return;
|
|
}
|
|
|
|
// TODO: consolidate both options
|
|
if (options && options.sharedWithUser) {
|
|
this._sharedWithUser = true;
|
|
}
|
|
if (options && options.linksOnly) {
|
|
this._linksOnly = true;
|
|
}
|
|
if (options && options.showDeleted) {
|
|
this._showDeleted = true;
|
|
}
|
|
if (options && options.isOverview) {
|
|
this._isOverview = true;
|
|
}
|
|
},
|
|
|
|
_renderRow: function() {
|
|
// HACK: needed to call the overridden _renderRow
|
|
// this is because at the time this class is created
|
|
// the overriding hasn't been done yet...
|
|
return OCA.Files.FileList.prototype._renderRow.apply(this, arguments);
|
|
},
|
|
|
|
_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.shareOwner);
|
|
$tr.attr('data-mounttype', 'shared-root');
|
|
var permission = parseInt($tr.attr('data-permissions')) | OC.PERMISSION_DELETE;
|
|
$tr.attr('data-permissions', permission);
|
|
}
|
|
if (this._showDeleted) {
|
|
var permission = fileData.permissions;
|
|
$tr.attr('data-share-permissions', permission);
|
|
}
|
|
|
|
// add row with expiration date for link only shares - influenced by _createRow of filelist
|
|
if (this._linksOnly) {
|
|
var expirationTimestamp = 0;
|
|
if(fileData.shares && fileData.shares[0].expiration !== null) {
|
|
expirationTimestamp = moment(fileData.shares[0].expiration).valueOf();
|
|
}
|
|
$tr.attr('data-expiration', expirationTimestamp);
|
|
|
|
// date column (1000 milliseconds to seconds, 60 seconds, 60 minutes, 24 hours)
|
|
// difference in days multiplied by 5 - brightest shade for expiry dates in more than 32 days (160/5)
|
|
var modifiedColor = Math.round((expirationTimestamp - (new Date()).getTime()) / 1000 / 60 / 60 / 24 * 5);
|
|
// ensure that the brightest color is still readable
|
|
if (modifiedColor >= 160) {
|
|
modifiedColor = 160;
|
|
}
|
|
|
|
if (expirationTimestamp > 0) {
|
|
formatted = OC.Util.formatDate(expirationTimestamp);
|
|
text = OC.Util.relativeModifiedDate(expirationTimestamp);
|
|
} else {
|
|
formatted = t('files_sharing', 'No expiration date set');
|
|
text = '';
|
|
modifiedColor = 160;
|
|
}
|
|
td = $('<td></td>').attr({"class": "date"});
|
|
td.append($('<span></span>').attr({
|
|
"class": "modified",
|
|
"title": formatted,
|
|
"style": 'color:rgb(' + modifiedColor + ',' + modifiedColor + ',' + modifiedColor + ')'
|
|
}).text(text)
|
|
.tooltip({placement: 'top'})
|
|
);
|
|
|
|
$tr.append(td);
|
|
}
|
|
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);
|
|
|
|
// hide expiration date header for non link only shares
|
|
if (!this._linksOnly) {
|
|
this.$el.find('th.column-expiration').addClass('hidden');
|
|
}
|
|
}
|
|
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
|
|
},
|
|
|
|
updateRow: function($tr, fileInfo, options) {
|
|
// no-op, suppress re-rendering
|
|
return $tr;
|
|
},
|
|
|
|
reload: function() {
|
|
this.showMask();
|
|
if (this._reloadCall) {
|
|
this._reloadCall.abort();
|
|
}
|
|
|
|
// there is only root
|
|
this._setCurrentDir('/', false);
|
|
|
|
var promises = [];
|
|
|
|
var deletedShares = {
|
|
url: OC.linkToOCS('apps/files_sharing/api/v1', 2) + 'deletedshares',
|
|
/* jshint camelcase: false */
|
|
data: {
|
|
format: 'json',
|
|
include_tags: true
|
|
},
|
|
type: 'GET',
|
|
beforeSend: function (xhr) {
|
|
xhr.setRequestHeader('OCS-APIREQUEST', 'true');
|
|
},
|
|
};
|
|
|
|
var shares = {
|
|
url: OC.linkToOCS('apps/files_sharing/api/v1') + 'shares',
|
|
/* jshint camelcase: false */
|
|
data: {
|
|
format: 'json',
|
|
shared_with_me: this._sharedWithUser !== false,
|
|
include_tags: true
|
|
},
|
|
type: 'GET',
|
|
beforeSend: function (xhr) {
|
|
xhr.setRequestHeader('OCS-APIREQUEST', 'true');
|
|
},
|
|
};
|
|
|
|
var remoteShares = {
|
|
url: OC.linkToOCS('apps/files_sharing/api/v1') + 'remote_shares',
|
|
/* jshint camelcase: false */
|
|
data: {
|
|
format: 'json',
|
|
include_tags: true
|
|
},
|
|
type: 'GET',
|
|
beforeSend: function (xhr) {
|
|
xhr.setRequestHeader('OCS-APIREQUEST', 'true');
|
|
},
|
|
};
|
|
|
|
// Add the proper ajax requests to the list and run them
|
|
// and make sure we have 2 promises
|
|
if (this._showDeleted) {
|
|
promises.push($.ajax(deletedShares));
|
|
} else {
|
|
promises.push($.ajax(shares));
|
|
|
|
if (this._sharedWithUser !== false || this._isOverview) {
|
|
promises.push($.ajax(remoteShares));
|
|
}
|
|
if (this._isOverview) {
|
|
shares.data.shared_with_me = !shares.data.shared_with_me
|
|
promises.push($.ajax(shares));
|
|
}
|
|
}
|
|
|
|
this._reloadCall = $.when.apply($, promises);
|
|
var callBack = this.reloadCallback.bind(this);
|
|
return this._reloadCall.then(callBack, callBack);
|
|
},
|
|
|
|
reloadCallback: function(shares, remoteShares, additionnalShares) {
|
|
delete this._reloadCall;
|
|
this.hideMask();
|
|
|
|
this.$el.find('#headerSharedWith').text(
|
|
t('files_sharing', this._sharedWithUser ? 'Shared by' : 'Shared with')
|
|
);
|
|
|
|
var files = [];
|
|
|
|
// make sure to use the same format
|
|
if (shares[0] && shares[0].ocs) {
|
|
shares = shares[0];
|
|
}
|
|
if (remoteShares && remoteShares[0] && remoteShares[0].ocs) {
|
|
remoteShares = remoteShares[0];
|
|
}
|
|
if (additionnalShares && additionnalShares[0] && additionnalShares[0].ocs) {
|
|
additionnalShares = additionnalShares[0];
|
|
}
|
|
|
|
if (shares.ocs && shares.ocs.data) {
|
|
files = files.concat(this._makeFilesFromShares(shares.ocs.data, this._sharedWithUser));
|
|
}
|
|
|
|
if (remoteShares && remoteShares.ocs && remoteShares.ocs.data) {
|
|
files = files.concat(this._makeFilesFromRemoteShares(remoteShares.ocs.data));
|
|
}
|
|
|
|
if (additionnalShares && additionnalShares && additionnalShares.ocs && additionnalShares.ocs.data) {
|
|
files = files.concat(this._makeFilesFromShares(additionnalShares.ocs.data, !this._sharedWithUser));
|
|
}
|
|
|
|
|
|
this.setFiles(files);
|
|
return true;
|
|
},
|
|
|
|
_makeFilesFromRemoteShares: function(data) {
|
|
var files = data;
|
|
|
|
files = _.chain(files)
|
|
// convert share data to file data
|
|
.map(function(share) {
|
|
var file = {
|
|
shareOwner: share.owner + '@' + share.remote.replace(/.*?:\/\//g, ""),
|
|
name: OC.basename(share.mountpoint),
|
|
mtime: share.mtime * 1000,
|
|
mimetype: share.mimetype,
|
|
type: share.type,
|
|
id: share.file_id,
|
|
path: OC.dirname(share.mountpoint),
|
|
permissions: share.permissions,
|
|
tags: share.tags || []
|
|
};
|
|
|
|
file.shares = [{
|
|
id: share.id,
|
|
type: OC.Share.SHARE_TYPE_REMOTE
|
|
}];
|
|
return file;
|
|
})
|
|
.value();
|
|
return files;
|
|
},
|
|
|
|
/**
|
|
* Converts the OCS API share response data to a file info
|
|
* list
|
|
* @param {Array} data OCS API share array
|
|
* @param {bool} sharedWithUser
|
|
* @return {Array.<OCA.Sharing.SharedFileInfo>} array of shared file info
|
|
*/
|
|
_makeFilesFromShares: function(data, sharedWithUser) {
|
|
/* jshint camelcase: false */
|
|
var files = data;
|
|
|
|
if (this._linksOnly) {
|
|
files = _.filter(data, function(share) {
|
|
return share.share_type === OC.Share.SHARE_TYPE_LINK;
|
|
});
|
|
}
|
|
|
|
// OCS API uses non-camelcased names
|
|
files = _.chain(files)
|
|
// convert share data to file data
|
|
.map(function(share) {
|
|
// TODO: use OC.Files.FileInfo
|
|
var file = {
|
|
id: share.file_source,
|
|
icon: OC.MimeType.getIconUrl(share.mimetype),
|
|
mimetype: share.mimetype,
|
|
tags: share.tags || []
|
|
};
|
|
if (share.item_type === 'folder') {
|
|
file.type = 'dir';
|
|
file.mimetype = 'httpd/unix-directory';
|
|
}
|
|
else {
|
|
file.type = 'file';
|
|
}
|
|
file.share = {
|
|
id: share.id,
|
|
type: share.share_type,
|
|
target: share.share_with,
|
|
stime: share.stime * 1000,
|
|
expiration: share.expiration,
|
|
};
|
|
if (sharedWithUser) {
|
|
file.shareOwner = share.displayname_owner;
|
|
file.shareOwnerId = share.uid_owner;
|
|
file.name = OC.basename(share.file_target);
|
|
file.path = OC.dirname(share.file_target);
|
|
file.permissions = share.permissions;
|
|
if (file.path) {
|
|
file.extraData = share.file_target;
|
|
}
|
|
}
|
|
else {
|
|
if (share.share_type !== OC.Share.SHARE_TYPE_LINK) {
|
|
file.share.targetDisplayName = share.share_with_displayname;
|
|
file.share.targetShareWithId = share.share_with;
|
|
}
|
|
file.name = OC.basename(share.path);
|
|
file.path = OC.dirname(share.path);
|
|
file.permissions = OC.PERMISSION_ALL;
|
|
if (file.path) {
|
|
file.extraData = 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];
|
|
var recipient = file.share.targetDisplayName;
|
|
var recipientId = file.share.targetShareWithId;
|
|
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.recipients = {};
|
|
data.recipientData = {};
|
|
// share types
|
|
data.shareTypes = {};
|
|
// counter is cheaper than calling _.keys().length
|
|
data.recipientsCount = 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 (recipient) {
|
|
// limit counterparts for output
|
|
if (data.recipientsCount < 4) {
|
|
// only store the first ones, they will be the only ones
|
|
// displayed
|
|
data.recipients[recipient] = true;
|
|
data.recipientData[data.recipientsCount] = {
|
|
'shareWith': recipientId,
|
|
'shareWithDisplayName': recipient
|
|
};
|
|
}
|
|
data.recipientsCount++;
|
|
}
|
|
|
|
data.shareTypes[file.share.type] = true;
|
|
|
|
delete file.share;
|
|
return memo;
|
|
}, {})
|
|
// Retrieve only the values of the returned hash
|
|
.values()
|
|
// Clean up
|
|
.each(function(data) {
|
|
// convert the recipients map to a flat
|
|
// array of sorted names
|
|
data.mountType = 'shared';
|
|
delete data.recipientsCount;
|
|
if (sharedWithUser) {
|
|
// only for outgoing shares
|
|
delete data.shareTypes;
|
|
} else {
|
|
data.shareTypes = _.keys(data.shareTypes);
|
|
}
|
|
})
|
|
// Finish the chain by getting the result
|
|
.value();
|
|
|
|
// Sort by expected sort comparator
|
|
return files.sort(this._sortComparator);
|
|
},
|
|
});
|
|
|
|
/**
|
|
* Share info attributes.
|
|
*
|
|
* @typedef {Object} OCA.Sharing.ShareInfo
|
|
*
|
|
* @property {int} id share ID
|
|
* @property {int} type share type
|
|
* @property {String} target share target, either user name or group name
|
|
* @property {int} stime share timestamp in milliseconds
|
|
* @property {String} [targetDisplayName] display name of the recipient
|
|
* (only when shared with others)
|
|
* @property {String} [targetShareWithId] id of the recipient
|
|
*
|
|
*/
|
|
|
|
/**
|
|
* Recipient attributes
|
|
*
|
|
* @typedef {Object} OCA.Sharing.RecipientInfo
|
|
* @property {String} shareWith the id of the recipient
|
|
* @property {String} shareWithDisplayName the display name of the recipient
|
|
*/
|
|
|
|
/**
|
|
* Shared file info attributes.
|
|
*
|
|
* @typedef {OCA.Files.FileInfo} OCA.Sharing.SharedFileInfo
|
|
*
|
|
* @property {Array.<OCA.Sharing.ShareInfo>} shares array of shares for
|
|
* this file
|
|
* @property {int} mtime most recent share time (if multiple shares)
|
|
* @property {String} shareOwner name of the share owner
|
|
* @property {Array.<String>} recipients name of the first 4 recipients
|
|
* (this is mostly for display purposes)
|
|
* @property {Object.<OCA.Sharing.RecipientInfo>} recipientData (as object for easier
|
|
* passing to HTML data attributes with jQuery)
|
|
*/
|
|
|
|
OCA.Sharing.FileList = FileList;
|
|
})();
|