1/* 2 * Copyright (c) 2015 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 11(function() { 12 if(!OC.Share) { 13 OC.Share = {}; 14 OC.Share.Types = {}; 15 } 16 17 /** 18 * @typedef {object} OC.Share.Types.LinkShareInfo 19 * @property {bool} isLinkShare 20 * @property {string} token 21 * @property {string|null} password 22 * @property {string} link 23 * @property {number} permissions 24 * @property {Date} expiration 25 * @property {number} stime share time 26 */ 27 28 /** 29 * @typedef {object} OC.Share.Types.Reshare 30 * @property {string} uid_owner 31 * @property {number} share_type 32 * @property {string} share_with 33 * @property {string} displayname_owner 34 * @property {number} permissions 35 * @property {OC.Share.Types.ShareAttribute[]} attributes 36 */ 37 38 /** 39 * @typedef {object} OC.Share.Types.ShareInfo 40 * @property {number} share_type 41 * @property {number} permissions 42 * @property {OC.Share.Types.ShareAttribute[]} attributes 43 * @property {number} file_source optional 44 * @property {number} item_source 45 * @property {string} token 46 * @property {string} share_with 47 * @property {string} share_with_displayname 48 * @property {string} mail_send 49 * @property {Date} expiration optional? 50 * @property {number} stime optional? 51 * @property {string} uid_owner 52 */ 53 54 /** 55 * @typedef {object} OC.Share.Types.ShareItemInfo 56 * @property {OC.Share.Types.Reshare} reshare 57 * @property {OC.Share.Types.ShareInfo[]} shares 58 * @property {OC.Share.Types.LinkShareInfo|undefined} linkShare 59 */ 60 61 /** 62 * @typedef {object} OC.Share.Types.ShareAttribute 63 * @property {string} key 64 * @property {string} scope 65 * @property {bool} enabled 66 */ 67 68 /** 69 * ShareAttributesApi v1 registered attributes 70 * @typedef {object} OC.Share.Types.RegisteredShareAttribute 71 * @property {string} key 72 * @property {bool} default 73 * @property {string} scope 74 * @property {string} label 75 * @property {string} description 76 * @property {number[]} shareType 77 * @property {number[]} incompatiblePermissions 78 * @property {number[]} requiredPermissions 79 * @property {OC.Share.Types.ShareAttribute[]} incompatibleAttributes 80 * @deprecated shareAttributesApi v2 requires apps to extend ShareItemModel with attributes 81 */ 82 83 /** 84 * These properties are sometimes returned by the server as strings instead 85 * of integers, so we need to convert them accordingly... 86 */ 87 var SHARE_RESPONSE_INT_PROPS = [ 88 'id', 'file_parent', 'mail_send', 'file_source', 'item_source', 'permissions', 89 'storage', 'share_type', 'parent', 'stime' 90 ]; 91 92 /** 93 * Properties which are json and need to be parsed 94 */ 95 var SHARE_RESPONSE_JSON_PROPS = [ 96 'attributes' 97 ]; 98 99 /** 100 * @class OCA.Share.ShareItemModel 101 * @classdesc 102 * 103 * Represents the GUI of the share dialogue 104 * 105 * // FIXME: use OC Share API once #17143 is done 106 * 107 * // TODO: this really should be a collection of share item models instead, 108 * where the link share is one of them 109 */ 110 var ShareItemModel = OC.Backbone.Model.extend({ 111 /** 112 * @type share id of the link share, if applicable 113 */ 114 _linkShareId: null, 115 116 _linkSharesCollection: null, 117 118 /** 119 * @type {OC.Share.Types.RegisteredShareAttribute[]} registered available share permissions for this file/folder share 120 * @deprecated shareAttributesApi v2 requires apps to extend ShareItemModel with attributes 121 */ 122 _registeredAttributes: null, 123 124 initialize: function(attributes, options) { 125 if(!_.isUndefined(options.configModel)) { 126 this.configModel = options.configModel; 127 } 128 if(!_.isUndefined(options.fileInfoModel)) { 129 /** @type {OC.Files.FileInfo} **/ 130 this.fileInfoModel = options.fileInfoModel; 131 } 132 133 this._linkSharesCollection = new OC.Share.SharesCollection(); 134 this._registeredAttributes = []; 135 136 _.bindAll(this, 'addShare'); 137 OC.Plugins.attach('OC.Share.ShareItemModel', this); 138 }, 139 140 defaults: { 141 permissions: 0, 142 linkShare: {} 143 }, 144 145 /** 146 * Returns the collection of link shares 147 * 148 * @return {OC.Share.SharesCollection} shares collection 149 */ 150 getLinkSharesCollection: function() { 151 return this._linkSharesCollection; 152 }, 153 154 /**
155 * Returns the file info for the file/folder on which this share information relate to 156 * 157 * @return {OC.Files.FileInfoModel} file info model 158 */ 159 getFileInfo: function() { 160 return this.fileInfoModel; 161 }, 162 163 addShare: function(properties, options) { 164 var shareType = properties.shareType; 165 options = options || {}; 166 properties = _.extend({}, properties); 167 168 // Set default expiration date 169 if ( 170 this.configModel.isDefaultExpireDateUserEnabled() && ( 171 shareType === OC.Share.SHARE_TYPE_USER || 172 shareType === OC.Share.SHARE_TYPE_GUEST) 173 ) { 174 properties.expireDate = this.configModel.getDefaultExpireDateUser('YYYY-MM-DD') 175 } 176 177 else if (this.configModel.isDefaultExpireDateGroupEnabled() && shareType === OC.Share.SHARE_TYPE_GROUP) { 178 properties.expireDate = this.configModel.getDefaultExpireDateGroup('YYYY-MM-DD') 179 } else if (this.configModel.isDefaultExpireDateRemoteEnabled() && shareType === OC.Share.SHARE_TYPE_REMOTE) { 180 properties.expireDate = this.configModel.getDefaultExpireDateRemote('YYYY-MM-DD') 181 } 182 183 // Get default permissions 184 var permissions = properties.permissions || OC.PERMISSION_ALL; 185 properties.permissions = permissions & this.getDefaultPermissions(); 186 187 // Extend attributes for new share 188 // note: required only for compatibility with attributes v1 189 if (this._registeredAttributes.length) { 190 properties.attributes = this._handleAddShareAttributes(properties, options); 191 } 192 193 if (_.isUndefined(properties.path)) { 194 properties.path = this.fileInfoModel.getFullPath(); 195 } 196 197 var self = this; 198 return $.ajax({ 199 type: 'POST', 200 url: this._getUrl('shares'), 201 data: JSON.stringify(properties), 202 dataType: 'json', 203 contentType: "application/json" 204 }).done(function() { 205 self.fetch().done(function() { 206 if (_.isFunction(options.success)) { 207 options.success(self); 208 } 209 }); 210 }).fail(function(xhr) { 211 var msg = t('core', 'Error'); 212 var result = xhr.responseJSON; 213 if (result && result.ocs && result.ocs.meta) { 214 msg = result.ocs.meta.message; 215 } 216 217 if (_.isFunction(options.error)) { 218 options.error(self, msg); 219 } else { 220 OC.dialogs.alert(msg, t('core', 'Error while sharing')); 221 } 222 }); 223 }, 224 225 updateShare: function(shareId, properties, options) { 226 var self = this; 227 228 // Extend attributes for update share if provided for update. 229 // note: required only for compatibility with attributes v1 230 if (this._registeredAttributes.length) { 231 properties.attributes = this._handleUpdateShareAttributes(shareId, properties, options); 232 } 233 234 return $.ajax({ 235 type: 'PUT', 236 url: this._getUrl('shares/' + encodeURIComponent(shareId)), 237 data: JSON.stringify(properties), 238 dataType: 'json', 239 contentType: "application/json" 240 }).done(function() { 241 self.fetch({ 242 success: function() { 243 if (_.isFunction(options.success)) { 244 options.success(self); 245 } 246 } 247 }); 248 }).fail(function(xhr) { 249 var msg = t('core', 'Error'); 250 var result = xhr.responseJSON; 251 if (result.ocs && result.ocs.meta) { 252 msg = result.ocs.meta.message; 253 } 254 255 if (_.isFunction(options.error)) { 256 options.error(self, msg); 257 } else { 258 OC.dialogs.alert(msg, t('core', 'Error while sharing')); 259 } 260 }); 261 }, 262 263 /** 264 * Deletes the share with the given id 265 * 266 * @param {int} shareId share id 267 * @return {jQuery} 268 */ 269 removeShare: function(shareId, options) { 270 var self = this; 271 options = options || {}; 272 return $.ajax({ 273 type: 'DELETE', 274 url: this._getUrl('shares/' + encodeURIComponent(shareId)), 275 }).done(function() { 276 self.fetch({ 277 success: function() { 278 if (_.isFunction(options.success)) { 279 options.success(self); 280 } 281 } 282 }); 283 }).fail(function(xhr) { 284 var msg = t('core', 'Error'); 285 var result = xhr.responseJSON; 286 if (result.ocs && result.ocs.meta) { 287 msg = result.ocs.meta.message; 288 } 289 290 if (_.isFunction(options.error)) { 291 options.error(self, msg); 292 } else { 293 OC.dialogs.alert(msg, t('core', 'Error removing share')); 294 } 295 }); 296 }, 297 298 /** 299 * Default permissions are Edit (CRUD) and Share 300 * Check if these permissions are possible 301 * 302 * @returns {number} 303 */ 304 getDefaultPermissions: function() { 305 var defaultPermissions = OC.getCapabilities()['files_sharing']['default_permissions'] || OC.PERMISSION_ALL; 306 var possiblePermissions = OC.PERMISSION_READ; 307 if (this.updatePermissionPossible()) { 308 possiblePermissions = possiblePermissions | OC.PERMISSION_UPDATE; 309 } 310 if (this.createPermissionPossible()) { 311 possiblePermissions = possiblePermissions | OC.PERMISSION_CREATE; 312 } 313 if (this.deletePermissionPossible()) { 314 possiblePermissions = possiblePermissions | OC.PERMISSION_DELET
314E; 315 } 316 if (this.configModel.get('isResharingAllowed') && (this.sharePermissionPossible())) { 317 possiblePermissions = possiblePermissions | OC.PERMISSION_SHARE; 318 } 319 320 return defaultPermissions & possiblePermissions; 321 }, 322 323 /** 324 * @returns {boolean} 325 */ 326 isFolder: function() { 327 return this.get('itemType') === 'folder'; 328 }, 329 330 /** 331 * @returns {boolean} 332 */ 333 isFile: function() { 334 return this.get('itemType') === 'file'; 335 }, 336 337 /** 338 * whether this item has reshare information 339 * @returns {boolean} 340 */ 341 hasReshare: function() { 342 var reshare = this.get('reshare'); 343 return _.isObject(reshare) && !_.isUndefined(reshare.uid_owner); 344 }, 345 346 /** 347 * whether this item has user share information 348 * @returns {boolean} 349 */ 350 hasUserShares: function() { 351 return this.getSharesWithCurrentItem().length > 0; 352 }, 353 354 /** 355 * Returns whether this item has a link share 356 * 357 * @return {bool} true if a link share exists, false otherwise 358 */ 359 hasLinkShare: function() { 360 return this._linkSharesCollection.length > 0; 361 }, 362 363 /** 364 * @returns {string} 365 */ 366 getReshareOwner: function() { 367 return this.get('reshare').uid_owner; 368 }, 369 370 /** 371 * @returns {string} 372 */ 373 getReshareOwnerDisplayname: function() { 374 return this.get('reshare').displayname_owner; 375 }, 376 377 /** 378 * @returns {string} 379 */ 380 getReshareWith: function() { 381 return this.get('reshare').share_with; 382 }, 383 384 /** 385 * @returns {string} 386 */ 387 getReshareWithDisplayName: function() { 388 var reshare = this.get('reshare'); 389 return reshare.share_with_displayname || reshare.share_with; 390 }, 391 392 /** 393 * @returns {number} 394 */ 395 getReshareType: function() { 396 return this.get('reshare').share_type; 397 }, 398 399 /** 400 * @returns {OC.Share.Types.ShareAttribute[]} 401 */ 402 getReshareAttributes: function() { 403 return this.get('reshare').attributes; 404 }, 405 406 /** 407 * Returns all share entries that only apply to the current item 408 * (file/folder) 409 * 410 * @return {Array.<OC.Share.Types.ShareInfo>} 411 */ 412 getSharesWithCurrentItem: function() { 413 var shares = this.get('shares') || []; 414 var fileId = this.fileInfoModel.get('id'); 415 return _.filter(shares, function(share) { 416 return share.item_source === fileId; 417 }); 418 }, 419 420 /** 421 * Returns share entry by share id. Share will only be returned that only apply to the current item 422 * (file/folder) 423 * 424 * @param shareId 425 * @return {OC.Share.Types.ShareInfo} 426 */ 427 getShareById: function(shareId) { 428 var shares = this.get('shares') || []; 429 for(var shareIndex = 0; shareIndex < shares.length; shareIndex++) { 430 if (shares[shareIndex].id === shareId) { 431 return shares[shareIndex]; 432 } 433 } 434 435 throw t('core', 'Unknown Share'); 436 }, 437 438 /** 439 * @param shareIndex 440 * @returns {string} 441 */ 442 getShareWith: function(shareIndex) { 443 /** @type OC.Share.Types.ShareInfo **/ 444 var share = this.get('shares')[shareIndex]; 445 if(!_.isObject(share)) { 446 throw "Unknown Share"; 447 } 448 return share.share_with; 449 }, 450 451 /** 452 * @param shareIndex 453 * @returns {string} 454 */ 455 getShareWithDisplayName: function(shareIndex) { 456 /** @type OC.Share.Types.ShareInfo **/ 457 var share = this.get('shares')[shareIndex]; 458 if(!_.isObject(share)) { 459 throw "Unknown Share"; 460 } 461 return share.share_with_displayname; 462 }, 463 464 /** 465 * @param shareIndex 466 * @returns {string} 467 */ 468 getShareWithAdditionalInfo: function(shareIndex) { 469 /** @type OC.Share.Types.ShareInfo **/ 470 var share = this.get('shares')[shareIndex]; 471 if(!_.isObject(share)) { 472 throw "Unknown Share"; 473 } 474 return share.share_with_additional_info; 475 }, 476 477 getShareType: function(shareIndex) { 478 /** @type OC.Share.Types.ShareInfo **/ 479 var share = this.get('shares')[shareIndex]; 480 if(!_.isObject(share)) { 481 throw "Unknown Share"; 482 } 483 return share.share_type; 484 }, 485 486 getExpirationDate: function(shareIndex) { 487 /** @type OC.Share.Types.ShareInfo **/ 488 var share = this.get('shares')[shareIndex]; 489 if(!_.isObject(share)) { 490 throw "Unknown Share"; 491 } 492 return (share.expiration !== null) ? moment(share.expiration).format('DD-MM-YYYY') : null; 493 }, 494 495 /** 496 * whether permission is in permission bitmap 497 * 498 * @param {number} permissions 499 * @param {number} permission 500 * @returns {boolean} 501 * @private 502 */ 503 _hasPermission: function(permissions, permission) { 504 return (permissions & permission) === permission; 505 }, 506 507 /** 508 * whether a share from shares has the requested permission 509 * 510 * @param {number} shareIndex 511 * @param {number} permission 512 * @returns {boolean} 513 * @private 514 */ 515 _shareHasPermission: function(shareIndex, permission) { 516 /** @type OC.Share.Types.ShareInfo **/ 517 var share = this.get('shares')[shareIndex]; 518 if(!_.isObject(share)) { 519 throw "Unknown Share"; 520 } 521 return this._hasPermission(share.permissions, permission); 522 }, 523 524 notificationMailWasSent: function(shareIndex) { 525 /** @type OC.Share.Types.ShareInfo **/ 526 var share = this.get('shares')[shareIndex]; 527 if(!_.isObject(share)) { 528 throw "Unknown Share"; 529 } 530 return share.mail_send === 1; 531 }, 532 533 /** 534 * Sends an email notification for the given share 535 * 536 * @param {int} shareType share type 537 * @param {string} shareWith recipient 538 * @param {bool} state whether to set the notification flag or remove it 539 */ 540 sendNotificationForShare: function(shareType, shareWith, state) { 541 var itemType = this.get('itemType'); 542 var itemSource = this.get('itemSource'); 543 var baseUrl = OC.linkToOCS('/apps/files_sharing/api/v1/notification/', 2); 544 var action = state ? 'send' : 'marksent'; 545 546 return $.post( 547 baseUrl + action, 548 { 549 format: 'json', 550 recipient: shareWith, 551 shareType: shareType, 552 itemSource: itemSource, 553 itemType: itemType 554 } 555 ); 556 }, 557 558 /** 559 * @returns {boolean} 560 */ 561 sharePermissionPossible: function() { 562 return (this.get('permissions') & OC.PERMISSION_SHARE) === OC.PERMISSION_SHARE; 563 }, 564 565 /** 566 * @param {number} shareIndex 567 * @returns {boolean} 568 */ 569 hasSharePermission: function(shareIndex) { 570 return this._shareHasPermission(shareIndex, OC.PERMISSION_SHARE); 571 }, 572 573 /** 574 * @returns {boolean} 575 */ 576 createPermissionPossible: function() { 577 return (this.get('permissions') & OC.PERMISSION_CREATE) === OC.PERMISSION_CREATE; 578 }, 579 580 /** 581 * @param {number} shareIndex 582 * @returns {boolean} 583 */ 584 hasCreatePermission: function(shareIndex) { 585 return this._shareHasPermission(shareIndex, OC.PERMISSION_CREATE); 586 }, 587 588 /** 589 * @returns {boolean} 590 */ 591 updatePermissionPossible: function() { 592 return (this.get('permissions') & OC.PERMISSION_UPDATE) === OC.PERMISSION_UPDATE; 593 }, 594 595 /** 596 * @param {number} shareIndex 597 * @returns {boolean} 598 */ 599 hasUpdatePermission: function(shareIndex) { 600 return this._shareHasPermission(shareIndex, OC.PERMISSION_UPDATE); 601 }, 602 603 /** 604 * @returns {boolean} 605 */ 606 deletePermissionPossible: function() {
607 return (this.get('permissions') & OC.PERMISSION_DELETE) === OC.PERMISSION_DELETE; 608 }, 609 610 /** 611 * @param {number} shareIndex 612 * @returns {boolean} 613 */ 614 hasDeletePermission: function(shareIndex) { 615 return this._shareHasPermission(shareIndex, OC.PERMISSION_DELETE); 616 }, 617 618 /** 619 * @returns {boolean} 620 */ 621 editPermissionPossible: function() { 622 return this.createPermissionPossible() 623 || this.updatePermissionPossible() 624 || this.deletePermissionPossible(); 625 }, 626 627 /** 628 * @returns {boolean} 629 */ 630 hasEditPermission: function(shareIndex) { 631 return this.hasCreatePermission(shareIndex) 632 || this.hasUpdatePermission(shareIndex) 633 || this.hasDeletePermission(shareIndex); 634 }, 635 636 _getUrl: function(base, params) { 637 params = _.extend({format: 'json'}, params || {}); 638 return OC.linkToOCS('apps/files_sharing/api/v1', 2) + base + '?' + OC.buildQueryString(params); 639 }, 640 641 _fetchShares: function() { 642 var path = this.fileInfoModel.getFullPath(); 643 return $.ajax({ 644 type: 'GET', 645 url: this._getUrl('shares', {path: path, reshares: true}) 646 }); 647 }, 648 649 _fetchReshare: function() { 650 // only fetch original share once 651 if (!this._reshareFetched) { 652 var path = this.fileInfoModel.getFullPath(); 653 this._reshareFetched = true; 654 return $.ajax({ 655 type: 'GET', 656 url: this._getUrl('shares', {path: path, shared_with_me: true}) 657 }); 658 } else { 659 return $.Deferred().resolve([{ 660 ocs: { 661 data: [this.get('reshare')] 662 } 663 }]); 664 } 665 }, 666 667 /** 668 * Group reshares into a single super share element. 669 * Does this by finding the most precise share and 670 * combines the permissions to be the most permissive. 671 * 672 * @param {Array} reshares 673 * @return {Object} reshare 674 */ 675 _groupReshares: function(reshares) { 676 if (!reshares || !reshares.length) { 677 return false; 678 } 679 680 var superShare = reshares.shift(); 681 var combinedPermissions = superShare.permissions; 682 _.each(reshares, function(reshare) { 683 // use share have higher priority than group share 684 if (reshare.share_type === OC.Share.SHARE_TYPE_USER && superShare.share_type === OC.Share.SHARE_TYPE_GROUP) { 685 superShare = reshare; 686 } 687 combinedPermissions |= reshare.permissions; 688 }); 689 690 superShare.permissions = combinedPermissions; 691 return superShare; 692 }, 693 694 fetch: function(options) { 695 var model = this; 696 697 this.trigger('request', this); 698 699 var deferred = $.when( 700 this._fetchShares(), 701 this._fetchReshare() 702 ); 703 704 deferred.done(function(data1, data2) { 705 model.trigger('sync', 'GET', this); 706 var sharesMap = {}; 707 _.each(data1[0].ocs.data, function(shareItem) { 708 sharesMap[shareItem.id] = shareItem; 709 }); 710 711 var reshare = false; 712 if (data2[0].ocs.data.length) { 713 reshare = model._groupReshares(data2[0].ocs.data); 714 } 715 716 // update model properties, 717 // this should cause rerendering of the object 718 model.set(model.parse({ 719 shares: sharesMap, 720 reshare: reshare 721 }), { 722 // do not allow silent, as apps might rely on reacting to 723 // changes to the model 724 silent: false, 725 }); 726 }); 727 728 return deferred; 729 }, 730 731 /** 732 * Updates OC.Share.itemShares and OC.Share.statuses. 733 * 734 * This is required in case the user navigates away and comes back, 735 * the share statuses from the old arrays are still used to fill in the icons 736 * in the file list. 737 */ 738 _legacyFillCurrentShares: function(shares) { 739 var fileId = this.fileInfoModel.get('id'); 740 if (!shares || !shares.length) { 741 delete OC.Share.statuses[fileId]; 742 OC.Share.currentShares = {}; 743 OC.Share.itemShares = []; 744 return; 745 } 746 747 var currentShareStatus = OC.Share.statuses[fileId]; 748 if (!currentShareStatus) { 749 currentShareStatus = {link: false}; 750 OC.Share.statuses[fileId] = currentShareStatus; 751 } 752 currentShareStatus.link = false; 753 754 OC.Share.currentShares = {}; 755 OC.Share.itemShares = []; 756 _.each(shares, 757 /** 758 * @param {OC.Share.Types.ShareInfo} share 759 */ 760 function(share) { 761 if (share.share_type === OC.Share.SHARE_TYPE_LINK) {
762 OC.Share.itemShares[share.share_type] = true; 763 currentShareStatus.link = true; 764 } else { 765 if (!OC.Share.itemShares[share.share_type]) { 766 OC.Share.itemShares[share.share_type] = []; 767 } 768 OC.Share.itemShares[share.share_type].push(share.share_with); 769 } 770 } 771 ); 772 }, 773 774 parse: function(data) { 775 if(data === false) { 776 console.warn('no data was returned'); 777 this.trigger('fetchError'); 778 return {}; 779 } 780 781 // check if this file has received shares to current user (direct incoming shared_with_me shares) 782 var permissions = this.get('possiblePermissions'); 783 if(!_.isUndefined(data.reshare) && !_.isUndefined(data.reshare.permissions) && data.reshare.uid_owner !== OC.currentUser) { 784 if (this.fileInfoModel.get('mountType') === 'shared' && this.fileInfoModel.isDirectory()) { 785 // if this file is already contained in a share (subfolder of shared folder) combine incoming shares permissions 786 // with parent folder share permissions. This handles a case of resharing a file/folder reshared from same 787 // share mountpoint multiple times in subfolders 788 permissions = permissions & (this.fileInfoModel.get('sharePermissions') | data.reshare.permissions); 789 } else { 790 permissions = permissions & data.reshare.permissions; 791 } 792 } 793 794 /** @type {OC.Share.Types.ShareInfo[]} **/ 795 var shares = _.map(data.shares, this._parseShare); 796 797 /** @type {OC.Share.Types.Reshare} **/ 798 var reshare = this._parseShare(data.reshare); 799 800 this._legacyFillCurrentShares(shares); 801 802 var linkShares = []; 803 // filter out the share by link 804 shares = _.reject(shares, 805 /** 806 * @param {OC.Share.Types.ShareInfo} share 807 */ 808 function(share) { 809 var isShareLink = 810 share.share_type === OC.Share.SHARE_TYPE_LINK 811 && ( share.file_source === this.get('itemSource') 812 || share.item_source === this.get('itemSource')); 813 814 if (isShareLink) { 815 linkShares.push(share); 816 return share; 817 } 818 }, 819 this 820 ); 821 822 // populate link shares collection with found link shares 823 this._linkSharesCollection.set(linkShares, {parse: true}); 824 825 // use the old crappy way for other shares for now 826 return { 827 reshare: reshare, 828 shares: shares, 829 permissions: permissions 830 }; 831 }, 832 833 /** 834 * Parse fields of the response 835 * 836 * @param {OC.Share.Types.ShareInfo} share 837 * @returns {OC.Share.Types.ShareInfo} share 838 */ 839 _parseShare: function(share) { 840 // properly parse some values because sometimes the server 841 // returns integers as string... 842 var i; 843 for (i = 0; i < SHARE_RESPONSE_INT_PROPS.length; i++) { 844 var propInt = SHARE_RESPONSE_INT_PROPS[i]; 845 if (!_.isUndefined(share[propInt])) { 846 share[propInt] = parseInt(share[propInt], 10); 847 } 848 } 849 for (i = 0; i < SHARE_RESPONSE_JSON_PROPS.length; i++) { 850 // Parse JSON if not yet parsed 851 var propJson = SHARE_RESPONSE_JSON_PROPS[i]; 852 if (!_.isUndefined(share[propJson]) && !_.isObject(share[propJson])) { 853 share[propJson] = JSON.parse(share[propJson]); 854 } 855 } 856 return share; 857 }, 858 859 /** 860 * Parses a string to an valid integer (unix timestamp) 861 * @param time 862 * @returns {*} 863 * @internal Only used to work around a bug in the backend 864 */ 865 _parseTime: function(time) { 866 if (_.isString(time)) { 867 // skip empty strings and hex values 868 if (time === '' || (time.length > 1 && time[0] === '0' && time[1] === 'x')) { 869 return null; 870 } 871 time = parseInt(time, 10); 872 if(isNaN(time)) { 873 time = null; 874 } 875 } 876 return time; 877 }, 878 879 /** 880 * Returns a list of share types from the existing shares. 881 * 882 * @return {Array.<int>} array of share types 883 */ 884 getShareTypes: function() { 885 var result; 886 result = _.pluck(this.getSharesWithCurrentItem(), 'share_type'); 887 if (this.hasLinkShare()) { 888 result.push(OC.Share.SHARE_TYPE_LINK); 889 } 890 return _.uniq(result); 891 }, 892 893 /** 894 * Returns share attributes for given share index 895 * 896 * @param shareIndex 897 * @returns OC.Share.Types.ShareAttribute[] 898 * @deprecated ShareAttributesAPI v1 registration will be deprecated. ShareAttributesAPI v2 requires apps to extend this class 899 */ 900 getShareAttributes: function(shareIndex) { 901 /** @type OC.Share.Types.ShareInfo **/ 902 var share = this.get('shares')[shareIndex]; 903 if(!_.isObject(share)) { 904 throw "Unknown Share"; 905 } 906 907 if (_.isNull(share.attributes) || _.isUndefined(share.attributes)) { 908 return []; 909 } 910 return share.attributes; 911 }, 912 913 /** 914 * @param {array} properties 915 * @param {array} options 916 * @returns {OC.Share.Types.ShareAttribute[]} 917 * @deprecated ShareAttributesAPI v1 will be deprecated. ShareAttributesAPI v2 requires apps to overwrite addShare 918 * @private 919 */ 920 _handleAddShareAttributes: function(properties, options) { 921 var shareAttributesV1 = []; 922 var filteredRegisteredAttributes = this._filterRegisteredAttributes(properties.permissions); 923 _.map(filteredRegisteredAttributes, function (filteredRegisteredAttribute) { 924 var isCompatible = true; 925 // Check if this attribute can be added due to its incompatible attributes 926 _.map(filteredRegisteredAttribute.incompatibleAttributes, function (incompatibleAttribute) { 927 _.map(filteredRegisteredAttributes, function (checkAttr) { 928 if (incompatibleAttribute.scope === checkAttr.scope && 929 incompatibleAttribute.key === checkAttr.key && 930 incompatibleAttribute.enabled === checkAttr.default) { 931 isCompatible = false;
932 } 933 }); 934 }); 935 936 if (isCompatible) { 937 shareAttributesV1.push({ 938 scope: filteredRegisteredAttribute.scope, 939 key: filteredRegisteredAttribute.key, 940 enabled: filteredRegisteredAttribute.default 941 }); 942 } 943 }); 944 var shareAttributes = shareAttributesV1; 945 946 /** 947 * ShareAttributesAPI v2 948 */ 949 var shareAttributesV2 = properties.attributes || {}; 950 for(var i in shareAttributesV2) { 951 var attr = shareAttributesV2[i]; 952 shareAttributes = this._updateShareAttribute(shareAttributes, attr); 953 } 954 955 return shareAttributes; 956 }, 957 958 /** 959 * @param {number} shareId 960 * @param {array} properties 961 * @param {array} options 962 * @returns {OC.Share.Types.ShareAttribute[]} 963 * @deprecated ShareAttributesAPI v1 will be deprecated. ShareAttributesAPI v2 requires apps to overwrite updateShare 964 * @private 965 */ 966 _handleUpdateShareAttributes: function(shareId, properties, options) { 967 var shareAttributesV1 = []; 968 var filteredAttributes = []; 969 var filteredRegisteredAttributes = this._filterRegisteredAttributes(properties.permissions); 970 _.map(filteredRegisteredAttributes, function(filteredRegisteredAttribute) { 971 // Check if this allowed registered attribute 972 // is on the list of currently set properties, 973 // and set this attribute 974 var found = false; 975 _.map(properties.attributes, function(currentAttribute) { 976 if (currentAttribute.key === filteredRegisteredAttribute.key 977 && currentAttribute.scope === filteredRegisteredAttribute.scope) { 978 found = true; 979 filteredAttributes.push({ 980 scope : filteredRegisteredAttribute.scope, 981 key: filteredRegisteredAttribute.key, 982 incompatibleAttributes: filteredRegisteredAttribute.incompatibleAttributes, 983 enabled: currentAttribute.enabled 984 }); 985 } 986 }); 987 988 // Make sure allowed registered attributes which are 989 // not yet set are added. These can become available 990 // e.g. because of share permission/attr change 991 // and need to be initialized with their default value 992 if (!found) { 993 filteredAttributes.push({ 994 scope : filteredRegisteredAttribute.scope, 995 key: filteredRegisteredAttribute.key, 996 incompatibleAttributes: filteredRegisteredAttribute.incompatibleAttributes, 997 enabled: filteredRegisteredAttribute.default 998 }); 999 } 1000 }); 1001 1002 _.map(filteredAttributes, function(filteredAttribute) { 1003 // Filter attributes by their incompatible attributes 1004 var isCompatible = true; 1005 // Check if this attribute can be added due to its incompatible attributes 1006 _.map(filteredAttribute.incompatibleAttributes, function(incompatibleAttribute) { 1007 _.map(filteredAttributes, function(checkAttr) { 1008 if (incompatibleAttribute.scope === checkAttr.scope && 1009 incompatibleAttribute.key === checkAttr.key && 1010 incompatibleAttribute.enabled === checkAttr.enabled) { 1011 isCompatible = false; 1012 } 1013 }); 1014 }); 1015 1016 if (isCompatible) { 1017 shareAttributesV1.push({ 1018 scope : filteredAttribute.scope, 1019 key: filteredAttribute.key, 1020 enabled: filteredAttribute.enabled 1021 }); 1022 } 1023 }); 1024 1025 var shareAttributes = shareAttributesV1; 1026 1027 /** 1028 * ShareAttributesAPI v2 1029 */ 1030 var shareAttributesV2 = properties.attributes || {}; 1031 for(var i in shareAttributesV2) { 1032 var attr = shareAttributesV2[i]; 1033 if (!this.getRegisteredShareAttribute(attr.scope , attr.key)) { 1034 // modify only ShareAttributesAPI v2 1035 shareAttributes = this._updateShareAttribute(shareAttributes, attr); 1036 } 1037 } 1038 1039 return shareAttributes; 1040 }, 1041 1042 _updateShareAttribute: function(shareAttributes, attr) { 1043 var exists = false; 1044 for(var i in shareAttributes) { 1045 if (shareAttributes[i].scope === attr.scope 1046 && shareAttributes[i].key === attr.key) { 1047 shareAttributes[i] = attr; 1048 exists = true; 1049 } 1050 } 1051 if (!exists) { 1052 shareAttributes.push(attr); 1053 } 1054 1055 return shareAttributes; 1056 }, 1057 1058 /** 1059 * Filter registered attributes by current share permissions 1060 * 1061 * @param {number} permissions 1062 * @returns {OC.Share.Types.RegisteredShareAttribute[]} 1063 * @deprecated ShareAttributesAPI v1 registration will be deprecated. ShareAttributesAPI v2 requires apps to extend this class 1064 * @private 1065 */ 1066 _filterRegisteredAttributes: function(permissions) { 1067 var filteredByPermissions = []; 1068 for(var i in this._registeredAttributes) { 1069 var compatible = true; 1070 var attr = this._registeredAttributes[i]; 1071 for(var ii in attr.incompatiblePermissions) { 1072 if (this._hasPermission(permissions, attr.incompatiblePermissions[ii])) { 1073 compatible = false;
1074 } 1075 } 1076 for(var ii in attr.requiredPermissions) { 1077 if (!this._hasPermission(permissions, attr.requiredPermissions[ii])) { 1078 compatible = false; 1079 } 1080 } 1081 1082 if (compatible) { 1083 filteredByPermissions.push(attr); 1084 } 1085 } 1086 1087 return filteredByPermissions; 1088 }, 1089 1090 /** 1091 * Returns share attribute label for given attribute scope and name. If 1092 * attribute does not exist, null is returned. 1093 * 1094 * @param scope 1095 * @param key 1096 * @returns {OC.Share.Types.RegisteredShareAttribute} 1097 * @deprecated ShareAttributesAPI v1 registration will be deprecated. ShareAttributesAPI v2 requires apps to extend this class 1098 */ 1099 getRegisteredShareAttribute: function(scope, key) { 1100 for(var i in this._registeredAttributes) { 1101 if (this._registeredAttributes[i].scope === scope 1102 && this._registeredAttributes[i].key === key) { 1103 return this._registeredAttributes[i]; 1104 } 1105 } 1106 return null; 1107 }, 1108 1109 /** 1110 * Apps can register default share attributes. The applications 1111 * registering share attributes are required to follow the rules: 1112 * attribute enabled -> functionality is added (e.g. can download) 1113 * attribute disabled -> functionality is restricted 1114 * incompatible attribute -> functionality is ignored 1115 * 1116 * @param {OC.Share.Types.RegisteredShareAttribute} $shareAttribute 1117 * @deprecated ShareAttributesAPI v1 registration will be deprecated. ShareAttributesAPI v2 requires apps to extend this class 1118 */ 1119 registerShareAttribute: function($shareAttribute) { 1120 // Add extra permission or update if already existing 1121 var exists = false; 1122 for(var i in this._registeredAttributes) { 1123 if (this._registeredAttributes[i].scope === $shareAttribute.scope 1124 && this._registeredAttributes[i].key === $shareAttribute.key) { 1125 this._registeredAttributes[i] = $shareAttribute; 1126 exists = true; 1127 } 1128 } 1129 if (!exists) { 1130 this._registeredAttributes.push($shareAttribute); 1131 } 1132 1133 } 1134 1135 }); 1136 1137 OC.Share.ShareItemModel = ShareItemModel; 1138})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.