cozy-sharing 40.1.6 → 40.1.7
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.
- package/CHANGELOG.md +4 -0
- package/dist/components/FederatedFolder/FederatedFolderModal.js +10 -12
- package/dist/components/Recipient/DowngradePermissionConfirmDialog.js +3 -12
- package/dist/components/Recipient/GroupRecipient.js +1 -4
- package/dist/components/Recipient/GroupRecipient.stories.js +3 -1
- package/dist/components/Recipient/GroupRecipientDetailWithAccess.js +1 -4
- package/dist/components/Recipient/GroupRecipientPermissions.js +5 -8
- package/dist/components/Recipient/GroupRecipientPermissions.stories.js +2 -0
- package/dist/components/Recipient/LinkRecipient.js +2 -6
- package/dist/components/Recipient/LinkRecipient.stories.js +4 -0
- package/dist/components/Recipient/LinkRecipientPermissions.js +13 -12
- package/dist/components/Recipient/LinkRecipientPermissions.stories.js +5 -1
- package/dist/components/Recipient/MemberRecipient.js +7 -6
- package/dist/components/Recipient/MemberRecipient.spec.js +7 -12
- package/dist/components/Recipient/MemberRecipientPermissions.js +28 -13
- package/dist/components/Recipient/MemberRecipientPermissions.spec.js +5 -14
- package/dist/components/Recipient/MemberRecipientPermissions.stories.js +2 -1
- package/dist/components/Recipient/OwnerRecipient.js +4 -17
- package/dist/components/Recipient/OwnerRecipient.stories.js +3 -1
- package/dist/components/Recipient/OwnerRecipientDefaultLite.js +1 -1
- package/dist/components/Recipient/PermissionTypeMenu.js +32 -155
- package/dist/components/Recipient/PermissionTypeMenuWrapper.js +37 -0
- package/dist/components/Recipient/{PermissionTypeMenu.spec.js → PermissionTypeMenuWrapper.spec.js} +2 -2
- package/dist/components/Recipient/RecipientList.js +10 -0
- package/dist/components/Recipient/RecipientList.stories.js +3 -0
- package/dist/components/ShareDialogCozyToCozy.js +20 -19
- package/dist/components/ShareDialogCozyToCozy.spec.js +3 -0
- package/dist/components/ShareDialogOnlyByLink.js +3 -5
- package/dist/components/ShareDialogOnlyByLink.spec.js +0 -1
- package/dist/components/ShareModal/SharingDetailsModal.js +2 -2
- package/dist/components/SharedDrive/EditSharedDriveModal.js +11 -10
- package/dist/components/SharedDrive/useSharedDrive.js +8 -11
- package/dist/components/SharingContent.js +3 -5
- package/dist/components/WhoHasAccess.js +27 -11
- package/dist/components/WhoHasAccessWrapper.js +122 -0
- package/dist/helpers/recipients.js +46 -0
- package/dist/helpers/recipients.spec.js +89 -0
- package/dist/hooks/useMemberPermissionType.js +191 -0
- package/dist/hooks/useResolveRecipients.js +53 -0
- package/dist/hooks/useResolveRecipients.spec.js +140 -0
- package/dist/index.js +1 -1
- package/package.json +3 -3
- package/src/components/FederatedFolder/FederatedFolderModal.jsx +6 -12
- package/src/components/Recipient/DowngradePermissionConfirmDialog.jsx +3 -10
- package/src/components/Recipient/GroupRecipient.jsx +1 -5
- package/src/components/Recipient/GroupRecipient.stories.jsx +3 -1
- package/src/components/Recipient/GroupRecipientDetailWithAccess.jsx +1 -4
- package/src/components/Recipient/GroupRecipientPermissions.jsx +5 -5
- package/src/components/Recipient/GroupRecipientPermissions.stories.jsx +2 -0
- package/src/components/Recipient/LinkRecipient.jsx +2 -4
- package/src/components/Recipient/LinkRecipient.stories.jsx +4 -0
- package/src/components/Recipient/LinkRecipientPermissions.jsx +16 -12
- package/src/components/Recipient/LinkRecipientPermissions.stories.jsx +5 -1
- package/src/components/Recipient/MemberRecipient.jsx +4 -4
- package/src/components/Recipient/MemberRecipient.spec.jsx +7 -10
- package/src/components/Recipient/MemberRecipientPermissions.jsx +29 -13
- package/src/components/Recipient/MemberRecipientPermissions.spec.jsx +5 -11
- package/src/components/Recipient/MemberRecipientPermissions.stories.jsx +2 -1
- package/src/components/Recipient/OwnerRecipient.jsx +4 -17
- package/src/components/Recipient/OwnerRecipient.stories.jsx +1 -1
- package/src/components/Recipient/OwnerRecipientDefaultLite.jsx +1 -1
- package/src/components/Recipient/PermissionTypeMenu.jsx +21 -81
- package/src/components/Recipient/PermissionTypeMenuWrapper.jsx +32 -0
- package/src/components/Recipient/{PermissionTypeMenu.spec.jsx → PermissionTypeMenuWrapper.spec.jsx} +2 -2
- package/src/components/Recipient/RecipientList.jsx +10 -0
- package/src/components/Recipient/RecipientList.stories.jsx +3 -0
- package/src/components/ShareDialogCozyToCozy.jsx +10 -11
- package/src/components/ShareDialogCozyToCozy.spec.jsx +1 -0
- package/src/components/ShareDialogOnlyByLink.jsx +2 -4
- package/src/components/ShareDialogOnlyByLink.spec.jsx +0 -1
- package/src/components/ShareModal/SharingDetailsModal.jsx +2 -2
- package/src/components/SharedDrive/EditSharedDriveModal.jsx +6 -11
- package/src/components/SharedDrive/useSharedDrive.js +4 -11
- package/src/components/SharingContent.jsx +2 -4
- package/src/components/WhoHasAccess.jsx +26 -12
- package/src/components/WhoHasAccessWrapper.jsx +117 -0
- package/src/helpers/recipients.js +44 -0
- package/src/helpers/recipients.spec.js +82 -0
- package/src/hooks/useMemberPermissionType.js +110 -0
- package/src/hooks/useResolveRecipients.js +38 -0
- package/src/hooks/useResolveRecipients.spec.js +70 -0
- package/src/index.jsx +1 -1
- package/dist/components/Recipient/OwnerRecipientDefault.js +0 -27
- package/dist/components/Recipient/OwnerRecipientDefault.stories.js +0 -10
- package/src/components/Recipient/OwnerRecipientDefault.jsx +0 -37
- package/src/components/Recipient/OwnerRecipientDefault.stories.jsx +0 -13
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import PropTypes from 'prop-types'
|
|
2
2
|
import React, { useCallback, useMemo, useState } from 'react'
|
|
3
3
|
|
|
4
|
-
import { useClient } from 'cozy-client'
|
|
5
4
|
import Button from 'cozy-ui/transpiled/react/Buttons'
|
|
6
5
|
import { useAlert } from 'cozy-ui/transpiled/react/providers/Alert'
|
|
7
6
|
import { useI18n } from 'twake-i18n'
|
|
@@ -10,10 +9,10 @@ import { default as DumbShareByLink } from './ShareByLink'
|
|
|
10
9
|
import ShareDialogTwoStepsConfirmationContainer from './ShareDialogTwoStepsConfirmationContainer'
|
|
11
10
|
import { ShareRecipientsLimitModal } from './ShareRecipientsLimitModal'
|
|
12
11
|
import SharingContent from './SharingContent'
|
|
13
|
-
import { getOrCreateFromArray } from '../helpers/contacts'
|
|
14
12
|
import { hasReachRecipientsLimit } from '../helpers/recipients'
|
|
15
13
|
import { getErrorMessage, getSuccessMessage } from '../helpers/share'
|
|
16
14
|
import { usePendingRecipients } from '../hooks/usePendingRecipients'
|
|
15
|
+
import { useResolveRecipients } from '../hooks/useResolveRecipients'
|
|
17
16
|
import { useSharingContext } from '../hooks/useSharingContext'
|
|
18
17
|
|
|
19
18
|
const SharingTitleFunction = ({ documentType, document }) =>
|
|
@@ -39,7 +38,7 @@ const ShareDialogCozyToCozy = ({
|
|
|
39
38
|
...props
|
|
40
39
|
}) => {
|
|
41
40
|
const { t } = useI18n()
|
|
42
|
-
const
|
|
41
|
+
const resolveRecipients = useResolveRecipients()
|
|
43
42
|
const { showAlert } = useAlert()
|
|
44
43
|
const { getSharingLink } = useSharingContext()
|
|
45
44
|
|
|
@@ -67,13 +66,13 @@ const ShareDialogCozyToCozy = ({
|
|
|
67
66
|
|
|
68
67
|
setSubmitting(true)
|
|
69
68
|
try {
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const
|
|
69
|
+
const { recipients: readWriteRecipients, readOnlyRecipients } =
|
|
70
|
+
await resolveRecipients(
|
|
71
|
+
pendingRecipients,
|
|
72
|
+
selectedOption,
|
|
73
|
+
createContact
|
|
74
|
+
)
|
|
75
|
+
const contacts = [...readWriteRecipients, ...readOnlyRecipients]
|
|
77
76
|
|
|
78
77
|
await onShare({
|
|
79
78
|
document,
|
|
@@ -109,7 +108,7 @@ const ShareDialogCozyToCozy = ({
|
|
|
109
108
|
}, [
|
|
110
109
|
pendingRecipients,
|
|
111
110
|
selectedOption,
|
|
112
|
-
|
|
111
|
+
resolveRecipients,
|
|
113
112
|
showAlert,
|
|
114
113
|
t,
|
|
115
114
|
recipients,
|
|
@@ -17,6 +17,7 @@ jest.mock('cozy-client', () => ({
|
|
|
17
17
|
jest.mock('../hooks/useSharingContext', () => ({
|
|
18
18
|
useSharingContext: () => ({
|
|
19
19
|
getSharingLink: jest.fn(() => null),
|
|
20
|
+
getDocumentPermissions: jest.fn(() => []),
|
|
20
21
|
updateSharingMemberType: jest.fn(),
|
|
21
22
|
isOrgSharedDrive: jest.fn(() => false)
|
|
22
23
|
})
|
|
@@ -8,7 +8,7 @@ import { useI18n } from 'twake-i18n'
|
|
|
8
8
|
|
|
9
9
|
import AntivirusAlert from './AntivirusAlert'
|
|
10
10
|
import { default as DumbShareByLink } from './ShareByLink'
|
|
11
|
-
import
|
|
11
|
+
import WhoHasAccessWrapper from './WhoHasAccessWrapper'
|
|
12
12
|
import { useSharingContext } from '../hooks/useSharingContext'
|
|
13
13
|
|
|
14
14
|
const ShareDialogOnlyByLink = ({
|
|
@@ -19,7 +19,6 @@ const ShareDialogOnlyByLink = ({
|
|
|
19
19
|
document,
|
|
20
20
|
documentType,
|
|
21
21
|
onClose,
|
|
22
|
-
permissions,
|
|
23
22
|
showGenerateLinkButton,
|
|
24
23
|
autoOpenShareRestriction
|
|
25
24
|
}) => {
|
|
@@ -46,7 +45,7 @@ const ShareDialogOnlyByLink = ({
|
|
|
46
45
|
>
|
|
47
46
|
{t('Share.contacts.whoHasAccess')}
|
|
48
47
|
</Typography>
|
|
49
|
-
<
|
|
48
|
+
<WhoHasAccessWrapper
|
|
50
49
|
document={document}
|
|
51
50
|
documentType={documentType}
|
|
52
51
|
isOwner={isOwner}
|
|
@@ -54,7 +53,6 @@ const ShareDialogOnlyByLink = ({
|
|
|
54
53
|
onRevokeSelf={onRevokeSelf}
|
|
55
54
|
recipients={recipients}
|
|
56
55
|
link={displayedLink}
|
|
57
|
-
permissions={permissions}
|
|
58
56
|
/>
|
|
59
57
|
<div
|
|
60
58
|
className={cx(
|
|
@@ -7,7 +7,7 @@ import { useI18n } from 'twake-i18n'
|
|
|
7
7
|
import { useSharingContext } from '../../hooks/useSharingContext'
|
|
8
8
|
import styles from '../../styles/share.styl'
|
|
9
9
|
import ShareByLink from '../ShareByLink'
|
|
10
|
-
import
|
|
10
|
+
import WhoHasAccessWrapper from '../WhoHasAccessWrapper'
|
|
11
11
|
|
|
12
12
|
export const SharingDetailsModal = ({
|
|
13
13
|
recipients,
|
|
@@ -34,7 +34,7 @@ export const SharingDetailsModal = ({
|
|
|
34
34
|
title={t(`${documentType}.share.details.title`)}
|
|
35
35
|
content={
|
|
36
36
|
<div className={styles['share-modal-content']}>
|
|
37
|
-
<
|
|
37
|
+
<WhoHasAccessWrapper
|
|
38
38
|
canManageMembers={false}
|
|
39
39
|
canManageLink={isSharedDrive}
|
|
40
40
|
isSharedDrive={isSharedDrive}
|
|
@@ -10,13 +10,13 @@ import { useSubmitWithLoader } from 'cozy-ui/transpiled/react/hooks/useSubmitWit
|
|
|
10
10
|
import { useI18n } from 'twake-i18n'
|
|
11
11
|
|
|
12
12
|
import SharedDriveHeader from './SharedDriveHeader'
|
|
13
|
-
import { getOrCreateFromArray } from '../../helpers/contacts'
|
|
14
13
|
import withLocales from '../../hoc/withLocales'
|
|
15
14
|
import { usePendingRecipients } from '../../hooks/usePendingRecipients'
|
|
15
|
+
import { useResolveRecipients } from '../../hooks/useResolveRecipients'
|
|
16
16
|
import { useSharingContext } from '../../hooks/useSharingContext'
|
|
17
17
|
import styles from '../../styles/share.styl'
|
|
18
18
|
import { default as DumbShareByEmail } from '../ShareByEmail'
|
|
19
|
-
import
|
|
19
|
+
import WhoHasAccessWrapper from '../WhoHasAccessWrapper'
|
|
20
20
|
|
|
21
21
|
export const EditSharedDriveModal = withLocales(({ onClose, document }) => {
|
|
22
22
|
const client = useClient()
|
|
@@ -29,18 +29,13 @@ export const EditSharedDriveModal = withLocales(({ onClose, document }) => {
|
|
|
29
29
|
setSelectedOption
|
|
30
30
|
} = usePendingRecipients()
|
|
31
31
|
const { onSubmit, isLoading } = useSubmitWithLoader()
|
|
32
|
+
const resolveRecipients = useResolveRecipients()
|
|
32
33
|
|
|
33
34
|
const [driveName, setDriveName] = useState(document.name)
|
|
34
35
|
|
|
35
36
|
const onSave = async () => {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
client,
|
|
39
|
-
pendingRecipients,
|
|
40
|
-
contact => client.create('io.cozy.contacts', contact)
|
|
41
|
-
)
|
|
42
|
-
const readWriteRecipients = selectedOption === 'readOnly' ? [] : contacts
|
|
43
|
-
const readOnlyRecipients = selectedOption === 'readOnly' ? contacts : []
|
|
37
|
+
const { recipients: readWriteRecipients, readOnlyRecipients } =
|
|
38
|
+
await resolveRecipients(pendingRecipients, selectedOption)
|
|
44
39
|
|
|
45
40
|
await share({
|
|
46
41
|
document: document,
|
|
@@ -100,7 +95,7 @@ export const EditSharedDriveModal = withLocales(({ onClose, document }) => {
|
|
|
100
95
|
onSelectedOptionChange={setSelectedOption}
|
|
101
96
|
/>
|
|
102
97
|
</div>
|
|
103
|
-
<
|
|
98
|
+
<WhoHasAccessWrapper
|
|
104
99
|
isOwner
|
|
105
100
|
isSharedDrive
|
|
106
101
|
showOwner={false}
|
|
@@ -2,12 +2,12 @@ import { useState } from 'react'
|
|
|
2
2
|
|
|
3
3
|
import { useClient } from 'cozy-client'
|
|
4
4
|
|
|
5
|
-
import { getOrCreateFromArray } from '../../helpers/contacts'
|
|
6
5
|
import { usePendingRecipients } from '../../hooks/usePendingRecipients'
|
|
7
|
-
import {
|
|
6
|
+
import { useResolveRecipients } from '../../hooks/useResolveRecipients'
|
|
8
7
|
|
|
9
8
|
export const useSharedDrive = () => {
|
|
10
9
|
const client = useClient()
|
|
10
|
+
const resolveRecipients = useResolveRecipients()
|
|
11
11
|
|
|
12
12
|
const [sharedDriveName, setSharedDriveName] = useState('')
|
|
13
13
|
const {
|
|
@@ -21,16 +21,9 @@ export const useSharedDrive = () => {
|
|
|
21
21
|
setSharedDriveName(event.target.value)
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
const createContact = contact => client.create(DOCTYPE_CONTACTS, contact)
|
|
25
|
-
|
|
26
24
|
const onCreate = async () => {
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
pendingRecipients,
|
|
30
|
-
createContact
|
|
31
|
-
)
|
|
32
|
-
const readWriteRecipients = selectedOption === 'readOnly' ? [] : contacts
|
|
33
|
-
const readOnlyRecipients = selectedOption === 'readOnly' ? contacts : []
|
|
25
|
+
const { recipients: readWriteRecipients, readOnlyRecipients } =
|
|
26
|
+
await resolveRecipients(pendingRecipients, selectedOption)
|
|
34
27
|
|
|
35
28
|
await client.collection('io.cozy.sharings').createSharedDrive({
|
|
36
29
|
name: sharedDriveName,
|
|
@@ -7,7 +7,7 @@ import { useI18n } from 'twake-i18n'
|
|
|
7
7
|
|
|
8
8
|
import AntivirusAlert from './AntivirusAlert'
|
|
9
9
|
import { default as DumbShareByEmail } from './ShareByEmail'
|
|
10
|
-
import
|
|
10
|
+
import WhoHasAccessWrapper from './WhoHasAccessWrapper'
|
|
11
11
|
import styles from '../styles/share.styl'
|
|
12
12
|
|
|
13
13
|
const SharingContent = ({
|
|
@@ -25,7 +25,6 @@ const SharingContent = ({
|
|
|
25
25
|
recipientsToBeConfirmed,
|
|
26
26
|
verifyRecipient,
|
|
27
27
|
link,
|
|
28
|
-
permissions,
|
|
29
28
|
pendingRecipients,
|
|
30
29
|
onPendingRecipientsChange,
|
|
31
30
|
selectedOption,
|
|
@@ -74,7 +73,7 @@ const SharingContent = ({
|
|
|
74
73
|
)}
|
|
75
74
|
</div>
|
|
76
75
|
{showWhoHasAccess && (
|
|
77
|
-
<
|
|
76
|
+
<WhoHasAccessWrapper
|
|
78
77
|
document={document}
|
|
79
78
|
documentType={documentType}
|
|
80
79
|
isOwner={isOwner}
|
|
@@ -84,7 +83,6 @@ const SharingContent = ({
|
|
|
84
83
|
recipientsToBeConfirmed={recipientsToBeConfirmed}
|
|
85
84
|
verifyRecipient={verifyRecipient}
|
|
86
85
|
link={link}
|
|
87
|
-
permissions={permissions}
|
|
88
86
|
/>
|
|
89
87
|
)}
|
|
90
88
|
</div>
|
|
@@ -8,7 +8,6 @@ import LinkRecipient from './Recipient/LinkRecipient'
|
|
|
8
8
|
import OwnerRecipient from './Recipient/OwnerRecipient'
|
|
9
9
|
import { RecipientList } from './Recipient/RecipientList'
|
|
10
10
|
import { usePrevious } from '../helpers/hooks'
|
|
11
|
-
import { useSharingContext } from '../hooks/useSharingContext'
|
|
12
11
|
|
|
13
12
|
/**
|
|
14
13
|
* Displays a warning if some contacts are waiting for confirmation of their sharing
|
|
@@ -35,6 +34,9 @@ const WhoHasAccess = ({
|
|
|
35
34
|
canManageMembers = !isReadOnly,
|
|
36
35
|
canManageLink = canManageMembers,
|
|
37
36
|
showOwner = true,
|
|
37
|
+
owner,
|
|
38
|
+
isInsideSharedParent = false,
|
|
39
|
+
sharedParentName,
|
|
38
40
|
recipients,
|
|
39
41
|
recipientsToBeConfirmed = [],
|
|
40
42
|
document,
|
|
@@ -42,15 +44,17 @@ const WhoHasAccess = ({
|
|
|
42
44
|
className,
|
|
43
45
|
onRevoke,
|
|
44
46
|
onRevokeSelf,
|
|
45
|
-
|
|
47
|
+
onRevokeGroup,
|
|
48
|
+
onLeaveGroup,
|
|
49
|
+
onSetMemberType,
|
|
50
|
+
onUpdateLinkPermissions,
|
|
51
|
+
onRevokeLink,
|
|
46
52
|
verifyRecipient,
|
|
47
53
|
link,
|
|
48
54
|
permissions
|
|
49
55
|
}) => {
|
|
50
56
|
const previousLink = usePrevious(link)
|
|
51
57
|
const linkHasBeenJustCreated = link && previousLink === null
|
|
52
|
-
const { isOrgSharedDrive } = useSharingContext()
|
|
53
|
-
const isOrgDrive = isOrgSharedDrive(document?._id)
|
|
54
58
|
|
|
55
59
|
return (
|
|
56
60
|
<div className={className}>
|
|
@@ -66,16 +70,13 @@ const WhoHasAccess = ({
|
|
|
66
70
|
onRevoke={onRevoke}
|
|
67
71
|
onRevokeSelf={onRevokeSelf}
|
|
68
72
|
permissions={permissions}
|
|
73
|
+
onUpdateLinkPermissions={onUpdateLinkPermissions}
|
|
74
|
+
onRevokeLink={onRevokeLink}
|
|
69
75
|
fadeIn={linkHasBeenJustCreated}
|
|
70
76
|
/>
|
|
71
77
|
)}
|
|
72
78
|
|
|
73
|
-
{showOwner &&
|
|
74
|
-
<OwnerRecipient
|
|
75
|
-
recipients={recipients}
|
|
76
|
-
isOrgSharedDrive={isOrgDrive}
|
|
77
|
-
/>
|
|
78
|
-
)}
|
|
79
|
+
{showOwner && <OwnerRecipient owner={owner} />}
|
|
79
80
|
|
|
80
81
|
<RecipientList
|
|
81
82
|
recipients={recipients}
|
|
@@ -86,9 +87,13 @@ const WhoHasAccess = ({
|
|
|
86
87
|
isSharedDrive={isSharedDrive}
|
|
87
88
|
document={document}
|
|
88
89
|
documentType={documentType}
|
|
90
|
+
isInsideSharedParent={isInsideSharedParent}
|
|
91
|
+
sharedParentName={sharedParentName}
|
|
89
92
|
onRevoke={onRevoke}
|
|
90
93
|
onRevokeSelf={onRevokeSelf}
|
|
91
|
-
|
|
94
|
+
onRevokeGroup={onRevokeGroup}
|
|
95
|
+
onLeaveGroup={onLeaveGroup}
|
|
96
|
+
onSetMemberType={onSetMemberType}
|
|
92
97
|
verifyRecipient={verifyRecipient}
|
|
93
98
|
/>
|
|
94
99
|
</List>
|
|
@@ -102,6 +107,9 @@ WhoHasAccess.propTypes = {
|
|
|
102
107
|
canManageMembers: PropTypes.bool,
|
|
103
108
|
canManageLink: PropTypes.bool,
|
|
104
109
|
showOwner: PropTypes.bool,
|
|
110
|
+
owner: PropTypes.object,
|
|
111
|
+
isInsideSharedParent: PropTypes.bool,
|
|
112
|
+
sharedParentName: PropTypes.string,
|
|
105
113
|
recipients: PropTypes.array.isRequired,
|
|
106
114
|
recipientsToBeConfirmed: PropTypes.arrayOf(
|
|
107
115
|
PropTypes.shape({
|
|
@@ -112,6 +120,12 @@ WhoHasAccess.propTypes = {
|
|
|
112
120
|
documentType: PropTypes.string.isRequired,
|
|
113
121
|
onRevoke: PropTypes.func.isRequired,
|
|
114
122
|
onRevokeSelf: PropTypes.func,
|
|
115
|
-
|
|
123
|
+
onRevokeGroup: PropTypes.func.isRequired,
|
|
124
|
+
onLeaveGroup: PropTypes.func.isRequired,
|
|
125
|
+
onSetMemberType: PropTypes.func.isRequired,
|
|
126
|
+
onUpdateLinkPermissions: PropTypes.func.isRequired,
|
|
127
|
+
onRevokeLink: PropTypes.func.isRequired,
|
|
128
|
+
verifyRecipient: PropTypes.func,
|
|
129
|
+
permissions: PropTypes.arrayOf(PropTypes.object)
|
|
116
130
|
}
|
|
117
131
|
export default WhoHasAccess
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
import { useClient, useQuery, hasQueryBeenLoaded } from 'cozy-client'
|
|
4
|
+
|
|
5
|
+
import WhoHasAccess from './WhoHasAccess'
|
|
6
|
+
import { hasMemberActions, markCurrentUser } from '../helpers/recipients'
|
|
7
|
+
import { useMemberPermissionType } from '../hooks/useMemberPermissionType'
|
|
8
|
+
import { useSharingContext } from '../hooks/useSharingContext'
|
|
9
|
+
import { buildInstanceSettingsQuery } from '../queries/queries'
|
|
10
|
+
|
|
11
|
+
// When the owner is not part of a sharing's members (eg. the folder only has
|
|
12
|
+
// a link), there is no per-sharing avatar to reference. The instance's public
|
|
13
|
+
// avatar still holds the owner's picture. fallback=initials makes the stack
|
|
14
|
+
// serve a generated initials avatar (instead of the app icon) when no picture
|
|
15
|
+
// has been uploaded, matching how avatars are rendered everywhere else.
|
|
16
|
+
const OWNER_PUBLIC_AVATAR_PATH = '/public/avatar?fallback=initials'
|
|
17
|
+
|
|
18
|
+
const WhoHasAccessWrapper = ({
|
|
19
|
+
isOwner = false,
|
|
20
|
+
canManageSharing = isOwner,
|
|
21
|
+
isSharedDrive,
|
|
22
|
+
isReadOnly,
|
|
23
|
+
canManageMembers = !isReadOnly,
|
|
24
|
+
canManageLink,
|
|
25
|
+
showOwner = true,
|
|
26
|
+
recipients,
|
|
27
|
+
recipientsToBeConfirmed = [],
|
|
28
|
+
document,
|
|
29
|
+
documentType,
|
|
30
|
+
className,
|
|
31
|
+
onRevoke,
|
|
32
|
+
onRevokeSelf,
|
|
33
|
+
verifyRecipient,
|
|
34
|
+
link
|
|
35
|
+
}) => {
|
|
36
|
+
const client = useClient()
|
|
37
|
+
const {
|
|
38
|
+
isOrgSharedDrive,
|
|
39
|
+
getDocumentPermissions,
|
|
40
|
+
updateDocumentPermissions,
|
|
41
|
+
revokeSharingLink,
|
|
42
|
+
revokeGroup,
|
|
43
|
+
revokeSelf
|
|
44
|
+
} = useSharingContext()
|
|
45
|
+
|
|
46
|
+
const instanceUri = client.options.uri
|
|
47
|
+
const markedRecipients = markCurrentUser(recipients, instanceUri)
|
|
48
|
+
const hasMemberMenus = markedRecipients.some(
|
|
49
|
+
recipient =>
|
|
50
|
+
!recipient.members &&
|
|
51
|
+
!recipientsToBeConfirmed.some(user => user.email === recipient.email) &&
|
|
52
|
+
hasMemberActions({
|
|
53
|
+
status: recipient.status,
|
|
54
|
+
isCurrentUser: recipient.isCurrentUser,
|
|
55
|
+
isOwner,
|
|
56
|
+
canManageSharing,
|
|
57
|
+
canManageMembers
|
|
58
|
+
})
|
|
59
|
+
)
|
|
60
|
+
const { isInsideSharedParent, sharedParentName, setMemberType } =
|
|
61
|
+
useMemberPermissionType(document, { shouldFetchPath: hasMemberMenus })
|
|
62
|
+
const ownerRecipient = markedRecipients.find(
|
|
63
|
+
recipient => recipient.status === 'owner'
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
const instanceSettingsQuery = buildInstanceSettingsQuery()
|
|
67
|
+
const instanceSettingsResult = useQuery(instanceSettingsQuery.definition, {
|
|
68
|
+
...instanceSettingsQuery.options,
|
|
69
|
+
enabled: showOwner && !ownerRecipient
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
const defaultOwner = hasQueryBeenLoaded(instanceSettingsResult)
|
|
73
|
+
? {
|
|
74
|
+
isOwner: true,
|
|
75
|
+
status: 'owner',
|
|
76
|
+
instance: instanceUri,
|
|
77
|
+
avatarPath: OWNER_PUBLIC_AVATAR_PATH,
|
|
78
|
+
public_name: instanceSettingsResult.data?.attributes?.public_name
|
|
79
|
+
}
|
|
80
|
+
: null
|
|
81
|
+
|
|
82
|
+
const owner = ownerRecipient
|
|
83
|
+
? { ...ownerRecipient, isOrgSharedDrive: isOrgSharedDrive(document?._id) }
|
|
84
|
+
: defaultOwner
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<WhoHasAccess
|
|
88
|
+
isOwner={isOwner}
|
|
89
|
+
canManageSharing={canManageSharing}
|
|
90
|
+
isSharedDrive={isSharedDrive}
|
|
91
|
+
isReadOnly={isReadOnly}
|
|
92
|
+
canManageMembers={canManageMembers}
|
|
93
|
+
canManageLink={canManageLink}
|
|
94
|
+
showOwner={showOwner}
|
|
95
|
+
owner={owner}
|
|
96
|
+
isInsideSharedParent={isInsideSharedParent}
|
|
97
|
+
sharedParentName={sharedParentName}
|
|
98
|
+
recipients={markedRecipients}
|
|
99
|
+
recipientsToBeConfirmed={recipientsToBeConfirmed}
|
|
100
|
+
document={document}
|
|
101
|
+
documentType={documentType}
|
|
102
|
+
className={className}
|
|
103
|
+
onRevoke={onRevoke}
|
|
104
|
+
onRevokeSelf={onRevokeSelf}
|
|
105
|
+
onRevokeGroup={revokeGroup}
|
|
106
|
+
onLeaveGroup={revokeSelf}
|
|
107
|
+
onSetMemberType={setMemberType}
|
|
108
|
+
onUpdateLinkPermissions={updateDocumentPermissions}
|
|
109
|
+
onRevokeLink={revokeSharingLink}
|
|
110
|
+
verifyRecipient={verifyRecipient}
|
|
111
|
+
link={link}
|
|
112
|
+
permissions={getDocumentPermissions(document?._id)}
|
|
113
|
+
/>
|
|
114
|
+
)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export default WhoHasAccessWrapper
|
|
@@ -99,3 +99,47 @@ export const getGroupRecipientSecondaryText = ({
|
|
|
99
99
|
(isUserInsideMembers ? ` (${t('GroupRecipient.secondary_you')})` : '')
|
|
100
100
|
)
|
|
101
101
|
}
|
|
102
|
+
|
|
103
|
+
const isInstance = (recipient, instanceUri) =>
|
|
104
|
+
recipient.instance !== undefined && recipient.instance === instanceUri
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Flags with `isCurrentUser` the recipients, and the members of group
|
|
108
|
+
* recipients, whose instance is the current user's one
|
|
109
|
+
*
|
|
110
|
+
* @param {object[]} recipients - Recipients of a sharing
|
|
111
|
+
* @param {string} instanceUri - URI of the current user's instance
|
|
112
|
+
* @returns {object[]} The recipients with their `isCurrentUser` flag
|
|
113
|
+
*/
|
|
114
|
+
export const markCurrentUser = (recipients, instanceUri) =>
|
|
115
|
+
recipients.map(recipient => ({
|
|
116
|
+
...recipient,
|
|
117
|
+
isCurrentUser: isInstance(recipient, instanceUri),
|
|
118
|
+
...(recipient.members && {
|
|
119
|
+
members: recipient.members.map(member => ({
|
|
120
|
+
...member,
|
|
121
|
+
isCurrentUser: isInstance(member, instanceUri)
|
|
122
|
+
}))
|
|
123
|
+
})
|
|
124
|
+
}))
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* @param {object} params
|
|
128
|
+
* @param {string} params.status - Status of the member in the sharing
|
|
129
|
+
* @param {boolean} params.isCurrentUser - Whether the member is the current user
|
|
130
|
+
* @param {boolean} params.isOwner - Whether the current user owns the sharing
|
|
131
|
+
* @param {boolean} params.canManageSharing - Whether the current user can manage the sharing
|
|
132
|
+
* @param {boolean} params.canManageMembers - Whether the members can be managed
|
|
133
|
+
* @returns {boolean} Whether the current user can change the member's
|
|
134
|
+
* permission type or revoke them
|
|
135
|
+
*/
|
|
136
|
+
export const hasMemberActions = ({
|
|
137
|
+
status,
|
|
138
|
+
isCurrentUser,
|
|
139
|
+
isOwner,
|
|
140
|
+
canManageSharing,
|
|
141
|
+
canManageMembers
|
|
142
|
+
}) =>
|
|
143
|
+
canManageMembers &&
|
|
144
|
+
status !== 'owner' &&
|
|
145
|
+
((isCurrentUser && !isOwner) || canManageSharing)
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { hasMemberActions, markCurrentUser } from './recipients'
|
|
2
|
+
|
|
3
|
+
const instanceUri = 'https://alice.cozy.cc'
|
|
4
|
+
|
|
5
|
+
describe('markCurrentUser', () => {
|
|
6
|
+
it('flags the recipient whose instance is the current one', () => {
|
|
7
|
+
const recipients = [
|
|
8
|
+
{ email: 'alice@cozy.cc', instance: instanceUri },
|
|
9
|
+
{ email: 'bob@cozy.cc', instance: 'https://bob.cozy.cc' },
|
|
10
|
+
{ email: 'carol@cozy.cc' }
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
expect(
|
|
14
|
+
markCurrentUser(recipients, instanceUri).map(r => r.isCurrentUser)
|
|
15
|
+
).toEqual([true, false, false])
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
it('does not flag a recipient without instance when the instance is unknown', () => {
|
|
19
|
+
expect(markCurrentUser([{ email: 'carol@cozy.cc' }], undefined)).toEqual([
|
|
20
|
+
{ email: 'carol@cozy.cc', isCurrentUser: false }
|
|
21
|
+
])
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
it('flags the members of group recipients', () => {
|
|
25
|
+
const recipients = [
|
|
26
|
+
{
|
|
27
|
+
name: 'Team',
|
|
28
|
+
members: [
|
|
29
|
+
{ instance: 'https://bob.cozy.cc' },
|
|
30
|
+
{ instance: instanceUri }
|
|
31
|
+
]
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
const [group] = markCurrentUser(recipients, instanceUri)
|
|
36
|
+
|
|
37
|
+
expect(group.isCurrentUser).toBe(false)
|
|
38
|
+
expect(group.members.map(m => m.isCurrentUser)).toEqual([false, true])
|
|
39
|
+
})
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
describe('hasMemberActions', () => {
|
|
43
|
+
const ownerRights = {
|
|
44
|
+
isOwner: true,
|
|
45
|
+
canManageSharing: true,
|
|
46
|
+
canManageMembers: true
|
|
47
|
+
}
|
|
48
|
+
const memberRights = {
|
|
49
|
+
isOwner: false,
|
|
50
|
+
canManageSharing: false,
|
|
51
|
+
canManageMembers: true
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
it('gives actions on the members to whom can manage the sharing', () => {
|
|
55
|
+
expect(hasMemberActions({ ...ownerRights, status: 'ready' })).toBe(true)
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
it('gives no action on the owner', () => {
|
|
59
|
+
expect(hasMemberActions({ ...ownerRights, status: 'owner' })).toBe(false)
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it('gives no action when the members cannot be managed', () => {
|
|
63
|
+
expect(
|
|
64
|
+
hasMemberActions({
|
|
65
|
+
...ownerRights,
|
|
66
|
+
canManageMembers: false,
|
|
67
|
+
status: 'ready'
|
|
68
|
+
})
|
|
69
|
+
).toBe(false)
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
it('gives actions on themselves to a member who cannot manage the sharing', () => {
|
|
73
|
+
expect(
|
|
74
|
+
hasMemberActions({
|
|
75
|
+
...memberRights,
|
|
76
|
+
status: 'ready',
|
|
77
|
+
isCurrentUser: true
|
|
78
|
+
})
|
|
79
|
+
).toBe(true)
|
|
80
|
+
expect(hasMemberActions({ ...memberRights, status: 'ready' })).toBe(false)
|
|
81
|
+
})
|
|
82
|
+
})
|