cozy-sharing 28.4.4 → 28.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/SharingProvider.js +296 -66
  3. package/dist/SharingProvider.spec.js +332 -2
  4. package/dist/components/FederatedFolder/DumbFederatedFolderModal.js +7 -4
  5. package/dist/components/FederatedFolder/FederatedFolderModal.js +79 -23
  6. package/dist/components/Recipient/LinkRecipient.js +6 -3
  7. package/dist/components/Recipient/LinkRecipient.stories.js +0 -1
  8. package/dist/components/Recipient/MemberRecipient.spec.js +13 -0
  9. package/dist/components/Recipient/MemberRecipientPermissions.js +80 -20
  10. package/dist/components/Recipient/MemberRecipientPermissions.spec.js +214 -0
  11. package/dist/components/Recipient/actions/setReadOnlySharedPermission.js +0 -1
  12. package/dist/components/Recipient/actions/setReadOnlySharedPermission.spec.js +72 -0
  13. package/dist/components/Recipient/actions/setReadWriteSharedPermission.js +0 -1
  14. package/dist/components/Recipient/actions/setReadWriteSharedPermission.spec.js +72 -0
  15. package/dist/components/ShareByLink.js +5 -1
  16. package/dist/components/ShareDialogCozyToCozy.spec.js +9 -0
  17. package/dist/components/ShareRestrictionModal/helpers.js +66 -29
  18. package/dist/components/SharedDrive/DumbSharedDriveModal.js +1 -1
  19. package/dist/components/SharedFolder/DumbBatchSharedFolderModal.js +17 -4
  20. package/dist/components/WhoHasAccess.js +0 -1
  21. package/dist/helpers/shortcodes.js +24 -0
  22. package/dist/state.js +3 -10
  23. package/locales/en.json +4 -1
  24. package/locales/fr.json +4 -1
  25. package/locales/ru.json +4 -1
  26. package/locales/vi.json +4 -1
  27. package/package.json +4 -4
  28. package/src/SharingProvider.jsx +128 -9
  29. package/src/SharingProvider.spec.jsx +224 -1
  30. package/src/components/FederatedFolder/DumbFederatedFolderModal.jsx +6 -3
  31. package/src/components/FederatedFolder/FederatedFolderModal.jsx +39 -6
  32. package/src/components/Recipient/LinkRecipient.jsx +10 -4
  33. package/src/components/Recipient/LinkRecipient.stories.jsx +0 -1
  34. package/src/components/Recipient/MemberRecipient.spec.jsx +11 -0
  35. package/src/components/Recipient/MemberRecipientPermissions.jsx +69 -16
  36. package/src/components/Recipient/MemberRecipientPermissions.spec.jsx +165 -0
  37. package/src/components/Recipient/actions/setReadOnlySharedPermission.js +1 -1
  38. package/src/components/Recipient/actions/setReadOnlySharedPermission.spec.jsx +95 -0
  39. package/src/components/Recipient/actions/setReadWriteSharedPermission.js +1 -1
  40. package/src/components/Recipient/actions/setReadWriteSharedPermission.spec.jsx +95 -0
  41. package/src/components/ShareByLink.jsx +3 -1
  42. package/src/components/ShareDialogCozyToCozy.spec.jsx +6 -0
  43. package/src/components/ShareRestrictionModal/helpers.js +42 -4
  44. package/src/components/SharedDrive/DumbSharedDriveModal.jsx +1 -1
  45. package/src/components/SharedFolder/DumbBatchSharedFolderModal.jsx +32 -16
  46. package/src/components/WhoHasAccess.jsx +0 -1
  47. package/src/helpers/shortcodes.js +28 -0
  48. package/src/state.js +3 -14
@@ -3,6 +3,7 @@ import _regeneratorRuntime from "@babel/runtime/regenerator";
3
3
  import { generateWebLink } from 'cozy-client';
4
4
  import minilog from 'cozy-minilog';
5
5
  import { getAppSlugFromDocumentType } from '../../helpers/link';
6
+ import { getShortcode } from '../../helpers/shortcodes';
6
7
  var log = minilog('ShareRestrictionModal/helpers');
7
8
  export var WRITE_PERMS = ['GET', 'POST', 'PUT', 'PATCH'];
8
9
  export var READ_ONLY_PERMS = ['GET'];
@@ -24,14 +25,12 @@ export var READ_ONLY_PERMS = ['GET'];
24
25
 
25
26
  export var createSharingLink = /*#__PURE__*/function () {
26
27
  var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee(_ref) {
27
- var _permsResult$data;
28
-
29
- var client, file, documentType, shareByLink, t, ttl, _ref$editingRights, editingRights, password, showAlert, permsResult, perms;
28
+ var client, file, documentType, shareByLink, getOwner, getSharingById, t, ttl, _ref$editingRights, editingRights, password, showAlert, permsResult, code;
30
29
 
31
30
  return _regeneratorRuntime.wrap(function _callee$(_context) {
32
31
  while (1) switch (_context.prev = _context.next) {
33
32
  case 0:
34
- client = _ref.client, file = _ref.file, documentType = _ref.documentType, shareByLink = _ref.shareByLink, t = _ref.t, ttl = _ref.ttl, _ref$editingRights = _ref.editingRights, editingRights = _ref$editingRights === void 0 ? 'readOnly' : _ref$editingRights, password = _ref.password, showAlert = _ref.showAlert;
33
+ client = _ref.client, file = _ref.file, documentType = _ref.documentType, shareByLink = _ref.shareByLink, getOwner = _ref.getOwner, getSharingById = _ref.getSharingById, t = _ref.t, ttl = _ref.ttl, _ref$editingRights = _ref.editingRights, editingRights = _ref$editingRights === void 0 ? 'readOnly' : _ref$editingRights, password = _ref.password, showAlert = _ref.showAlert;
35
34
  _context.next = 3;
36
35
  return createPermissions({
37
36
  file: file,
@@ -46,24 +45,23 @@ export var createSharingLink = /*#__PURE__*/function () {
46
45
 
47
46
  case 3:
48
47
  permsResult = _context.sent;
48
+ code = getShortcode(permsResult === null || permsResult === void 0 ? void 0 : permsResult.data);
49
49
 
50
- if (permsResult !== null && permsResult !== void 0 && (_permsResult$data = permsResult.data) !== null && _permsResult$data !== void 0 && (_permsResult$data = _permsResult$data.attributes) !== null && _permsResult$data !== void 0 && (_permsResult$data = _permsResult$data.shortcodes) !== null && _permsResult$data !== void 0 && _permsResult$data.code) {
51
- _context.next = 6;
50
+ if (code) {
51
+ _context.next = 7;
52
52
  break;
53
53
  }
54
54
 
55
55
  return _context.abrupt("return", null);
56
56
 
57
- case 6:
58
- perms = permsResult.data;
59
- return _context.abrupt("return", generateWebLink({
60
- cozyUrl: client.getStackClient().uri,
61
- searchParams: [['sharecode', perms.attributes.shortcodes.code]],
62
- pathname: '/public',
63
- slug: getAppSlugFromDocumentType({
64
- documentType: documentType
65
- }),
66
- subDomainType: client.capabilities.flat_subdomains ? 'flat' : 'nested'
57
+ case 7:
58
+ return _context.abrupt("return", generateShareLinkFromFile({
59
+ client: client,
60
+ file: file,
61
+ sharecode: code,
62
+ getOwner: getOwner,
63
+ getSharingById: getSharingById,
64
+ documentType: documentType
67
65
  }));
68
66
 
69
67
  case 8:
@@ -77,6 +75,45 @@ export var createSharingLink = /*#__PURE__*/function () {
77
75
  return _ref2.apply(this, arguments);
78
76
  };
79
77
  }();
78
+ /**
79
+ * Generate a sharing link from a file and sharecode
80
+ * @param {object} options
81
+ * @param {object} options.client - CozyClient instance
82
+ * @param {object} options.file - File to share
83
+ * @param {string} options.sharecode - Share code
84
+ * @param {Function} options.getOwner - Function to get the owner of a sharing
85
+ * @param {Function} options.getSharingById - Function to get a sharing by ID
86
+ * @param {string} options.documentType - Type of the document
87
+ * @returns {string} - The generated web link
88
+ */
89
+
90
+ export var generateShareLinkFromFile = function generateShareLinkFromFile(_ref3) {
91
+ var client = _ref3.client,
92
+ file = _ref3.file,
93
+ sharecode = _ref3.sharecode,
94
+ getOwner = _ref3.getOwner,
95
+ getSharingById = _ref3.getSharingById,
96
+ documentType = _ref3.documentType;
97
+ var cozyUrl = client.getStackClient().uri;
98
+
99
+ if (file.driveId) {
100
+ var _sharing$rules;
101
+
102
+ var sharing = getSharingById(file.driveId);
103
+ var owner = getOwner(sharing === null || sharing === void 0 || (_sharing$rules = sharing.rules) === null || _sharing$rules === void 0 || (_sharing$rules = _sharing$rules[0]) === null || _sharing$rules === void 0 || (_sharing$rules = _sharing$rules.values) === null || _sharing$rules === void 0 ? void 0 : _sharing$rules[0]);
104
+ cozyUrl = (owner === null || owner === void 0 ? void 0 : owner.instance) || client.getStackClient().uri;
105
+ }
106
+
107
+ return generateWebLink({
108
+ cozyUrl: cozyUrl,
109
+ searchParams: [['sharecode', sharecode]],
110
+ pathname: '/public',
111
+ slug: getAppSlugFromDocumentType({
112
+ documentType: documentType
113
+ }),
114
+ subDomainType: client.capabilities.flat_subdomains ? 'flat' : 'nested'
115
+ });
116
+ };
80
117
  /**
81
118
  * Copy a string to the clipboard
82
119
  *
@@ -87,8 +124,8 @@ export var createSharingLink = /*#__PURE__*/function () {
87
124
  */
88
125
 
89
126
  export var copyToClipboard = /*#__PURE__*/function () {
90
- var _ref3 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(value) {
91
- var _ref4,
127
+ var _ref4 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2(value) {
128
+ var _ref5,
92
129
  t,
93
130
  showAlert,
94
131
  _args2 = arguments;
@@ -96,7 +133,7 @@ export var copyToClipboard = /*#__PURE__*/function () {
96
133
  return _regeneratorRuntime.wrap(function _callee2$(_context2) {
97
134
  while (1) switch (_context2.prev = _context2.next) {
98
135
  case 0:
99
- _ref4 = _args2.length > 1 && _args2[1] !== undefined ? _args2[1] : {}, t = _ref4.t, showAlert = _ref4.showAlert;
136
+ _ref5 = _args2.length > 1 && _args2[1] !== undefined ? _args2[1] : {}, t = _ref5.t, showAlert = _ref5.showAlert;
100
137
 
101
138
  if (value) {
102
139
  _context2.next = 3;
@@ -137,7 +174,7 @@ export var copyToClipboard = /*#__PURE__*/function () {
137
174
  }));
138
175
 
139
176
  return function copyToClipboard(_x2) {
140
- return _ref3.apply(this, arguments);
177
+ return _ref4.apply(this, arguments);
141
178
  };
142
179
  }();
143
180
  /**
@@ -188,12 +225,12 @@ export var makeTTL = function makeTTL(selectedDate) {
188
225
  */
189
226
 
190
227
  export var createPermissions = /*#__PURE__*/function () {
191
- var _ref6 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee3(_ref5) {
228
+ var _ref7 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee3(_ref6) {
192
229
  var file, t, ttl, password, editingRights, documentType, shareByLink, showAlert, verbs;
193
230
  return _regeneratorRuntime.wrap(function _callee3$(_context3) {
194
231
  while (1) switch (_context3.prev = _context3.next) {
195
232
  case 0:
196
- file = _ref5.file, t = _ref5.t, ttl = _ref5.ttl, password = _ref5.password, editingRights = _ref5.editingRights, documentType = _ref5.documentType, shareByLink = _ref5.shareByLink, showAlert = _ref5.showAlert;
233
+ file = _ref6.file, t = _ref6.t, ttl = _ref6.ttl, password = _ref6.password, editingRights = _ref6.editingRights, documentType = _ref6.documentType, shareByLink = _ref6.shareByLink, showAlert = _ref6.showAlert;
197
234
  _context3.prev = 1;
198
235
  verbs = editingRights === 'readOnly' ? READ_ONLY_PERMS : WRITE_PERMS;
199
236
  return _context3.abrupt("return", shareByLink(file, {
@@ -220,7 +257,7 @@ export var createPermissions = /*#__PURE__*/function () {
220
257
  }));
221
258
 
222
259
  return function createPermissions(_x3) {
223
- return _ref6.apply(this, arguments);
260
+ return _ref7.apply(this, arguments);
224
261
  };
225
262
  }();
226
263
  /**
@@ -239,12 +276,12 @@ export var createPermissions = /*#__PURE__*/function () {
239
276
  */
240
277
 
241
278
  export var updatePermissions = /*#__PURE__*/function () {
242
- var _ref8 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee4(_ref7) {
279
+ var _ref9 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee4(_ref8) {
243
280
  var file, t, dateToggle, selectedDate, passwordToggle, password, editingRights, documentType, updateDocumentPermissions, showAlert, expiresAt, ensurePassword, verbs;
244
281
  return _regeneratorRuntime.wrap(function _callee4$(_context4) {
245
282
  while (1) switch (_context4.prev = _context4.next) {
246
283
  case 0:
247
- file = _ref7.file, t = _ref7.t, dateToggle = _ref7.dateToggle, selectedDate = _ref7.selectedDate, passwordToggle = _ref7.passwordToggle, password = _ref7.password, editingRights = _ref7.editingRights, documentType = _ref7.documentType, updateDocumentPermissions = _ref7.updateDocumentPermissions, showAlert = _ref7.showAlert;
284
+ file = _ref8.file, t = _ref8.t, dateToggle = _ref8.dateToggle, selectedDate = _ref8.selectedDate, passwordToggle = _ref8.passwordToggle, password = _ref8.password, editingRights = _ref8.editingRights, documentType = _ref8.documentType, updateDocumentPermissions = _ref8.updateDocumentPermissions, showAlert = _ref8.showAlert;
248
285
  _context4.prev = 1;
249
286
  expiresAt = dateToggle ? (selectedDate === null || selectedDate === void 0 ? void 0 : selectedDate.toISOString()) || undefined : '';
250
287
  ensurePassword = passwordToggle ? password || undefined : '';
@@ -273,7 +310,7 @@ export var updatePermissions = /*#__PURE__*/function () {
273
310
  }));
274
311
 
275
312
  return function updatePermissions(_x4) {
276
- return _ref8.apply(this, arguments);
313
+ return _ref9.apply(this, arguments);
277
314
  };
278
315
  }();
279
316
  /**
@@ -287,12 +324,12 @@ export var updatePermissions = /*#__PURE__*/function () {
287
324
  */
288
325
 
289
326
  export var revokePermissions = /*#__PURE__*/function () {
290
- var _ref10 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee5(_ref9) {
327
+ var _ref11 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee5(_ref10) {
291
328
  var file, t, documentType, revokeSharingLink, showAlert;
292
329
  return _regeneratorRuntime.wrap(function _callee5$(_context5) {
293
330
  while (1) switch (_context5.prev = _context5.next) {
294
331
  case 0:
295
- file = _ref9.file, t = _ref9.t, documentType = _ref9.documentType, revokeSharingLink = _ref9.revokeSharingLink, showAlert = _ref9.showAlert;
332
+ file = _ref10.file, t = _ref10.t, documentType = _ref10.documentType, revokeSharingLink = _ref10.revokeSharingLink, showAlert = _ref10.showAlert;
296
333
  _context5.prev = 1;
297
334
  return _context5.abrupt("return", revokeSharingLink(file));
298
335
 
@@ -314,6 +351,6 @@ export var revokePermissions = /*#__PURE__*/function () {
314
351
  }));
315
352
 
316
353
  return function revokePermissions(_x5) {
317
- return _ref10.apply(this, arguments);
354
+ return _ref11.apply(this, arguments);
318
355
  };
319
356
  }();
@@ -2,7 +2,7 @@ import PropTypes from 'prop-types';
2
2
  import React from 'react';
3
3
  import { useI18n } from 'twake-i18n';
4
4
  import withLocales from '../../hoc/withLocales';
5
- import DumbBatchSharedFolderModal from '../SharedFolder/DumbBatchSharedFolderModal';
5
+ import { DumbBatchSharedFolderModal } from '../SharedFolder/DumbBatchSharedFolderModal';
6
6
  export var DumbSharedDriveModal = withLocales(function (_ref) {
7
7
  var title = _ref.title,
8
8
  document = _ref.document,
@@ -34,7 +34,9 @@ export var DumbBatchSharedFolderModal = withLocales(function (_ref) {
34
34
  createLabel = _ref.createLabel,
35
35
  shareLabel = _ref.shareLabel,
36
36
  saveLabel = _ref.saveLabel,
37
- sharingDesc = _ref.sharingDesc;
37
+ sharingDesc = _ref.sharingDesc,
38
+ _ref$showShareByEmail = _ref.showShareByEmail,
39
+ showShareByEmail = _ref$showShareByEmail === void 0 ? true : _ref$showShareByEmail;
38
40
  var hasRecipients = Boolean(recipients === null || recipients === void 0 ? void 0 : recipients.length); // Determine action buttons based on modal mode
39
41
 
40
42
  var actionButtons = function () {
@@ -50,6 +52,14 @@ export var DumbBatchSharedFolderModal = withLocales(function (_ref) {
50
52
  }));
51
53
  }
52
54
 
55
+ if (!showShareByEmail) {
56
+ return /*#__PURE__*/React.createElement(ShareByLink, {
57
+ link: sharingLink,
58
+ document: document,
59
+ documentType: "Files"
60
+ });
61
+ }
62
+
53
63
  if (!showNameField) {
54
64
  return /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(ShareByLink, {
55
65
  link: sharingLink,
@@ -104,7 +114,7 @@ export var DumbBatchSharedFolderModal = withLocales(function (_ref) {
104
114
  }), /*#__PURE__*/React.createElement(Typography, {
105
115
  variant: "h6",
106
116
  className: "u-mt-1-half u-mb-half"
107
- }, addPeopleLabel), /*#__PURE__*/React.createElement(DumbShareByEmail, {
117
+ }, addPeopleLabel), showShareByEmail && /*#__PURE__*/React.createElement(DumbShareByEmail, {
108
118
  createContact: createContact,
109
119
  currentRecipients: currentRecipients,
110
120
  document: document,
@@ -154,6 +164,9 @@ DumbBatchSharedFolderModal.propTypes = {
154
164
  createLabel: PropTypes.string,
155
165
  shareLabel: PropTypes.string,
156
166
  saveLabel: PropTypes.string,
157
- sharingDesc: PropTypes.string.isRequired
167
+ sharingDesc: PropTypes.string,
168
+ showShareByEmail: PropTypes.bool
158
169
  };
159
- export default DumbBatchSharedFolderModal;
170
+ DumbBatchSharedFolderModal.defaultProps = {
171
+ sharingDesc: ''
172
+ };
@@ -55,7 +55,6 @@ var WhoHasAccess = function WhoHasAccess(_ref2) {
55
55
  documentType: documentType,
56
56
  onRevoke: onRevoke,
57
57
  onRevokeSelf: onRevokeSelf,
58
- link: link,
59
58
  permissions: permissions,
60
59
  fadeIn: linkHasBeenJustCreated
61
60
  }), /*#__PURE__*/React.createElement(OwnerRecipient, {
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Get the shortcode from a permission object
3
+ * The shortcode can be in different places depending on how the sharing was created:
4
+ * - attributes.shortcodes.email
5
+ * - attributes.shortcodes.code
6
+ * - attributes.codes.email
7
+ * - attributes.codes.code
8
+ *
9
+ * We used to use `email` as `codes` attribute for a sharingByLink.
10
+ * But when we use cozy-client to create a Permission, by default
11
+ * the codes attribute is set to `code`. MesPapiers app is using this
12
+ * default behavior... So the sharing by link created by mes papiers
13
+ * didn't appear correctly in cozy-sharing.
14
+ * This is a bit ugly, we should have a better way to know if this is
15
+ * a sharing by link or not.
16
+ *
17
+ * @param {object} permission - The permission object
18
+ * @returns {string|null} The shortcode or null if not found
19
+ */
20
+ export var getShortcode = function getShortcode(permission) {
21
+ var _permission$attribute, _permission$attribute2, _permission$attribute3, _permission$attribute4;
22
+
23
+ return (permission === null || permission === void 0 || (_permission$attribute = permission.attributes) === null || _permission$attribute === void 0 || (_permission$attribute = _permission$attribute.shortcodes) === null || _permission$attribute === void 0 ? void 0 : _permission$attribute.email) || (permission === null || permission === void 0 || (_permission$attribute2 = permission.attributes) === null || _permission$attribute2 === void 0 || (_permission$attribute2 = _permission$attribute2.shortcodes) === null || _permission$attribute2 === void 0 ? void 0 : _permission$attribute2.code) || (permission === null || permission === void 0 || (_permission$attribute3 = permission.attributes) === null || _permission$attribute3 === void 0 || (_permission$attribute3 = _permission$attribute3.codes) === null || _permission$attribute3 === void 0 ? void 0 : _permission$attribute3.email) || (permission === null || permission === void 0 || (_permission$attribute4 = permission.attributes) === null || _permission$attribute4 === void 0 || (_permission$attribute4 = _permission$attribute4.codes) === null || _permission$attribute4 === void 0 ? void 0 : _permission$attribute4.code) || null;
24
+ };
package/dist/state.js CHANGED
@@ -10,8 +10,8 @@ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (O
10
10
 
11
11
  function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
12
12
 
13
- import get from 'lodash/get';
14
13
  import flag from 'cozy-flags';
14
+ import { getShortcode } from './helpers/shortcodes';
15
15
  var RECEIVE_SHARINGS = 'RECEIVE_SHARINGS';
16
16
  var ADD_SHARING = 'ADD_SHARING';
17
17
  var UPDATE_SHARING = 'UPDATE_SHARING';
@@ -521,15 +521,8 @@ export var getSharingLink = function getSharingLink(state, docId, documentType)
521
521
  // This shouldn't have happened, but unfortunately some duplicate sharing links have been created in the past
522
522
  var perms = getDocumentPermissions(state, docId);
523
523
  if (perms.length === 0) return null;
524
- var perm = perms[0]; // We used to use `email` as `codes` attribute for a sharingByLink.
525
- // But when we use cozy-client to create a Permission, by default
526
- // the codes attribute is set to `code`. MesPapiers app is using this
527
- // default behavior... So the sharing by link created by mes papiers
528
- // didn't appear correctly in cozy-sharing.
529
- // This is a bit ugly, we should have a better way to know if this is
530
- // a sharing by link or not.
531
-
532
- var code = get(perm, 'attributes.shortcodes.email') || get(perm, 'attributes.shortcodes.code') || get(perm, 'attributes.codes.email') || get(perm, 'attributes.codes.code');
524
+ var perm = perms[0];
525
+ var code = getShortcode(perm);
533
526
 
534
527
  if (code) {
535
528
  return buildSharingLink(state, documentType, code);
package/locales/en.json CHANGED
@@ -34,7 +34,10 @@
34
34
  "members": {
35
35
  "count": "%{smart_count} member |||| %{smart_count} members",
36
36
  "others": "and 1 other… |||| and %{smart_count} others…",
37
- "otherContacts": "other contact |||| other contacts"
37
+ "otherContacts": "other contact |||| other contacts",
38
+ "error": {
39
+ "changePermission": "An error occurred while changing the permissions. Please try again."
40
+ }
38
41
  },
39
42
  "contacts": {
40
43
  "whoHasAccess": "Who has access?",
package/locales/fr.json CHANGED
@@ -34,7 +34,10 @@
34
34
  "members": {
35
35
  "count": "%{smart_count} membre |||| %{smart_count} membres",
36
36
  "others": "et 1 autre… |||| et %{smart_count} autres…",
37
- "otherContacts": "autre contact |||| autres contacts"
37
+ "otherContacts": "autre contact |||| autres contacts",
38
+ "error": {
39
+ "changePermission": "Une erreur s'est produite lors du changement des permissions. Veuillez réessayer."
40
+ }
38
41
  },
39
42
  "contacts": {
40
43
  "whoHasAccess": "Qui a accès ?",
package/locales/ru.json CHANGED
@@ -34,7 +34,10 @@
34
34
  "members": {
35
35
  "count": "%{smart_count} участник |||| %{smart_count} участника |||| %{smart_count} участников",
36
36
  "others": "и 1 другой… |||| и %{smart_count} других…",
37
- "otherContacts": "другой контакт |||| других контакта"
37
+ "otherContacts": "другой контакт |||| других контакта",
38
+ "error": {
39
+ "changePermission": "Произошла ошибка при изменении разрешений. Пожалуйста, попробуйте снова."
40
+ }
38
41
  },
39
42
  "contacts": {
40
43
  "whoHasAccess": "У кого есть доступ?",
package/locales/vi.json CHANGED
@@ -34,7 +34,10 @@
34
34
  "members": {
35
35
  "count": "%{smart_count} thành viên |||| %{smart_count} thành viên",
36
36
  "others": "và 1 người khác… |||| và %{smart_count} người khác…",
37
- "otherContacts": "người liên hệ khác |||| người liên hệ khác"
37
+ "otherContacts": "người liên hệ khác |||| người liên hệ khác",
38
+ "error": {
39
+ "changePermission": "Đã xảy ra lỗi khi thay đổi quyền. Vui lòng thử lại."
40
+ }
38
41
  },
39
42
  "contacts": {
40
43
  "whoHasAccess": "Ai có quyền truy cập?",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cozy-sharing",
3
- "version": "28.4.4",
3
+ "version": "28.6.0",
4
4
  "description": "Provides sharing login for React applications.",
5
5
  "main": "dist/index.js",
6
6
  "author": "Cozy",
@@ -54,7 +54,7 @@
54
54
  "babel-jest": "27.5.1",
55
55
  "babel-plugin-css-modules-transform": "1.6.2",
56
56
  "babel-plugin-inline-react-svg": "1.1.2",
57
- "cozy-client": "^60.19.0",
57
+ "cozy-client": "^60.22.0",
58
58
  "cozy-device-helper": "^3.7.1",
59
59
  "cozy-intent": "^2.30.3",
60
60
  "cozy-minilog": "^3.10.0",
@@ -69,7 +69,7 @@
69
69
  "twake-i18n": "^0.3.2"
70
70
  },
71
71
  "peerDependencies": {
72
- "cozy-client": ">=60.19.0",
72
+ "cozy-client": ">=60.22.0",
73
73
  "cozy-device-helper": ">=3.7.1",
74
74
  "cozy-intent": ">=2.29.1",
75
75
  "cozy-minilog": ">=3.9.1",
@@ -83,5 +83,5 @@
83
83
  "sideEffects": [
84
84
  "*.css"
85
85
  ],
86
- "gitHead": "3b9c5cf0b6442700d6a3b9d21010089f3f9f8b32"
86
+ "gitHead": "64311a9d08372224b5214cbf49f7d658d93f9af9"
87
87
  }
@@ -3,6 +3,7 @@ import React, { Component } from 'react'
3
3
  import { withClient } from 'cozy-client'
4
4
  import minilog from 'cozy-minilog'
5
5
 
6
+ import { generateShareLinkFromFile } from './components/ShareRestrictionModal/helpers'
6
7
  import SharingContext from './context'
7
8
  import { fetchNextPermissions } from './fetchNextPermissions'
8
9
  import { fetchFilesPaths } from './helpers/files'
@@ -11,6 +12,7 @@ import {
11
12
  createSharingInStore,
12
13
  updateSharingInStore
13
14
  } from './helpers/sharings'
15
+ import { getShortcode } from './helpers/shortcodes'
14
16
  import { SynchronousJobQueue } from './helpers/synchronousJobQueue'
15
17
  import { fetchApps } from './queries/queries'
16
18
  import reducer, {
@@ -67,6 +69,7 @@ export class SharingProvider extends Component {
67
69
  canReshare: docId => canReshare(this.state, docId, instanceUri),
68
70
  getOwner: docId => getOwner(this.state, docId),
69
71
  getSharingType: docId => getSharingType(this.state, docId, instanceUri),
72
+ getSharingById: docId => getSharingById(this.state, docId),
70
73
  getSharingForSelf: docId => getSharingForSelf(this.state, docId),
71
74
  getRecipients: docId => getRecipients(this.state, docId),
72
75
  getSharedParentPath: documentPath =>
@@ -83,6 +86,7 @@ export class SharingProvider extends Component {
83
86
  revokeGroup: this.revokeGroup,
84
87
  revokeSelf: this.revokeSelf,
85
88
  shareByLink: this.shareByLink,
89
+ getFederatedShareLink: this.getFederatedShareLink,
86
90
  updateDocumentPermissions: this.updateDocumentPermissions,
87
91
  revokeSharingLink: this.revokeSharingLink,
88
92
  hasLoadedAtLeastOnePage: false,
@@ -90,7 +94,8 @@ export class SharingProvider extends Component {
90
94
  revokeAllRecipients: this.revokeAllRecipients,
91
95
  refresh: this.fetchAllSharings,
92
96
  hasWriteAccess: this.hasWriteAccess,
93
- renameSharedDrive: this.renameSharedDrive
97
+ renameSharedDrive: this.renameSharedDrive,
98
+ updateSharingMemberType: this.updateSharingMemberType
94
99
  }
95
100
  this.isPublic = props.isPublic ?? false
96
101
  this.realtime = null
@@ -335,12 +340,116 @@ export class SharingProvider extends Component {
335
340
  this.dispatch(revokeSelf(sharing))
336
341
  }
337
342
 
343
+ /**
344
+ * Update a sharing member's read_only field to change permission type
345
+ * @param {string} sharingId - The ID of the sharing
346
+ * @param {number} memberIndex - The index of the member in the sharing members array
347
+ * @param {string} newType - 'one-way' for read-only, 'two-way' for read-write
348
+ */
349
+ updateSharingMemberType = async (sharingId, memberIndex, newType) => {
350
+ const sharing = getSharingById(this.state, sharingId)
351
+ if (!sharing) throw new Error('Sharing not found')
352
+
353
+ const member = sharing.attributes.members[memberIndex]
354
+ if (!member) throw new Error('Member not found')
355
+
356
+ // Find the contact by email (same pattern as helpers/contacts.js)
357
+ const matchedContacts = await this.props.client
358
+ .collection('io.cozy.contacts')
359
+ .find(
360
+ {
361
+ email: { $elemMatch: { address: member.email } },
362
+ _id: { $gt: null }
363
+ },
364
+ { indexedFields: ['_id'] }
365
+ )
366
+
367
+ if (!matchedContacts.data.length) {
368
+ throw new Error(`Contact not found for member ${member.email}`)
369
+ }
370
+ const contact = matchedContacts.data[0]
371
+
372
+ // Revoke (API call only, no state dispatch to avoid UI flickering)
373
+ await this.sharingCol.revokeRecipient(sharing, memberIndex)
374
+
375
+ // Re-add with the new permission type
376
+ const readOnly = newType === 'one-way'
377
+ try {
378
+ await this.addRecipients({
379
+ document: sharing,
380
+ recipients: readOnly ? [] : [contact],
381
+ readOnlyRecipients: readOnly ? [contact] : []
382
+ })
383
+ } catch (error) {
384
+ log.error(
385
+ `Failed to re-add member ${member.email} after revocation. The member has been revoked but could not be re-added with the new permission type.`,
386
+ error
387
+ )
388
+ throw error
389
+ }
390
+ }
391
+
338
392
  shareByLink = async (document, options) => {
339
- const resp = await this.permissionCol.createSharingLink(document, options)
393
+ const resp = document.driveId
394
+ ? await this.createSharedDriveSharingLink(document, options)
395
+ : await this.permissionCol.createSharingLink(document, options)
340
396
  this.dispatch(addSharingLink(resp.data))
341
397
  return resp
342
398
  }
343
399
 
400
+ getFederatedShareLink = document => {
401
+ if (!document.driveId) return null
402
+
403
+ const permissions = getDocumentPermissions(this.state, document._id)
404
+ const perm = permissions.find(p => getShortcode(p))
405
+ if (!perm) return null
406
+
407
+ const code = getShortcode(perm)
408
+ if (!code) return null
409
+
410
+ const { client } = this.props
411
+ return generateShareLinkFromFile({
412
+ client,
413
+ file: document,
414
+ sharecode: code,
415
+ getOwner: this.state.getOwner,
416
+ getSharingById: this.state.getSharingById,
417
+ documentType: this.state.documentType
418
+ })
419
+ }
420
+
421
+ createSharedDriveSharingLink = async (document, options) => {
422
+ const { client } = this.props
423
+ const drivePermissionCollection = client.collection('io.cozy.permissions', {
424
+ driveId: document.driveId
425
+ })
426
+ const resp = await drivePermissionCollection.createSharingLink(
427
+ document,
428
+ options
429
+ )
430
+ return resp
431
+ }
432
+
433
+ updateSharedDrivePermissions = async (
434
+ document,
435
+ permissionDocument,
436
+ updatedPermissions,
437
+ options
438
+ ) => {
439
+ const { client } = this.props
440
+ const { expiresAt, password } = options
441
+ const drivePermissionCollection = client.collection('io.cozy.permissions', {
442
+ driveId: document.driveId
443
+ })
444
+ const resp = await drivePermissionCollection.add(
445
+ permissionDocument,
446
+ updatedPermissions,
447
+ { expiresAt, password }
448
+ )
449
+ this.dispatch(updateSharingLink(resp.data))
450
+ return resp
451
+ }
452
+
344
453
  /**
345
454
  * updateDocumentPermissions - Description
346
455
  *
@@ -356,19 +465,29 @@ export class SharingProvider extends Component {
356
465
  const { verbs, expiresAt, password } = options
357
466
  const permissions = getDocumentPermissions(this.state, document.id)
358
467
 
468
+ if (!permissions || permissions.length === 0) {
469
+ return []
470
+ }
471
+
359
472
  const responses = await Promise.all(
360
473
  permissions.map(async permissionDocument => {
361
474
  const updatedPermissions = permissionDocument.attributes.permissions
362
- Object.keys(updatedPermissions).map(permType => {
475
+ Object.keys(updatedPermissions).forEach(permType => {
363
476
  updatedPermissions[permType].verbs = verbs
364
477
  })
365
478
 
366
- const resp = await this.permissionCol.add(
367
- permissionDocument,
368
- updatedPermissions,
369
- { expiresAt, password }
370
- )
371
- this.dispatch(updateSharingLink(resp.data))
479
+ const resp = document.driveId
480
+ ? await this.updateSharedDrivePermissions(
481
+ document,
482
+ permissionDocument,
483
+ updatedPermissions,
484
+ options
485
+ )
486
+ : await this.permissionCol.add(
487
+ permissionDocument,
488
+ updatedPermissions,
489
+ { expiresAt, password }
490
+ )
372
491
  return resp
373
492
  })
374
493
  )