shareitemmodel.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948
  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. (function() {
  11. if(!OC.Share) {
  12. OC.Share = {};
  13. OC.Share.Types = {};
  14. }
  15. /**
  16. * @typedef {object} OC.Share.Types.LinkShareInfo
  17. * @property {string} token
  18. * @property {bool} hideDownload
  19. * @property {string|null} password
  20. * @property {bool} sendPasswordByTalk
  21. * @property {number} permissions
  22. * @property {Date} expiration
  23. * @property {number} stime share time
  24. */
  25. /**
  26. * @typedef {object} OC.Share.Types.Reshare
  27. * @property {string} uid_owner
  28. * @property {number} share_type
  29. * @property {string} share_with
  30. * @property {string} displayname_owner
  31. * @property {number} permissions
  32. */
  33. /**
  34. * @typedef {object} OC.Share.Types.ShareInfo
  35. * @property {number} share_type
  36. * @property {number} permissions
  37. * @property {number} file_source optional
  38. * @property {number} item_source
  39. * @property {string} token
  40. * @property {string} share_with
  41. * @property {string} share_with_displayname
  42. * @property {string} share_with_avatar
  43. * @property {string} mail_send
  44. * @property {Date} expiration optional?
  45. * @property {number} stime optional?
  46. * @property {string} uid_owner
  47. * @property {string} displayname_owner
  48. */
  49. /**
  50. * @typedef {object} OC.Share.Types.ShareItemInfo
  51. * @property {OC.Share.Types.Reshare} reshare
  52. * @property {OC.Share.Types.ShareInfo[]} shares
  53. * @property {OC.Share.Types.LinkShareInfo|undefined} linkShare
  54. */
  55. /**
  56. * These properties are sometimes returned by the server as strings instead
  57. * of integers, so we need to convert them accordingly...
  58. */
  59. var SHARE_RESPONSE_INT_PROPS = [
  60. 'id', 'file_parent', 'mail_send', 'file_source', 'item_source', 'permissions',
  61. 'storage', 'share_type', 'parent', 'stime'
  62. ];
  63. /**
  64. * @class OCA.Share.ShareItemModel
  65. * @classdesc
  66. *
  67. * Represents the GUI of the share dialogue
  68. *
  69. * // FIXME: use OC Share API once #17143 is done
  70. *
  71. * // TODO: this really should be a collection of share item models instead,
  72. * where the link share is one of them
  73. */
  74. var ShareItemModel = OC.Backbone.Model.extend({
  75. /**
  76. * share id of the link share, if applicable
  77. */
  78. _linkShareId: null,
  79. initialize: function(attributes, options) {
  80. if(!_.isUndefined(options.configModel)) {
  81. this.configModel = options.configModel;
  82. }
  83. if(!_.isUndefined(options.fileInfoModel)) {
  84. /** @type {OC.Files.FileInfo} **/
  85. this.fileInfoModel = options.fileInfoModel;
  86. }
  87. _.bindAll(this, 'addShare');
  88. },
  89. defaults: {
  90. allowPublicUploadStatus: false,
  91. permissions: 0,
  92. linkShares: []
  93. },
  94. /**
  95. * Saves the current link share information.
  96. *
  97. * This will trigger an ajax call and, if successful, refetch the model
  98. * afterwards. Callbacks "success", "error" and "complete" can be given
  99. * in the options object; "success" is called after a successful save
  100. * once the model is refetch, "error" is called after a failed save, and
  101. * "complete" is called both after a successful save and after a failed
  102. * save. Note that "complete" is called before "success" and "error" are
  103. * called (unlike in jQuery, in which it is called after them); this
  104. * ensures that "complete" is called even if refetching the model fails.
  105. *
  106. * TODO: this should be a separate model
  107. */
  108. saveLinkShare: function(attributes, options) {
  109. options = options || {};
  110. attributes = _.extend({}, attributes);
  111. var shareId = null;
  112. var call;
  113. // oh yeah...
  114. if (attributes.expiration) {
  115. attributes.expireDate = attributes.expiration;
  116. delete attributes.expiration;
  117. }
  118. var linkShares = this.get('linkShares');
  119. var shareIndex = _.findIndex(linkShares, function(share) {return share.id === attributes.cid})
  120. if (linkShares.length > 0 && shareIndex !== -1) {
  121. shareId = linkShares[shareIndex].id;
  122. // note: update can only update a single value at a time
  123. call = this.updateShare(shareId, attributes, options);
  124. } else {
  125. attributes = _.defaults(attributes, {
  126. hideDownload: false,
  127. password: '',
  128. passwordChanged: false,
  129. sendPasswordByTalk: false,
  130. permissions: OC.PERMISSION_READ,
  131. expireDate: this.configModel.getDefaultExpirationDateString(),
  132. shareType: OC.Share.SHARE_TYPE_LINK
  133. });
  134. call = this.addShare(attributes, options);
  135. }
  136. return call;
  137. },
  138. addShare: function(attributes, options) {
  139. var shareType = attributes.shareType;
  140. attributes = _.extend({}, attributes);
  141. // get default permissions
  142. var defaultPermissions = OC.getCapabilities()['files_sharing']['default_permissions'] || OC.PERMISSION_ALL;
  143. var possiblePermissions = OC.PERMISSION_READ;
  144. if (this.updatePermissionPossible()) {
  145. possiblePermissions = possiblePermissions | OC.PERMISSION_UPDATE;
  146. }
  147. if (this.createPermissionPossible()) {
  148. possiblePermissions = possiblePermissions | OC.PERMISSION_CREATE;
  149. }
  150. if (this.deletePermissionPossible()) {
  151. possiblePermissions = possiblePermissions | OC.PERMISSION_DELETE;
  152. }
  153. if (this.configModel.get('isResharingAllowed') && (this.sharePermissionPossible())) {
  154. possiblePermissions = possiblePermissions | OC.PERMISSION_SHARE;
  155. }
  156. attributes.permissions = defaultPermissions & possiblePermissions;
  157. if (_.isUndefined(attributes.path)) {
  158. attributes.path = this.fileInfoModel.getFullPath();
  159. }
  160. return this._addOrUpdateShare({
  161. type: 'POST',
  162. url: this._getUrl('shares'),
  163. data: attributes,
  164. dataType: 'json'
  165. }, options);
  166. },
  167. updateShare: function(shareId, attrs, options) {
  168. return this._addOrUpdateShare({
  169. type: 'PUT',
  170. url: this._getUrl('shares/' + encodeURIComponent(shareId)),
  171. data: attrs,
  172. dataType: 'json'
  173. }, options);
  174. },
  175. _addOrUpdateShare: function(ajaxSettings, options) {
  176. var self = this;
  177. options = options || {};
  178. return $.ajax(
  179. ajaxSettings
  180. ).always(function() {
  181. if (_.isFunction(options.complete)) {
  182. options.complete(self);
  183. }
  184. }).done(function() {
  185. self.fetch().done(function() {
  186. if (_.isFunction(options.success)) {
  187. options.success(self);
  188. }
  189. });
  190. }).fail(function(xhr) {
  191. var msg = t('core', 'Error');
  192. var result = xhr.responseJSON;
  193. if (result && result.ocs && result.ocs.meta) {
  194. msg = result.ocs.meta.message;
  195. }
  196. if (_.isFunction(options.error)) {
  197. options.error(self, msg);
  198. } else {
  199. OC.dialogs.alert(msg, t('core', 'Error while sharing'));
  200. }
  201. });
  202. },
  203. /**
  204. * Deletes the share with the given id
  205. *
  206. * @param {int} shareId share id
  207. * @return {jQuery}
  208. */
  209. removeShare: function(shareId, options) {
  210. var self = this;
  211. options = options || {};
  212. return $.ajax({
  213. type: 'DELETE',
  214. url: this._getUrl('shares/' + encodeURIComponent(shareId)),
  215. }).done(function() {
  216. self.fetch({
  217. success: function() {
  218. if (_.isFunction(options.success)) {
  219. options.success(self);
  220. }
  221. }
  222. });
  223. }).fail(function(xhr) {
  224. var msg = t('core', 'Error');
  225. var result = xhr.responseJSON;
  226. if (result.ocs && result.ocs.meta) {
  227. msg = result.ocs.meta.message;
  228. }
  229. if (_.isFunction(options.error)) {
  230. options.error(self, msg);
  231. } else {
  232. OC.dialogs.alert(msg, t('core', 'Error removing share'));
  233. }
  234. });
  235. },
  236. /**
  237. * @returns {boolean}
  238. */
  239. isPublicUploadAllowed: function() {
  240. return this.get('allowPublicUploadStatus');
  241. },
  242. isPublicEditingAllowed: function() {
  243. return this.get('allowPublicEditingStatus');
  244. },
  245. /**
  246. * @returns {boolean}
  247. */
  248. isHideFileListSet: function() {
  249. return this.get('hideFileListStatus');
  250. },
  251. /**
  252. * @returns {boolean}
  253. */
  254. isFolder: function() {
  255. return this.get('itemType') === 'folder';
  256. },
  257. /**
  258. * @returns {boolean}
  259. */
  260. isFile: function() {
  261. return this.get('itemType') === 'file';
  262. },
  263. /**
  264. * whether this item has reshare information
  265. * @returns {boolean}
  266. */
  267. hasReshare: function() {
  268. var reshare = this.get('reshare');
  269. return _.isObject(reshare) && !_.isUndefined(reshare.uid_owner);
  270. },
  271. /**
  272. * whether this item has user share information
  273. * @returns {boolean}
  274. */
  275. hasUserShares: function() {
  276. return this.getSharesWithCurrentItem().length > 0;
  277. },
  278. /**
  279. * Returns whether this item has link shares
  280. *
  281. * @return {bool} true if a link share exists, false otherwise
  282. */
  283. hasLinkShares: function() {
  284. var linkShares = this.get('linkShares');
  285. if (linkShares && linkShares.length > 0) {
  286. return true;
  287. }
  288. return false;
  289. },
  290. /**
  291. * @returns {string}
  292. */
  293. getReshareOwner: function() {
  294. return this.get('reshare').uid_owner;
  295. },
  296. /**
  297. * @returns {string}
  298. */
  299. getReshareOwnerDisplayname: function() {
  300. return this.get('reshare').displayname_owner;
  301. },
  302. /**
  303. * @returns {string}
  304. */
  305. getReshareNote: function() {
  306. return this.get('reshare').note;
  307. },
  308. /**
  309. * @returns {string}
  310. */
  311. getReshareWith: function() {
  312. return this.get('reshare').share_with;
  313. },
  314. /**
  315. * @returns {string}
  316. */
  317. getReshareWithDisplayName: function() {
  318. var reshare = this.get('reshare');
  319. return reshare.share_with_displayname || reshare.share_with;
  320. },
  321. /**
  322. * @returns {number}
  323. */
  324. getReshareType: function() {
  325. return this.get('reshare').share_type;
  326. },
  327. getExpireDate: function(shareIndex) {
  328. return this._shareExpireDate(shareIndex);
  329. },
  330. getNote: function(shareIndex) {
  331. return this._shareNote(shareIndex);
  332. },
  333. /**
  334. * Returns all share entries that only apply to the current item
  335. * (file/folder)
  336. *
  337. * @return {Array.<OC.Share.Types.ShareInfo>}
  338. */
  339. getSharesWithCurrentItem: function() {
  340. var shares = this.get('shares') || [];
  341. var fileId = this.fileInfoModel.get('id');
  342. return _.filter(shares, function(share) {
  343. return share.item_source === fileId;
  344. });
  345. },
  346. /**
  347. * @param shareIndex
  348. * @returns {string}
  349. */
  350. getShareWith: function(shareIndex) {
  351. /** @type OC.Share.Types.ShareInfo **/
  352. var share = this.get('shares')[shareIndex];
  353. if(!_.isObject(share)) {
  354. throw "Unknown Share";
  355. }
  356. return share.share_with;
  357. },
  358. /**
  359. * @param shareIndex
  360. * @returns {string}
  361. */
  362. getShareWithDisplayName: function(shareIndex) {
  363. /** @type OC.Share.Types.ShareInfo **/
  364. var share = this.get('shares')[shareIndex];
  365. if(!_.isObject(share)) {
  366. throw "Unknown Share";
  367. }
  368. return share.share_with_displayname;
  369. },
  370. /**
  371. * @param shareIndex
  372. * @returns {string}
  373. */
  374. getShareWithAvatar: function(shareIndex) {
  375. /** @type OC.Share.Types.ShareInfo **/
  376. var share = this.get('shares')[shareIndex];
  377. if(!_.isObject(share)) {
  378. throw "Unknown Share";
  379. }
  380. return share.share_with_avatar;
  381. },
  382. /**
  383. * @param shareIndex
  384. * @returns {string}
  385. */
  386. getSharedBy: function(shareIndex) {
  387. /** @type OC.Share.Types.ShareInfo **/
  388. var share = this.get('shares')[shareIndex];
  389. if(!_.isObject(share)) {
  390. throw "Unknown Share";
  391. }
  392. return share.uid_owner;
  393. },
  394. /**
  395. * @param shareIndex
  396. * @returns {string}
  397. */
  398. getSharedByDisplayName: function(shareIndex) {
  399. /** @type OC.Share.Types.ShareInfo **/
  400. var share = this.get('shares')[shareIndex];
  401. if(!_.isObject(share)) {
  402. throw "Unknown Share";
  403. }
  404. return share.displayname_owner;
  405. },
  406. /**
  407. * @param shareIndex
  408. * @returns {string}
  409. */
  410. getFileOwnerUid: function(shareIndex) {
  411. /** @type OC.Share.Types.ShareInfo **/
  412. var share = this.get('shares')[shareIndex];
  413. if(!_.isObject(share)) {
  414. throw "Unknown Share";
  415. }
  416. return share.uid_file_owner;
  417. },
  418. /**
  419. * returns the array index of a sharee for a provided shareId
  420. *
  421. * @param shareId
  422. * @returns {number}
  423. */
  424. findShareWithIndex: function(shareId) {
  425. var shares = this.get('shares');
  426. if(!_.isArray(shares)) {
  427. throw "Unknown Share";
  428. }
  429. for(var i = 0; i < shares.length; i++) {
  430. var shareWith = shares[i];
  431. if(shareWith.id === shareId) {
  432. return i;
  433. }
  434. }
  435. throw "Unknown Sharee";
  436. },
  437. getShareType: function(shareIndex) {
  438. /** @type OC.Share.Types.ShareInfo **/
  439. var share = this.get('shares')[shareIndex];
  440. if(!_.isObject(share)) {
  441. throw "Unknown Share";
  442. }
  443. return share.share_type;
  444. },
  445. /**
  446. * whether a share from shares has the requested permission
  447. *
  448. * @param {number} shareIndex
  449. * @param {number} permission
  450. * @returns {boolean}
  451. * @private
  452. */
  453. _shareHasPermission: function(shareIndex, permission) {
  454. /** @type OC.Share.Types.ShareInfo **/
  455. var share = this.get('shares')[shareIndex];
  456. if(!_.isObject(share)) {
  457. throw "Unknown Share";
  458. }
  459. return (share.permissions & permission) === permission;
  460. },
  461. _shareExpireDate: function(shareIndex) {
  462. var share = this.get('shares')[shareIndex];
  463. if(!_.isObject(share)) {
  464. throw "Unknown Share";
  465. }
  466. var date2 = share.expiration;
  467. return date2;
  468. },
  469. _shareNote: function(shareIndex) {
  470. var share = this.get('shares')[shareIndex];
  471. if(!_.isObject(share)) {
  472. throw "Unknown Share";
  473. }
  474. return share.note;
  475. },
  476. /**
  477. * @return {int}
  478. */
  479. getPermissions: function() {
  480. return this.get('permissions');
  481. },
  482. /**
  483. * @returns {boolean}
  484. */
  485. sharePermissionPossible: function() {
  486. return (this.get('permissions') & OC.PERMISSION_SHARE) === OC.PERMISSION_SHARE;
  487. },
  488. /**
  489. * @param {number} shareIndex
  490. * @returns {boolean}
  491. */
  492. hasSharePermission: function(shareIndex) {
  493. return this._shareHasPermission(shareIndex, OC.PERMISSION_SHARE);
  494. },
  495. /**
  496. * @returns {boolean}
  497. */
  498. createPermissionPossible: function() {
  499. return (this.get('permissions') & OC.PERMISSION_CREATE) === OC.PERMISSION_CREATE;
  500. },
  501. /**
  502. * @param {number} shareIndex
  503. * @returns {boolean}
  504. */
  505. hasCreatePermission: function(shareIndex) {
  506. return this._shareHasPermission(shareIndex, OC.PERMISSION_CREATE);
  507. },
  508. /**
  509. * @returns {boolean}
  510. */
  511. updatePermissionPossible: function() {
  512. return (this.get('permissions') & OC.PERMISSION_UPDATE) === OC.PERMISSION_UPDATE;
  513. },
  514. /**
  515. * @param {number} shareIndex
  516. * @returns {boolean}
  517. */
  518. hasUpdatePermission: function(shareIndex) {
  519. return this._shareHasPermission(shareIndex, OC.PERMISSION_UPDATE);
  520. },
  521. /**
  522. * @returns {boolean}
  523. */
  524. deletePermissionPossible: function() {
  525. return (this.get('permissions') & OC.PERMISSION_DELETE) === OC.PERMISSION_DELETE;
  526. },
  527. /**
  528. * @param {number} shareIndex
  529. * @returns {boolean}
  530. */
  531. hasDeletePermission: function(shareIndex) {
  532. return this._shareHasPermission(shareIndex, OC.PERMISSION_DELETE);
  533. },
  534. hasReadPermission: function(shareIndex) {
  535. return this._shareHasPermission(shareIndex, OC.PERMISSION_READ);
  536. },
  537. /**
  538. * @returns {boolean}
  539. */
  540. editPermissionPossible: function() {
  541. return this.createPermissionPossible()
  542. || this.updatePermissionPossible()
  543. || this.deletePermissionPossible();
  544. },
  545. /**
  546. * @returns {string}
  547. * The state that the 'can edit' permission checkbox should have.
  548. * Possible values:
  549. * - empty string: no permission
  550. * - 'checked': all applicable permissions
  551. * - 'indeterminate': some but not all permissions
  552. */
  553. editPermissionState: function(shareIndex) {
  554. var hcp = this.hasCreatePermission(shareIndex);
  555. var hup = this.hasUpdatePermission(shareIndex);
  556. var hdp = this.hasDeletePermission(shareIndex);
  557. if (this.isFile()) {
  558. if (hcp || hup || hdp) {
  559. return 'checked';
  560. }
  561. return '';
  562. }
  563. if (!hcp && !hup && !hdp) {
  564. return '';
  565. }
  566. if ( (this.createPermissionPossible() && !hcp)
  567. || (this.updatePermissionPossible() && !hup)
  568. || (this.deletePermissionPossible() && !hdp) ) {
  569. return 'indeterminate';
  570. }
  571. return 'checked';
  572. },
  573. /**
  574. * @returns {int}
  575. */
  576. linkSharePermissions: function(shareId) {
  577. var linkShares = this.get('linkShares');
  578. var shareIndex = _.findIndex(linkShares, function(share) {return share.id === shareId})
  579. if (!this.hasLinkShares()) {
  580. return -1;
  581. } else if (linkShares.length > 0 && shareIndex !== -1) {
  582. return linkShares[shareIndex].permissions;
  583. }
  584. return -1;
  585. },
  586. _getUrl: function(base, params) {
  587. params = _.extend({format: 'json'}, params || {});
  588. return OC.linkToOCS('apps/files_sharing/api/v1', 2) + base + '?' + OC.buildQueryString(params);
  589. },
  590. _fetchShares: function() {
  591. var path = this.fileInfoModel.getFullPath();
  592. return $.ajax({
  593. type: 'GET',
  594. url: this._getUrl('shares', {path: path, reshares: true})
  595. });
  596. },
  597. _fetchReshare: function() {
  598. // only fetch original share once
  599. if (!this._reshareFetched) {
  600. var path = this.fileInfoModel.getFullPath();
  601. this._reshareFetched = true;
  602. return $.ajax({
  603. type: 'GET',
  604. url: this._getUrl('shares', {path: path, shared_with_me: true})
  605. });
  606. } else {
  607. return $.Deferred().resolve([{
  608. ocs: {
  609. data: [this.get('reshare')]
  610. }
  611. }]);
  612. }
  613. },
  614. /**
  615. * Group reshares into a single super share element.
  616. * Does this by finding the most precise share and
  617. * combines the permissions to be the most permissive.
  618. *
  619. * @param {Array} reshares
  620. * @return {Object} reshare
  621. */
  622. _groupReshares: function(reshares) {
  623. if (!reshares || !reshares.length) {
  624. return false;
  625. }
  626. var superShare = reshares.shift();
  627. var combinedPermissions = superShare.permissions;
  628. _.each(reshares, function(reshare) {
  629. // use share have higher priority than group share
  630. if (reshare.share_type === OC.Share.SHARE_TYPE_USER && superShare.share_type === OC.Share.SHARE_TYPE_GROUP) {
  631. superShare = reshare;
  632. }
  633. combinedPermissions |= reshare.permissions;
  634. });
  635. superShare.permissions = combinedPermissions;
  636. return superShare;
  637. },
  638. fetch: function(options) {
  639. var model = this;
  640. this.trigger('request', this);
  641. var deferred = $.when(
  642. this._fetchShares(),
  643. this._fetchReshare()
  644. );
  645. deferred.done(function(data1, data2) {
  646. model.trigger('sync', 'GET', this);
  647. var sharesMap = {};
  648. _.each(data1[0].ocs.data, function(shareItem) {
  649. sharesMap[shareItem.id] = shareItem;
  650. });
  651. var reshare = false;
  652. if (data2[0].ocs.data.length) {
  653. reshare = model._groupReshares(data2[0].ocs.data);
  654. }
  655. model.set(model.parse({
  656. shares: sharesMap,
  657. reshare: reshare
  658. }));
  659. if(!_.isUndefined(options) && _.isFunction(options.success)) {
  660. options.success();
  661. }
  662. });
  663. return deferred;
  664. },
  665. /**
  666. * Updates OC.Share.itemShares and OC.Share.statuses.
  667. *
  668. * This is required in case the user navigates away and comes back,
  669. * the share statuses from the old arrays are still used to fill in the icons
  670. * in the file list.
  671. */
  672. _legacyFillCurrentShares: function(shares) {
  673. var fileId = this.fileInfoModel.get('id');
  674. if (!shares || !shares.length) {
  675. delete OC.Share.statuses[fileId];
  676. OC.Share.currentShares = {};
  677. OC.Share.itemShares = [];
  678. return;
  679. }
  680. var currentShareStatus = OC.Share.statuses[fileId];
  681. if (!currentShareStatus) {
  682. currentShareStatus = {link: false};
  683. OC.Share.statuses[fileId] = currentShareStatus;
  684. }
  685. currentShareStatus.link = false;
  686. OC.Share.currentShares = {};
  687. OC.Share.itemShares = [];
  688. _.each(shares,
  689. /**
  690. * @param {OC.Share.Types.ShareInfo} share
  691. */
  692. function(share) {
  693. if (share.share_type === OC.Share.SHARE_TYPE_LINK) {
  694. OC.Share.itemShares[share.share_type] = true;
  695. currentShareStatus.link = true;
  696. } else {
  697. if (!OC.Share.itemShares[share.share_type]) {
  698. OC.Share.itemShares[share.share_type] = [];
  699. }
  700. OC.Share.itemShares[share.share_type].push(share.share_with);
  701. }
  702. }
  703. );
  704. },
  705. parse: function(data) {
  706. if(data === false) {
  707. console.warn('no data was returned');
  708. this.trigger('fetchError');
  709. return {};
  710. }
  711. var permissions = this.fileInfoModel.get('permissions');
  712. if(!_.isUndefined(data.reshare) && !_.isUndefined(data.reshare.permissions) && data.reshare.uid_owner !== OC.currentUser) {
  713. permissions = permissions & data.reshare.permissions;
  714. }
  715. var allowPublicUploadStatus = false;
  716. if(!_.isUndefined(data.shares)) {
  717. $.each(data.shares, function (key, value) {
  718. if (value.share_type === OC.Share.SHARE_TYPE_LINK) {
  719. allowPublicUploadStatus = (value.permissions & OC.PERMISSION_CREATE) ? true : false;
  720. return true;
  721. }
  722. });
  723. }
  724. var allowPublicEditingStatus = true;
  725. if(!_.isUndefined(data.shares)) {
  726. $.each(data.shares, function (key, value) {
  727. if (value.share_type === OC.Share.SHARE_TYPE_LINK) {
  728. allowPublicEditingStatus = (value.permissions & OC.PERMISSION_UPDATE) ? true : false;
  729. return true;
  730. }
  731. });
  732. }
  733. var hideFileListStatus = false;
  734. if(!_.isUndefined(data.shares)) {
  735. $.each(data.shares, function (key, value) {
  736. if (value.share_type === OC.Share.SHARE_TYPE_LINK) {
  737. hideFileListStatus = (value.permissions & OC.PERMISSION_READ) ? false : true;
  738. return true;
  739. }
  740. });
  741. }
  742. /** @type {OC.Share.Types.ShareInfo[]} **/
  743. var shares = _.map(data.shares, function(share) {
  744. // properly parse some values because sometimes the server
  745. // returns integers as string...
  746. var i;
  747. for (i = 0; i < SHARE_RESPONSE_INT_PROPS.length; i++) {
  748. var prop = SHARE_RESPONSE_INT_PROPS[i];
  749. if (!_.isUndefined(share[prop])) {
  750. share[prop] = parseInt(share[prop], 10);
  751. }
  752. }
  753. return share;
  754. });
  755. this._legacyFillCurrentShares(shares);
  756. var linkShares = [];
  757. // filter out the share by link
  758. shares = _.reject(shares,
  759. /**
  760. * @param {OC.Share.Types.ShareInfo} share
  761. */
  762. function(share) {
  763. var isShareLink =
  764. share.share_type === OC.Share.SHARE_TYPE_LINK
  765. && ( share.file_source === this.get('itemSource')
  766. || share.item_source === this.get('itemSource'));
  767. if (isShareLink) {
  768. /**
  769. * Ignore reshared link shares for now
  770. * FIXME: Find a way to display properly
  771. */
  772. if (share.uid_owner !== OC.currentUser) {
  773. return;
  774. }
  775. var link = window.location.protocol + '//' + window.location.host;
  776. if (!share.token) {
  777. // pre-token link
  778. var fullPath = this.fileInfoModel.get('path') + '/' +
  779. this.fileInfoModel.get('name');
  780. var location = '/' + OC.currentUser + '/files' + fullPath;
  781. var type = this.fileInfoModel.isDirectory() ? 'folder' : 'file';
  782. link += OC.linkTo('', 'public.php') + '?service=files&' +
  783. type + '=' + encodeURIComponent(location);
  784. } else {
  785. link += OC.generateUrl('/s/') + share.token;
  786. }
  787. linkShares.push(_.extend({}, share, {
  788. // hide_download is returned as an int, so force it
  789. // to a boolean
  790. hideDownload: !!share.hide_download,
  791. password: share.share_with,
  792. sendPasswordByTalk: share.send_password_by_talk
  793. }));
  794. return share;
  795. }
  796. },
  797. this
  798. );
  799. return {
  800. reshare: data.reshare,
  801. shares: shares,
  802. linkShares: linkShares,
  803. permissions: permissions,
  804. allowPublicUploadStatus: allowPublicUploadStatus,
  805. allowPublicEditingStatus: allowPublicEditingStatus,
  806. hideFileListStatus: hideFileListStatus
  807. };
  808. },
  809. /**
  810. * Parses a string to an valid integer (unix timestamp)
  811. * @param time
  812. * @returns {*}
  813. * @internal Only used to work around a bug in the backend
  814. */
  815. _parseTime: function(time) {
  816. if (_.isString(time)) {
  817. // skip empty strings and hex values
  818. if (time === '' || (time.length > 1 && time[0] === '0' && time[1] === 'x')) {
  819. return null;
  820. }
  821. time = parseInt(time, 10);
  822. if(isNaN(time)) {
  823. time = null;
  824. }
  825. }
  826. return time;
  827. },
  828. /**
  829. * Returns a list of share types from the existing shares.
  830. *
  831. * @return {Array.<int>} array of share types
  832. */
  833. getShareTypes: function() {
  834. var result;
  835. result = _.pluck(this.getSharesWithCurrentItem(), 'share_type');
  836. if (this.hasLinkShares()) {
  837. result.push(OC.Share.SHARE_TYPE_LINK);
  838. }
  839. return _.uniq(result);
  840. }
  841. });
  842. OC.Share.ShareItemModel = ShareItemModel;
  843. })();