nextcloud /
server
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | /* |
||
| 2 | * Copyright (c) 2014 Vincent Petry <[email protected]> |
||
| 3 | * |
||
| 4 | * This file is licensed under the Affero General Public License version 3 |
||
| 5 | * or later. |
||
| 6 | * |
||
| 7 | * See the COPYING-README file. |
||
| 8 | * |
||
| 9 | */ |
||
| 10 | (function() { |
||
| 11 | |||
| 12 | /** |
||
| 13 | * @class OCA.Sharing.FileList |
||
| 14 | * @augments OCA.Files.FileList |
||
| 15 | * |
||
| 16 | * @classdesc Sharing file list. |
||
| 17 | * Contains both "shared with others" and "shared with you" modes. |
||
| 18 | * |
||
| 19 | * @param $el container element with existing markup for the #controls |
||
| 20 | * and a table |
||
| 21 | * @param [options] map of options, see other parameters |
||
| 22 | * @param {boolean} [options.sharedWithUser] true to return files shared with |
||
| 23 | * the current user, false to return files that the user shared with others. |
||
| 24 | * Defaults to false. |
||
| 25 | * @param {boolean} [options.linksOnly] true to return only link shares |
||
| 26 | */ |
||
| 27 | var FileList = function($el, options) { |
||
| 28 | this.initialize($el, options); |
||
| 29 | }; |
||
| 30 | FileList.prototype = _.extend({}, OCA.Files.FileList.prototype, |
||
| 31 | /** @lends OCA.Sharing.FileList.prototype */ { |
||
| 32 | appName: 'Shares', |
||
| 33 | |||
| 34 | /** |
||
| 35 | * Whether the list shows the files shared with the user (true) or |
||
| 36 | * the files that the user shared with others (false). |
||
| 37 | */ |
||
| 38 | _sharedWithUser: false, |
||
| 39 | _linksOnly: false, |
||
| 40 | _clientSideSort: true, |
||
| 41 | _allowSelection: false, |
||
| 42 | |||
| 43 | /** |
||
| 44 | * @private |
||
| 45 | */ |
||
| 46 | initialize: function($el, options) { |
||
| 47 | OCA.Files.FileList.prototype.initialize.apply(this, arguments); |
||
| 48 | if (this.initialized) { |
||
| 49 | return; |
||
| 50 | } |
||
| 51 | |||
| 52 | // TODO: consolidate both options |
||
| 53 | if (options && options.sharedWithUser) { |
||
| 54 | this._sharedWithUser = true; |
||
| 55 | } |
||
| 56 | if (options && options.linksOnly) { |
||
| 57 | this._linksOnly = true; |
||
| 58 | } |
||
| 59 | OC.Plugins.attach('OCA.Sharing.FileList', this); |
||
| 60 | }, |
||
| 61 | |||
| 62 | _renderRow: function() { |
||
| 63 | // HACK: needed to call the overridden _renderRow |
||
| 64 | // this is because at the time this class is created |
||
| 65 | // the overriding hasn't been done yet... |
||
| 66 | return OCA.Files.FileList.prototype._renderRow.apply(this, arguments); |
||
| 67 | }, |
||
| 68 | |||
| 69 | _createRow: function(fileData) { |
||
| 70 | // TODO: hook earlier and render the whole row here |
||
| 71 | var $tr = OCA.Files.FileList.prototype._createRow.apply(this, arguments); |
||
| 72 | $tr.find('.filesize').remove(); |
||
| 73 | $tr.find('td.date').before($tr.children('td:first')); |
||
| 74 | $tr.find('td.filename input:checkbox').remove(); |
||
| 75 | $tr.attr('data-share-id', _.pluck(fileData.shares, 'id').join(',')); |
||
| 76 | if (this._sharedWithUser) { |
||
| 77 | $tr.attr('data-share-owner', fileData.shareOwner); |
||
| 78 | $tr.attr('data-mounttype', 'shared-root'); |
||
| 79 | var permission = parseInt($tr.attr('data-permissions')) | OC.PERMISSION_DELETE; |
||
| 80 | $tr.attr('data-permissions', permission); |
||
| 81 | } |
||
| 82 | return $tr; |
||
| 83 | }, |
||
| 84 | |||
| 85 | /** |
||
| 86 | * Set whether the list should contain outgoing shares |
||
| 87 | * or incoming shares. |
||
| 88 | * |
||
| 89 | * @param state true for incoming shares, false otherwise |
||
| 90 | */ |
||
| 91 | setSharedWithUser: function(state) { |
||
| 92 | this._sharedWithUser = !!state; |
||
| 93 | }, |
||
| 94 | |||
| 95 | updateEmptyContent: function() { |
||
| 96 | var dir = this.getCurrentDirectory(); |
||
| 97 | if (dir === '/') { |
||
| 98 | // root has special permissions |
||
| 99 | this.$el.find('#emptycontent').toggleClass('hidden', !this.isEmpty); |
||
| 100 | this.$el.find('#filestable thead th').toggleClass('hidden', this.isEmpty); |
||
| 101 | } |
||
| 102 | else { |
||
| 103 | OCA.Files.FileList.prototype.updateEmptyContent.apply(this, arguments); |
||
| 104 | } |
||
| 105 | }, |
||
| 106 | |||
| 107 | getDirectoryPermissions: function() { |
||
| 108 | return OC.PERMISSION_READ | OC.PERMISSION_DELETE; |
||
| 109 | }, |
||
| 110 | |||
| 111 | updateStorageStatistics: function() { |
||
| 112 | // no op because it doesn't have |
||
| 113 | // storage info like free space / used space |
||
| 114 | }, |
||
| 115 | |||
| 116 | reload: function() { |
||
| 117 | this.showMask(); |
||
| 118 | if (this._reloadCall) { |
||
| 119 | this._reloadCall.abort(); |
||
| 120 | } |
||
| 121 | |||
| 122 | // there is only root |
||
| 123 | this._setCurrentDir('/', false); |
||
| 124 | |||
| 125 | var promises = []; |
||
| 126 | var shares = $.ajax({ |
||
| 127 | url: OC.linkToOCS('apps/files_sharing/api/v1') + 'shares', |
||
| 128 | /* jshint camelcase: false */ |
||
| 129 | data: { |
||
| 130 | format: 'json', |
||
| 131 | shared_with_me: !!this._sharedWithUser |
||
| 132 | }, |
||
| 133 | type: 'GET', |
||
| 134 | beforeSend: function(xhr) { |
||
| 135 | xhr.setRequestHeader('OCS-APIREQUEST', 'true'); |
||
| 136 | }, |
||
| 137 | }); |
||
| 138 | promises.push(shares); |
||
| 139 | |||
| 140 | if (!!this._sharedWithUser) { |
||
| 141 | var remoteShares = $.ajax({ |
||
| 142 | url: OC.linkToOCS('apps/files_sharing/api/v1') + 'remote_shares', |
||
| 143 | /* jshint camelcase: false */ |
||
| 144 | data: { |
||
| 145 | format: 'json' |
||
| 146 | }, |
||
| 147 | type: 'GET', |
||
| 148 | beforeSend: function(xhr) { |
||
| 149 | xhr.setRequestHeader('OCS-APIREQUEST', 'true'); |
||
| 150 | }, |
||
| 151 | }); |
||
| 152 | promises.push(remoteShares); |
||
| 153 | } else { |
||
| 154 | //Push empty promise so callback gets called the same way |
||
| 155 | promises.push($.Deferred().resolve()); |
||
| 156 | } |
||
| 157 | |||
| 158 | this._reloadCall = $.when.apply($, promises); |
||
| 159 | var callBack = this.reloadCallback.bind(this); |
||
| 160 | return this._reloadCall.then(callBack, callBack); |
||
| 161 | }, |
||
| 162 | |||
| 163 | reloadCallback: function(shares, remoteShares) { |
||
| 164 | delete this._reloadCall; |
||
| 165 | this.hideMask(); |
||
| 166 | |||
| 167 | this.$el.find('#headerSharedWith').text( |
||
| 168 | t('files_sharing', this._sharedWithUser ? 'Shared by' : 'Shared with') |
||
| 169 | ); |
||
| 170 | |||
| 171 | var files = []; |
||
| 172 | |||
| 173 | if (shares[0].ocs && shares[0].ocs.data) { |
||
| 174 | files = files.concat(this._makeFilesFromShares(shares[0].ocs.data)); |
||
| 175 | } |
||
| 176 | |||
| 177 | if (remoteShares && remoteShares[0].ocs && remoteShares[0].ocs.data) { |
||
| 178 | files = files.concat(this._makeFilesFromRemoteShares(remoteShares[0].ocs.data)); |
||
| 179 | } |
||
| 180 | |||
| 181 | this.setFiles(files); |
||
| 182 | return true; |
||
| 183 | }, |
||
| 184 | |||
| 185 | _makeFilesFromRemoteShares: function(data) { |
||
| 186 | var self = this; |
||
| 187 | var files = data; |
||
| 188 | |||
| 189 | files = _.chain(files) |
||
| 190 | // convert share data to file data |
||
| 191 | .map(function(share) { |
||
| 192 | var file = { |
||
| 193 | shareOwner: share.owner + '@' + share.remote.replace(/.*?:\/\//g, ""), |
||
| 194 | name: OC.basename(share.mountpoint), |
||
| 195 | mtime: share.mtime * 1000, |
||
| 196 | mimetype: share.mimetype, |
||
| 197 | type: share.type, |
||
| 198 | id: share.file_id, |
||
|
0 ignored issues
–
show
Coding Style
introduced
by
Loading history...
|
|||
| 199 | path: OC.dirname(share.mountpoint), |
||
| 200 | permissions: share.permissions |
||
| 201 | }; |
||
| 202 | |||
| 203 | file.shares = [{ |
||
| 204 | id: share.id, |
||
| 205 | type: OC.Share.SHARE_TYPE_REMOTE |
||
| 206 | }]; |
||
| 207 | return file; |
||
| 208 | }) |
||
| 209 | .value(); |
||
| 210 | return files; |
||
| 211 | }, |
||
| 212 | |||
| 213 | /** |
||
| 214 | * Converts the OCS API share response data to a file info |
||
| 215 | * list |
||
| 216 | * @param {Array} data OCS API share array |
||
| 217 | * @return {Array.<OCA.Sharing.SharedFileInfo>} array of shared file info |
||
| 218 | */ |
||
| 219 | _makeFilesFromShares: function(data) { |
||
| 220 | /* jshint camelcase: false */ |
||
| 221 | var self = this; |
||
| 222 | var files = data; |
||
| 223 | |||
| 224 | if (this._linksOnly) { |
||
| 225 | files = _.filter(data, function(share) { |
||
| 226 | return share.share_type === OC.Share.SHARE_TYPE_LINK; |
||
| 227 | }); |
||
| 228 | } |
||
| 229 | |||
| 230 | // OCS API uses non-camelcased names |
||
| 231 | files = _.chain(files) |
||
| 232 | // convert share data to file data |
||
| 233 | .map(function(share) { |
||
| 234 | // TODO: use OC.Files.FileInfo |
||
| 235 | var file = { |
||
| 236 | id: share.file_source, |
||
| 237 | icon: OC.MimeType.getIconUrl(share.mimetype), |
||
| 238 | mimetype: share.mimetype |
||
| 239 | }; |
||
| 240 | if (share.item_type === 'folder') { |
||
| 241 | file.type = 'dir'; |
||
| 242 | file.mimetype = 'httpd/unix-directory'; |
||
| 243 | } |
||
| 244 | else { |
||
| 245 | file.type = 'file'; |
||
| 246 | } |
||
| 247 | file.share = { |
||
| 248 | id: share.id, |
||
| 249 | type: share.share_type, |
||
| 250 | target: share.share_with, |
||
| 251 | stime: share.stime * 1000, |
||
| 252 | }; |
||
| 253 | if (self._sharedWithUser) { |
||
| 254 | file.shareOwner = share.displayname_owner; |
||
| 255 | file.name = OC.basename(share.file_target); |
||
| 256 | file.path = OC.dirname(share.file_target); |
||
| 257 | file.permissions = share.permissions; |
||
| 258 | if (file.path) { |
||
| 259 | file.extraData = share.file_target; |
||
| 260 | } |
||
| 261 | } |
||
| 262 | else { |
||
| 263 | if (share.share_type !== OC.Share.SHARE_TYPE_LINK) { |
||
| 264 | file.share.targetDisplayName = share.share_with_displayname; |
||
| 265 | } |
||
| 266 | file.name = OC.basename(share.path); |
||
| 267 | file.path = OC.dirname(share.path); |
||
| 268 | file.permissions = OC.PERMISSION_ALL; |
||
| 269 | if (file.path) { |
||
| 270 | file.extraData = share.path; |
||
| 271 | } |
||
| 272 | } |
||
| 273 | return file; |
||
| 274 | }) |
||
| 275 | // Group all files and have a "shares" array with |
||
| 276 | // the share info for each file. |
||
| 277 | // |
||
| 278 | // This uses a hash memo to cumulate share information |
||
| 279 | // inside the same file object (by file id). |
||
| 280 | .reduce(function(memo, file) { |
||
| 281 | var data = memo[file.id]; |
||
| 282 | var recipient = file.share.targetDisplayName; |
||
| 283 | if (!data) { |
||
| 284 | data = memo[file.id] = file; |
||
| 285 | data.shares = [file.share]; |
||
| 286 | // using a hash to make them unique, |
||
| 287 | // this is only a list to be displayed |
||
| 288 | data.recipients = {}; |
||
| 289 | // share types |
||
| 290 | data.shareTypes = {}; |
||
| 291 | // counter is cheaper than calling _.keys().length |
||
| 292 | data.recipientsCount = 0; |
||
| 293 | data.mtime = file.share.stime; |
||
| 294 | } |
||
| 295 | else { |
||
| 296 | // always take the most recent stime |
||
| 297 | if (file.share.stime > data.mtime) { |
||
| 298 | data.mtime = file.share.stime; |
||
| 299 | } |
||
| 300 | data.shares.push(file.share); |
||
| 301 | } |
||
| 302 | |||
| 303 | if (recipient) { |
||
| 304 | // limit counterparts for output |
||
| 305 | if (data.recipientsCount < 4) { |
||
| 306 | // only store the first ones, they will be the only ones |
||
| 307 | // displayed |
||
| 308 | data.recipients[recipient] = true; |
||
| 309 | } |
||
| 310 | data.recipientsCount++; |
||
| 311 | } |
||
| 312 | |||
| 313 | data.shareTypes[file.share.type] = true; |
||
| 314 | |||
| 315 | delete file.share; |
||
| 316 | return memo; |
||
| 317 | }, {}) |
||
| 318 | // Retrieve only the values of the returned hash |
||
| 319 | .values() |
||
| 320 | // Clean up |
||
| 321 | .each(function(data) { |
||
| 322 | // convert the recipients map to a flat |
||
| 323 | // array of sorted names |
||
| 324 | data.mountType = 'shared'; |
||
| 325 | data.recipients = _.keys(data.recipients); |
||
| 326 | data.recipientsDisplayName = OCA.Sharing.Util.formatRecipients( |
||
| 327 | data.recipients, |
||
| 328 | data.recipientsCount |
||
| 329 | ); |
||
| 330 | delete data.recipientsCount; |
||
| 331 | if (self._sharedWithUser) { |
||
| 332 | // only for outgoing shres |
||
| 333 | delete data.shareTypes; |
||
| 334 | } else { |
||
| 335 | data.shareTypes = _.keys(data.shareTypes); |
||
| 336 | } |
||
| 337 | }) |
||
| 338 | // Finish the chain by getting the result |
||
| 339 | .value(); |
||
| 340 | |||
| 341 | // Sort by expected sort comparator |
||
| 342 | return files.sort(this._sortComparator); |
||
| 343 | } |
||
| 344 | }); |
||
| 345 | |||
| 346 | /** |
||
| 347 | * Share info attributes. |
||
| 348 | * |
||
| 349 | * @typedef {Object} OCA.Sharing.ShareInfo |
||
| 350 | * |
||
| 351 | * @property {int} id share ID |
||
| 352 | * @property {int} type share type |
||
| 353 | * @property {String} target share target, either user name or group name |
||
| 354 | * @property {int} stime share timestamp in milliseconds |
||
| 355 | * @property {String} [targetDisplayName] display name of the recipient |
||
| 356 | * (only when shared with others) |
||
| 357 | * |
||
| 358 | */ |
||
| 359 | |||
| 360 | /** |
||
| 361 | * Shared file info attributes. |
||
| 362 | * |
||
| 363 | * @typedef {OCA.Files.FileInfo} OCA.Sharing.SharedFileInfo |
||
| 364 | * |
||
| 365 | * @property {Array.<OCA.Sharing.ShareInfo>} shares array of shares for |
||
| 366 | * this file |
||
| 367 | * @property {int} mtime most recent share time (if multiple shares) |
||
| 368 | * @property {String} shareOwner name of the share owner |
||
| 369 | * @property {Array.<String>} recipients name of the first 4 recipients |
||
| 370 | * (this is mostly for display purposes) |
||
| 371 | * @property {String} recipientsDisplayName display name |
||
| 372 | */ |
||
| 373 | |||
| 374 | OCA.Sharing.FileList = FileList; |
||
| 375 | })(); |
||
| 376 |