share.js 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. /*
  2. * Copyright (c) 2014
  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. _.extend(OC.Files.Client, {
  12. PROPERTY_SHARE_TYPES: '{' + OC.Files.Client.NS_OWNCLOUD + '}share-types',
  13. PROPERTY_OWNER_ID: '{' + OC.Files.Client.NS_OWNCLOUD + '}owner-id',
  14. PROPERTY_OWNER_DISPLAY_NAME: '{' + OC.Files.Client.NS_OWNCLOUD + '}owner-display-name'
  15. });
  16. if (!OCA.Sharing) {
  17. OCA.Sharing = {};
  18. }
  19. /**
  20. * @namespace
  21. */
  22. OCA.Sharing.Util = {
  23. /**
  24. * Initialize the sharing plugin.
  25. *
  26. * Registers the "Share" file action and adds additional
  27. * DOM attributes for the sharing file info.
  28. *
  29. * @param {OCA.Files.FileList} fileList file list to be extended
  30. */
  31. attach: function(fileList) {
  32. // core sharing is disabled/not loaded
  33. if (!OC.Share) {
  34. return;
  35. }
  36. if (fileList.id === 'trashbin' || fileList.id === 'files.public') {
  37. return;
  38. }
  39. var fileActions = fileList.fileActions;
  40. var oldCreateRow = fileList._createRow;
  41. fileList._createRow = function(fileData) {
  42. var tr = oldCreateRow.apply(this, arguments);
  43. var sharePermissions = OCA.Sharing.Util.getSharePermissions(fileData);
  44. if (fileData.permissions === 0) {
  45. // no permission, disabling sidebar
  46. delete fileActions.actions.all.Comment;
  47. delete fileActions.actions.all.Details;
  48. delete fileActions.actions.all.Goto;
  49. }
  50. tr.attr('data-share-permissions', sharePermissions);
  51. if (fileData.shareOwner) {
  52. tr.attr('data-share-owner', fileData.shareOwner);
  53. tr.attr('data-share-owner-id', fileData.shareOwnerId);
  54. // user should always be able to rename a mount point
  55. if (fileData.mountType === 'shared-root') {
  56. tr.attr('data-permissions', fileData.permissions | OC.PERMISSION_UPDATE);
  57. }
  58. }
  59. if (fileData.recipientData && !_.isEmpty(fileData.recipientData)) {
  60. tr.attr('data-share-recipient-data', JSON.stringify(fileData.recipientData));
  61. }
  62. if (fileData.shareTypes) {
  63. tr.attr('data-share-types', fileData.shareTypes.join(','));
  64. }
  65. return tr;
  66. };
  67. var oldElementToFile = fileList.elementToFile;
  68. fileList.elementToFile = function($el) {
  69. var fileInfo = oldElementToFile.apply(this, arguments);
  70. fileInfo.sharePermissions = $el.attr('data-share-permissions') || undefined;
  71. fileInfo.shareOwner = $el.attr('data-share-owner') || undefined;
  72. fileInfo.shareOwnerId = $el.attr('data-share-owner-id') || undefined;
  73. if( $el.attr('data-share-types')){
  74. fileInfo.shareTypes = $el.attr('data-share-types').split(',');
  75. }
  76. if( $el.attr('data-expiration')){
  77. var expirationTimestamp = parseInt($el.attr('data-expiration'));
  78. fileInfo.shares = [];
  79. fileInfo.shares.push({expiration: expirationTimestamp});
  80. }
  81. return fileInfo;
  82. };
  83. var oldGetWebdavProperties = fileList._getWebdavProperties;
  84. fileList._getWebdavProperties = function() {
  85. var props = oldGetWebdavProperties.apply(this, arguments);
  86. props.push(OC.Files.Client.PROPERTY_OWNER_ID);
  87. props.push(OC.Files.Client.PROPERTY_OWNER_DISPLAY_NAME);
  88. props.push(OC.Files.Client.PROPERTY_SHARE_TYPES);
  89. return props;
  90. };
  91. fileList.filesClient.addFileInfoParser(function(response) {
  92. var data = {};
  93. var props = response.propStat[0].properties;
  94. var permissionsProp = props[OC.Files.Client.PROPERTY_PERMISSIONS];
  95. if (permissionsProp && permissionsProp.indexOf('S') >= 0) {
  96. data.shareOwner = props[OC.Files.Client.PROPERTY_OWNER_DISPLAY_NAME];
  97. data.shareOwnerId = props[OC.Files.Client.PROPERTY_OWNER_ID];
  98. }
  99. var shareTypesProp = props[OC.Files.Client.PROPERTY_SHARE_TYPES];
  100. if (shareTypesProp) {
  101. data.shareTypes = _.chain(shareTypesProp).filter(function(xmlvalue) {
  102. return (xmlvalue.namespaceURI === OC.Files.Client.NS_OWNCLOUD && xmlvalue.nodeName.split(':')[1] === 'share-type');
  103. }).map(function(xmlvalue) {
  104. return parseInt(xmlvalue.textContent || xmlvalue.text, 10);
  105. }).value();
  106. }
  107. return data;
  108. });
  109. // use delegate to catch the case with multiple file lists
  110. fileList.$el.on('fileActionsReady', function(ev){
  111. var $files = ev.$files;
  112. _.each($files, function(file) {
  113. var $tr = $(file);
  114. var shareTypes = $tr.attr('data-share-types') || '';
  115. var shareOwner = $tr.attr('data-share-owner');
  116. if (shareTypes || shareOwner) {
  117. var hasLink = false;
  118. var hasShares = false;
  119. _.each(shareTypes.split(',') || [], function(shareType) {
  120. shareType = parseInt(shareType, 10);
  121. if (shareType === OC.Share.SHARE_TYPE_LINK) {
  122. hasLink = true;
  123. } else if (shareType === OC.Share.SHARE_TYPE_EMAIL) {
  124. hasLink = true;
  125. } else if (shareType === OC.Share.SHARE_TYPE_USER) {
  126. hasShares = true;
  127. } else if (shareType === OC.Share.SHARE_TYPE_GROUP) {
  128. hasShares = true;
  129. } else if (shareType === OC.Share.SHARE_TYPE_REMOTE) {
  130. hasShares = true;
  131. } else if (shareType === OC.Share.SHARE_TYPE_CIRCLE) {
  132. hasShares = true;
  133. } else if (shareType === OC.Share.SHARE_TYPE_ROOM) {
  134. hasShares = true;
  135. }
  136. });
  137. OCA.Sharing.Util._updateFileActionIcon($tr, hasShares, hasLink);
  138. }
  139. });
  140. });
  141. fileList.$el.on('changeDirectory', function() {
  142. OCA.Sharing.sharesLoaded = false;
  143. });
  144. fileActions.registerAction({
  145. name: 'Share',
  146. displayName: '',
  147. altText: t('core', 'Share'),
  148. mime: 'all',
  149. permissions: OC.PERMISSION_ALL,
  150. iconClass: 'icon-shared',
  151. type: OCA.Files.FileActions.TYPE_INLINE,
  152. actionHandler: function(fileName, context) {
  153. // do not open sidebar if permission is set and equal to 0
  154. var permissions = parseInt(context.$file.data('share-permissions'), 10);
  155. if (isNaN(permissions) || permissions > 0) {
  156. fileList.showDetailsView(fileName, 'shareTabView');
  157. }
  158. },
  159. render: function(actionSpec, isDefault, context) {
  160. var permissions = parseInt(context.$file.data('permissions'), 10);
  161. // if no share permissions but share owner exists, still show the link
  162. if ((permissions & OC.PERMISSION_SHARE) !== 0 || context.$file.attr('data-share-owner')) {
  163. return fileActions._defaultRenderAction.call(fileActions, actionSpec, isDefault, context);
  164. }
  165. // don't render anything
  166. return null;
  167. }
  168. });
  169. var shareTab = new OCA.Sharing.ShareTabView('shareTabView', {order: -20});
  170. // detect changes and change the matching list entry
  171. shareTab.on('sharesChanged', function(shareModel) {
  172. var fileInfoModel = shareModel.fileInfoModel;
  173. var $tr = fileList.findFileEl(fileInfoModel.get('name'));
  174. // We count email shares as link share
  175. var hasLinkShares = shareModel.hasLinkShares();
  176. shareModel.get('shares').forEach(function (share) {
  177. if (share.share_type === OC.Share.SHARE_TYPE_EMAIL) {
  178. hasLinkShares = true;
  179. }
  180. });
  181. OCA.Sharing.Util._updateFileListDataAttributes(fileList, $tr, shareModel);
  182. if (!OCA.Sharing.Util._updateFileActionIcon($tr, shareModel.hasUserShares(), hasLinkShares)) {
  183. // remove icon, if applicable
  184. OC.Share.markFileAsShared($tr, false, false);
  185. }
  186. // FIXME: this is too convoluted. We need to get rid of the above updates
  187. // and only ever update the model and let the events take care of rerendering
  188. fileInfoModel.set({
  189. shareTypes: shareModel.getShareTypes(),
  190. // in case markFileAsShared decided to change the icon,
  191. // we need to modify the model
  192. // (FIXME: yes, this is hacky)
  193. icon: $tr.attr('data-icon')
  194. });
  195. });
  196. fileList.registerTabView(shareTab);
  197. var breadCrumbSharingDetailView = new OCA.Sharing.ShareBreadCrumbView({shareTab: shareTab});
  198. fileList.registerBreadCrumbDetailView(breadCrumbSharingDetailView);
  199. },
  200. /**
  201. * Update file list data attributes
  202. */
  203. _updateFileListDataAttributes: function(fileList, $tr, shareModel) {
  204. // files app current cannot show recipients on load, so we don't update the
  205. // icon when changed for consistency
  206. if (fileList.id === 'files') {
  207. return;
  208. }
  209. var recipients = _.pluck(shareModel.get('shares'), 'share_with_displayname');
  210. // note: we only update the data attribute because updateIcon()
  211. if (recipients.length) {
  212. var recipientData = _.mapObject(shareModel.get('shares'), function (share) {
  213. return {shareWith: share.share_with, shareWithDisplayName: share.share_with_displayname};
  214. });
  215. $tr.attr('data-share-recipient-data', JSON.stringify(recipientData));
  216. }
  217. else {
  218. $tr.removeAttr('data-share-recipient-data');
  219. }
  220. },
  221. /**
  222. * Update the file action share icon for the given file
  223. *
  224. * @param $tr file element of the file to update
  225. * @param {boolean} hasUserShares true if a user share exists
  226. * @param {boolean} hasLinkShares true if a link share exists
  227. *
  228. * @return {boolean} true if the icon was set, false otherwise
  229. */
  230. _updateFileActionIcon: function($tr, hasUserShares, hasLinkShares) {
  231. // if the statuses are loaded already, use them for the icon
  232. // (needed when scrolling to the next page)
  233. if (hasUserShares || hasLinkShares || $tr.attr('data-share-recipient-data') || $tr.attr('data-share-owner')) {
  234. OC.Share.markFileAsShared($tr, true, hasLinkShares);
  235. return true;
  236. }
  237. return false;
  238. },
  239. /**
  240. * @param {Array} fileData
  241. * @returns {String}
  242. */
  243. getSharePermissions: function(fileData) {
  244. return fileData.sharePermissions;
  245. }
  246. };
  247. })();
  248. OC.Plugins.register('OCA.Files.FileList', OCA.Sharing.Util);