cozy-sharing 18.0.0 → 19.0.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 (42) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/dist/components/Recipient/LinkRecipientPermissions.js +18 -129
  3. package/dist/components/Recipient/actions/editPermissionLink.js +71 -0
  4. package/dist/components/Recipient/actions/readOnlyPermissionLink.js +63 -0
  5. package/dist/components/ShareByLink.js +45 -185
  6. package/dist/components/ShareDialogCozyToCozy.js +2 -6
  7. package/dist/components/ShareDialogOnlyByLink.js +2 -8
  8. package/dist/components/ShareDialogTwoStepsConfirmationContainer.js +1 -3
  9. package/dist/components/ShareModal/EditableSharingModal.js +1 -7
  10. package/dist/components/ShareModal.js +0 -12
  11. package/dist/components/ShareRestrictionModal/BoxDate.js +77 -0
  12. package/dist/components/ShareRestrictionModal/BoxEditingRights.js +83 -0
  13. package/dist/components/ShareRestrictionModal/BoxPassword.js +138 -0
  14. package/dist/components/ShareRestrictionModal/ShareRestrictionContentModal.js +94 -0
  15. package/dist/components/ShareRestrictionModal/ShareRestrictionModal.js +260 -0
  16. package/dist/components/ShareRestrictionModal/helpers.js +275 -0
  17. package/dist/components/ShareRestrictionModal/helpers.spec.js +110 -0
  18. package/dist/components/WhoHasAccess.js +1 -5
  19. package/locales/en.json +28 -0
  20. package/locales/fr.json +29 -0
  21. package/package.json +11 -7
  22. package/src/components/Recipient/LinkRecipientPermissions.jsx +28 -137
  23. package/src/components/Recipient/actions/editPermissionLink.js +63 -0
  24. package/src/components/Recipient/actions/readOnlyPermissionLink.js +59 -0
  25. package/src/components/ShareByLink.jsx +20 -96
  26. package/src/components/ShareDialogCozyToCozy.jsx +1 -5
  27. package/src/components/ShareDialogOnlyByLink.jsx +0 -6
  28. package/src/components/ShareDialogTwoStepsConfirmationContainer.jsx +0 -2
  29. package/src/components/ShareModal/EditableSharingModal.jsx +1 -7
  30. package/src/components/ShareModal.jsx +0 -12
  31. package/src/components/ShareRestrictionModal/BoxDate.jsx +76 -0
  32. package/src/components/ShareRestrictionModal/BoxEditingRights.jsx +91 -0
  33. package/src/components/ShareRestrictionModal/BoxPassword.jsx +112 -0
  34. package/src/components/ShareRestrictionModal/ShareRestrictionContentModal.jsx +102 -0
  35. package/src/components/ShareRestrictionModal/ShareRestrictionModal.jsx +177 -0
  36. package/src/components/ShareRestrictionModal/helpers.js +193 -0
  37. package/src/components/ShareRestrictionModal/helpers.spec.js +78 -0
  38. package/src/components/WhoHasAccess.jsx +1 -5
  39. package/dist/components/EditLinkPermissionDialog.js +0 -72
  40. package/dist/components/EditLinkPermissionDialog.spec.js +0 -48
  41. package/src/components/EditLinkPermissionDialog.jsx +0 -89
  42. package/src/components/EditLinkPermissionDialog.spec.jsx +0 -53
@@ -0,0 +1,193 @@
1
+ import { getSharingLink } from 'cozy-client/dist/models/sharing'
2
+ import minilog from 'cozy-minilog'
3
+
4
+ const log = minilog('ShareRestrictionModal/helpers')
5
+
6
+ export const WRITE_PERMS = ['GET', 'POST', 'PUT', 'PATCH']
7
+ export const READ_ONLY_PERMS = ['GET']
8
+
9
+ /**
10
+ * Copy a string to the clipboard
11
+ *
12
+ * @param {string} value - The string saved to the clipboard
13
+ * @param {object} [options]
14
+ * @param {number} options.t - Translation function
15
+ * @param {Function} options.showAlert - Function to display an alert
16
+ */
17
+ export const copyToClipboard = async (value, { t, showAlert } = {}) => {
18
+ if (!value) return false
19
+ try {
20
+ await navigator.clipboard.writeText(value)
21
+ showAlert({
22
+ message: t('copyReminderContent.success'),
23
+ severity: 'success',
24
+ variant: 'filled'
25
+ })
26
+ } catch (error) {
27
+ showAlert({
28
+ message: t('copyReminderContent.error'),
29
+ severity: 'error',
30
+ variant: 'filled'
31
+ })
32
+ log.error(
33
+ "Error in 'copyToClipboard' function when trying to copy to clipboard",
34
+ error
35
+ )
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Make a TTL string from a date
41
+ * @param {Date|string} selectedDate
42
+ * @returns {string} - TTL string in seconds (e.g. '1234s')
43
+ */
44
+ export const makeTTL = selectedDate => {
45
+ if (!selectedDate) return
46
+ try {
47
+ let selectedDateConverted = selectedDate
48
+ if (typeof selectedDate === 'string') {
49
+ selectedDateConverted = new Date(selectedDate)
50
+ if (selectedDateConverted?.toString() === 'Invalid Date') {
51
+ throw new Error(`Invalid date: ${selectedDate}`)
52
+ }
53
+ }
54
+ if (selectedDateConverted instanceof Date) {
55
+ const now = new Date()
56
+ const ttl =
57
+ selectedDateConverted > now
58
+ ? `${Math.round((selectedDateConverted - now) / 1000)}s`
59
+ : undefined
60
+ return ttl
61
+ }
62
+ throw new Error(`Invalid date: ${selectedDate}`)
63
+ } catch (error) {
64
+ log.error(error)
65
+ return
66
+ }
67
+ }
68
+
69
+ /**
70
+ * forwardFile - Triggers the download of one or multiple files by the browser
71
+ * @param {object} options
72
+ * @param {import('cozy-client/types/CozyClient').default} options.client
73
+ * @param {import('cozy-client/types/types').IOCozyFile} options.file File to download
74
+ * @param {Function} options.t i18n function
75
+ * @param {string} options.ttl Time to live of the sharing link
76
+ * @param {string} options.password Password of the sharing link
77
+ * @param {Function} options.showAlert - Function to display an alert
78
+ */
79
+ export const forwardFile = async ({
80
+ client,
81
+ file,
82
+ t,
83
+ ttl,
84
+ password,
85
+ showAlert
86
+ }) => {
87
+ try {
88
+ // We currently support only one file at a time
89
+ const url = await getSharingLink(client, [file._id], { ttl, password })
90
+ const isZipFile = file.class === 'zip'
91
+ const shareData = {
92
+ title: t('shareFile.title', {
93
+ name: file.name,
94
+ smart_count: isZipFile ? 2 : 1
95
+ }),
96
+ text: t('shareFile.text', {
97
+ name: file.name,
98
+ smart_count: isZipFile ? 2 : 1
99
+ }),
100
+ url
101
+ }
102
+ navigator.share(shareData)
103
+ } catch (error) {
104
+ showAlert({
105
+ message: t('shareFile.error'),
106
+ severity: 'error',
107
+ variant: 'filled'
108
+ })
109
+ }
110
+ }
111
+
112
+ /**
113
+ * updatePermissions - Updates the permissions of a file
114
+ * @param {object} options
115
+ * @param {import('cozy-client/types/types').IOCozyFile} options.file File to update permissions
116
+ * @param {Function} options.t i18n function
117
+ * @param {string} options.documentType - Type of the document
118
+ * @param {'readOnly'|'write'} options.editingRights - Editing rights
119
+ * @param {Function} options.updateDocumentPermissions - Function to update permissions
120
+ * @param {Function} options.showAlert - Function to display an alert
121
+ */
122
+ export const updatePermissions = async ({
123
+ file,
124
+ t,
125
+ editingRights,
126
+ documentType,
127
+ updateDocumentPermissions,
128
+ showAlert
129
+ }) => {
130
+ switch (editingRights) {
131
+ case 'readOnly':
132
+ try {
133
+ return updateDocumentPermissions(file, READ_ONLY_PERMS)
134
+ } catch (err) {
135
+ showAlert({
136
+ message: t(`${documentType}.share.shareByLink.permserror`),
137
+ severity: 'error',
138
+ variant: 'filled'
139
+ })
140
+ log.error(
141
+ "Error in 'readOnlyPermissionLink' function when trying to change permission",
142
+ err
143
+ )
144
+ }
145
+ break
146
+ case 'write':
147
+ try {
148
+ return updateDocumentPermissions(file, WRITE_PERMS)
149
+ } catch (err) {
150
+ showAlert({
151
+ message: t(`${documentType}.share.shareByLink.permserror`),
152
+ severity: 'error',
153
+ variant: 'filled'
154
+ })
155
+ log.error(
156
+ "Error in 'editPermissionLink' function when trying to change permission",
157
+ err
158
+ )
159
+ }
160
+ break
161
+ }
162
+ }
163
+
164
+ /**
165
+ * revokePermissions - Revokes the permissions of a file
166
+ * @param {object} options
167
+ * @param {import('cozy-client/types/types').IOCozyFile} options.file File to revoke permissions
168
+ * @param {Function} options.t i18n function
169
+ * @param {string} options.documentType - Type of the document
170
+ * @param {Function} options.revokeSharingLink - Function to revoke permissions
171
+ * @param {Function} options.showAlert - Function to display an alert
172
+ */
173
+ export const revokePermissions = async ({
174
+ file,
175
+ t,
176
+ documentType,
177
+ revokeSharingLink,
178
+ showAlert
179
+ }) => {
180
+ try {
181
+ return revokeSharingLink(file)
182
+ } catch (err) {
183
+ showAlert({
184
+ message: t(`${documentType}.share.error.revoke`),
185
+ severity: 'error',
186
+ variant: 'filled'
187
+ })
188
+ log.error(
189
+ "Error in 'revokePermissions' function when trying to revoke sharing link",
190
+ err
191
+ )
192
+ }
193
+ }
@@ -0,0 +1,78 @@
1
+ import { makeTTL, revokePermissions, updatePermissions } from './helpers'
2
+
3
+ describe('ShareRestrictionModal/helpers', () => {
4
+ describe('makeTTL', () => {
5
+ it('sould return undefined', () => {
6
+ expect(makeTTL()).toBeUndefined()
7
+ expect(makeTTL(123)).toBeUndefined()
8
+ expect(makeTTL('abc')).toBeUndefined()
9
+ expect(makeTTL(new Date('2023-01-01T00:00:00.000Z'))).toBeUndefined()
10
+ expect(makeTTL(new Date('abc'))).toBeUndefined()
11
+ expect(makeTTL('2023-01-01T00:00:00.000Z')).toBeUndefined()
12
+ })
13
+ it('sould return TTL in seconds', () => {
14
+ expect(makeTTL(new Date('2100-01-01T00:00:00.000Z'))).toBeDefined()
15
+ expect(makeTTL('2100-01-01T00:00:00.000Z')).toBeDefined()
16
+ })
17
+ })
18
+
19
+ describe('updatePermissions', () => {
20
+ const documentType = 'Files'
21
+ it('should call updateDocumentPermissions with "readOnly" permission', async () => {
22
+ const updateDocumentPermissions = jest.fn()
23
+ const file = { _id: '123' }
24
+ const editingRights = 'readOnly'
25
+
26
+ await updatePermissions({
27
+ file,
28
+ t: jest.fn(),
29
+ editingRights,
30
+ documentType,
31
+ updateDocumentPermissions,
32
+ showAlert: jest.fn()
33
+ })
34
+
35
+ expect(updateDocumentPermissions).toHaveBeenCalledWith(file, ['GET'])
36
+ })
37
+
38
+ it('should call updateDocumentPermissions with "write" permissions', async () => {
39
+ const updateDocumentPermissions = jest.fn()
40
+ const file = { _id: '123' }
41
+ const editingRights = 'write'
42
+
43
+ await updatePermissions({
44
+ file,
45
+ t: jest.fn(),
46
+ editingRights,
47
+ documentType,
48
+ updateDocumentPermissions,
49
+ showAlert: jest.fn()
50
+ })
51
+
52
+ expect(updateDocumentPermissions).toHaveBeenCalledWith(file, [
53
+ 'GET',
54
+ 'POST',
55
+ 'PUT',
56
+ 'PATCH'
57
+ ])
58
+ })
59
+ })
60
+
61
+ describe('revokePermissions', () => {
62
+ const documentType = 'Files'
63
+ it('should call revokeSharingLink', async () => {
64
+ const revokeSharingLink = jest.fn()
65
+ const file = { _id: '123' }
66
+
67
+ await revokePermissions({
68
+ file,
69
+ t: jest.fn(),
70
+ documentType,
71
+ revokeSharingLink,
72
+ showAlert: jest.fn()
73
+ })
74
+
75
+ expect(revokeSharingLink).toHaveBeenCalledWith(file)
76
+ })
77
+ })
78
+ })
@@ -37,9 +37,7 @@ const WhoHasAccess = ({
37
37
  onRevokeSelf,
38
38
  verifyRecipient,
39
39
  link,
40
- permissions,
41
- onUpdateShareLinkPermissions,
42
- onRevokeLink
40
+ permissions
43
41
  }) => {
44
42
  const previousLink = usePrevious(link)
45
43
  const linkHasBeenJustCreated = link && previousLink === null
@@ -58,8 +56,6 @@ const WhoHasAccess = ({
58
56
  onRevokeSelf={onRevokeSelf}
59
57
  link={link}
60
58
  permissions={permissions}
61
- onChangePermissions={onUpdateShareLinkPermissions}
62
- onDisable={onRevokeLink}
63
59
  fadeIn={linkHasBeenJustCreated}
64
60
  />
65
61
  )}
@@ -1,72 +0,0 @@
1
- import _slicedToArray from "@babel/runtime/helpers/slicedToArray";
2
- import PropTypes from 'prop-types';
3
- import React, { useState } from 'react';
4
- import Button from 'cozy-ui/transpiled/react/Buttons';
5
- import { ConfirmDialog } from 'cozy-ui/transpiled/react/CozyDialogs';
6
- import FormControlLabel from 'cozy-ui/transpiled/react/FormControlLabel';
7
- import RadioGroup from 'cozy-ui/transpiled/react/RadioGroup';
8
- import Radio from 'cozy-ui/transpiled/react/Radios';
9
- import { useI18n } from 'cozy-ui/transpiled/react/providers/I18n';
10
-
11
- var EditLinkPermissionDialog = function EditLinkPermissionDialog(_ref) {
12
- var open = _ref.open,
13
- onClose = _ref.onClose,
14
- document = _ref.document,
15
- onPermissionsSelected = _ref.onPermissionsSelected;
16
-
17
- var _useI18n = useI18n(),
18
- t = _useI18n.t;
19
-
20
- var _useState = useState(true),
21
- _useState2 = _slicedToArray(_useState, 2),
22
- isReadOnlyPermissions = _useState2[0],
23
- setIsReadOnlyPermissions = _useState2[1];
24
-
25
- var onChange = function onChange(event) {
26
- setIsReadOnlyPermissions(event.target.value === 'true');
27
- };
28
-
29
- var onConfirm = function onConfirm() {
30
- var verbs = isReadOnlyPermissions ? ['GET'] : ['GET', 'POST', 'PUT', 'PATCH'];
31
- onPermissionsSelected({
32
- verbs: verbs
33
- });
34
- onClose();
35
- };
36
-
37
- return /*#__PURE__*/React.createElement(ConfirmDialog, {
38
- open: open,
39
- onClose: onClose,
40
- title: t('Share.permissionLink.title'),
41
- content: /*#__PURE__*/React.createElement(RadioGroup, {
42
- "aria-label": "radio",
43
- value: isReadOnlyPermissions.toString(),
44
- onChange: onChange
45
- }, /*#__PURE__*/React.createElement(FormControlLabel, {
46
- value: "true",
47
- label: (document === null || document === void 0 ? void 0 : document.type) === 'directory' ? t("Share.permissionLink.seeFolder") : t("Share.permissionLink.seeFile"),
48
- control: /*#__PURE__*/React.createElement(Radio, null)
49
- }), /*#__PURE__*/React.createElement(FormControlLabel, {
50
- value: "false",
51
- label: (document === null || document === void 0 ? void 0 : document.type) === 'directory' ? t("Share.permissionLink.modifyFolder") : t("Share.permissionLink.modifyFile"),
52
- control: /*#__PURE__*/React.createElement(Radio, null)
53
- })),
54
- actions: /*#__PURE__*/React.createElement(React.Fragment, null, /*#__PURE__*/React.createElement(Button, {
55
- variant: "secondary",
56
- label: t('Share.permissionLink.cancel'),
57
- onClick: onClose
58
- }), /*#__PURE__*/React.createElement(Button, {
59
- variant: "primary",
60
- label: t('Share.permissionLink.confirm'),
61
- onClick: onConfirm
62
- }))
63
- });
64
- };
65
-
66
- EditLinkPermissionDialog.propTypes = {
67
- open: PropTypes.bool.isRequired,
68
- onClose: PropTypes.func.isRequired,
69
- document: PropTypes.object.isRequired,
70
- onPermissionsSelected: PropTypes.func.isRequired
71
- };
72
- export default EditLinkPermissionDialog;
@@ -1,48 +0,0 @@
1
- import _extends from "@babel/runtime/helpers/extends";
2
- import { render, fireEvent } from '@testing-library/react';
3
- import React from 'react';
4
- import { createMockClient } from 'cozy-client';
5
- import EditLinkPermissionDialog from './EditLinkPermissionDialog';
6
- import AppLike from '../../test/AppLike';
7
- describe('EditLinkPermissionDialog', function () {
8
- var client = createMockClient({});
9
-
10
- var setup = function setup(props) {
11
- return render( /*#__PURE__*/React.createElement(AppLike, {
12
- client: client
13
- }, /*#__PURE__*/React.createElement(EditLinkPermissionDialog, _extends({
14
- open: true,
15
- onClose: function onClose() {}
16
- }, props))));
17
- };
18
-
19
- it('should select read permissions', function () {
20
- var onPermissionsSelected = jest.fn();
21
- var props = {
22
- onPermissionsSelected: onPermissionsSelected
23
- };
24
-
25
- var _setup = setup(props),
26
- getByText = _setup.getByText;
27
-
28
- fireEvent.click(getByText('OK'));
29
- expect(onPermissionsSelected).toHaveBeenCalledWith({
30
- verbs: ['GET']
31
- });
32
- });
33
- it('should select write permissions', function () {
34
- var onPermissionsSelected = jest.fn();
35
- var props = {
36
- onPermissionsSelected: onPermissionsSelected
37
- };
38
-
39
- var _setup2 = setup(props),
40
- getByText = _setup2.getByText;
41
-
42
- fireEvent.click(getByText('Modify file'));
43
- fireEvent.click(getByText('OK'));
44
- expect(onPermissionsSelected).toHaveBeenCalledWith({
45
- verbs: ['GET', 'POST', 'PUT', 'PATCH']
46
- });
47
- });
48
- });
@@ -1,89 +0,0 @@
1
- import PropTypes from 'prop-types'
2
- import React, { useState } from 'react'
3
-
4
- import Button from 'cozy-ui/transpiled/react/Buttons'
5
- import { ConfirmDialog } from 'cozy-ui/transpiled/react/CozyDialogs'
6
- import FormControlLabel from 'cozy-ui/transpiled/react/FormControlLabel'
7
- import RadioGroup from 'cozy-ui/transpiled/react/RadioGroup'
8
- import Radio from 'cozy-ui/transpiled/react/Radios'
9
- import { useI18n } from 'cozy-ui/transpiled/react/providers/I18n'
10
-
11
- const EditLinkPermissionDialog = ({
12
- open,
13
- onClose,
14
- document,
15
- onPermissionsSelected
16
- }) => {
17
- const { t } = useI18n()
18
-
19
- const [isReadOnlyPermissions, setIsReadOnlyPermissions] = useState(true)
20
-
21
- const onChange = event => {
22
- setIsReadOnlyPermissions(event.target.value === 'true')
23
- }
24
-
25
- const onConfirm = () => {
26
- const verbs = isReadOnlyPermissions
27
- ? ['GET']
28
- : ['GET', 'POST', 'PUT', 'PATCH']
29
- onPermissionsSelected({ verbs })
30
- onClose()
31
- }
32
-
33
- return (
34
- <ConfirmDialog
35
- open={open}
36
- onClose={onClose}
37
- title={t('Share.permissionLink.title')}
38
- content={
39
- <RadioGroup
40
- aria-label="radio"
41
- value={isReadOnlyPermissions.toString()}
42
- onChange={onChange}
43
- >
44
- <FormControlLabel
45
- value="true"
46
- label={
47
- document?.type === 'directory'
48
- ? t(`Share.permissionLink.seeFolder`)
49
- : t(`Share.permissionLink.seeFile`)
50
- }
51
- control={<Radio />}
52
- />
53
- <FormControlLabel
54
- value="false"
55
- label={
56
- document?.type === 'directory'
57
- ? t(`Share.permissionLink.modifyFolder`)
58
- : t(`Share.permissionLink.modifyFile`)
59
- }
60
- control={<Radio />}
61
- />
62
- </RadioGroup>
63
- }
64
- actions={
65
- <>
66
- <Button
67
- variant="secondary"
68
- label={t('Share.permissionLink.cancel')}
69
- onClick={onClose}
70
- />
71
- <Button
72
- variant="primary"
73
- label={t('Share.permissionLink.confirm')}
74
- onClick={onConfirm}
75
- />
76
- </>
77
- }
78
- />
79
- )
80
- }
81
-
82
- EditLinkPermissionDialog.propTypes = {
83
- open: PropTypes.bool.isRequired,
84
- onClose: PropTypes.func.isRequired,
85
- document: PropTypes.object.isRequired,
86
- onPermissionsSelected: PropTypes.func.isRequired
87
- }
88
-
89
- export default EditLinkPermissionDialog
@@ -1,53 +0,0 @@
1
- import { render, fireEvent } from '@testing-library/react'
2
- import React from 'react'
3
-
4
- import { createMockClient } from 'cozy-client'
5
-
6
- import EditLinkPermissionDialog from './EditLinkPermissionDialog'
7
- import AppLike from '../../test/AppLike'
8
-
9
- describe('EditLinkPermissionDialog', () => {
10
- const client = createMockClient({})
11
-
12
- const setup = props => {
13
- return render(
14
- <AppLike client={client}>
15
- <EditLinkPermissionDialog open onClose={() => {}} {...props} />
16
- </AppLike>
17
- )
18
- }
19
-
20
- it('should select read permissions', () => {
21
- const onPermissionsSelected = jest.fn()
22
-
23
- const props = {
24
- onPermissionsSelected
25
- }
26
-
27
- const { getByText } = setup(props)
28
-
29
- fireEvent.click(getByText('OK'))
30
-
31
- expect(onPermissionsSelected).toHaveBeenCalledWith({
32
- verbs: ['GET']
33
- })
34
- })
35
-
36
- it('should select write permissions', () => {
37
- const onPermissionsSelected = jest.fn()
38
-
39
- const props = {
40
- onPermissionsSelected
41
- }
42
-
43
- const { getByText } = setup(props)
44
-
45
- fireEvent.click(getByText('Modify file'))
46
-
47
- fireEvent.click(getByText('OK'))
48
-
49
- expect(onPermissionsSelected).toHaveBeenCalledWith({
50
- verbs: ['GET', 'POST', 'PUT', 'PATCH']
51
- })
52
- })
53
- })