cozy-sharing 33.5.1 → 33.6.1
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 +17 -0
- package/dist/SharingProvider.spec.js +242 -113
- package/dist/components/FederatedFolder/FederatedFolderModal.js +1 -4
- package/dist/components/Recipient/LinkRecipient.js +40 -17
- package/dist/components/Recipient/LinkRecipientPermissions.js +7 -6
- package/dist/components/Recipient/MemberRecipient.js +1 -4
- package/dist/components/Recipient/MemberRecipientPermissions.js +4 -6
- package/dist/components/Recipient/RecipientConfirm.js +1 -4
- package/dist/components/Recipient/RecipientList.js +2 -0
- package/dist/components/ShareByEmail.js +1 -4
- package/dist/components/ShareDialogCozyToCozy.js +1 -4
- package/dist/components/ShareModal/ShareModal.js +1 -5
- package/dist/components/ShareModal/SharingDetailsModal.js +6 -38
- package/dist/components/SharedDrive/EditSharedDriveModal.js +1 -4
- package/dist/components/WhoHasAccess.js +4 -0
- package/dist/state.js +5 -4
- package/dist/state.spec.js +44 -27
- package/dist/styles/share.styl +0 -21
- package/dist/stylesheet.css +0 -22
- package/locales/en.json +1 -2
- package/locales/fr.json +1 -2
- package/locales/ru.json +1 -2
- package/locales/vi.json +1 -2
- package/package.json +2 -2
- package/src/SharingProvider.spec.jsx +115 -1
- package/src/components/Recipient/LinkRecipient.jsx +50 -15
- package/src/components/Recipient/LinkRecipientPermissions.jsx +27 -20
- package/src/components/Recipient/MemberRecipient.jsx +1 -1
- package/src/components/Recipient/MemberRecipientPermissions.jsx +10 -5
- package/src/components/Recipient/RecipientList.jsx +2 -0
- package/src/components/ShareModal/ShareModal.jsx +1 -5
- package/src/components/ShareModal/SharingDetailsModal.jsx +3 -47
- package/src/components/WhoHasAccess.jsx +3 -0
- package/src/state.js +5 -13
- package/src/state.spec.js +63 -29
- package/src/styles/share.styl +0 -21
|
@@ -5,7 +5,12 @@ import { createMockClient } from 'cozy-client'
|
|
|
5
5
|
|
|
6
6
|
import { SharingProvider } from './SharingProvider'
|
|
7
7
|
import SharingContext from './context'
|
|
8
|
-
import reducer, {
|
|
8
|
+
import reducer, {
|
|
9
|
+
addSharingLink,
|
|
10
|
+
getDocumentPermissions,
|
|
11
|
+
getDocumentSharing,
|
|
12
|
+
receiveSharings
|
|
13
|
+
} from './state'
|
|
9
14
|
import AppLike from '../test/AppLike'
|
|
10
15
|
|
|
11
16
|
const AppWrapper = ({ children, client, isPublic }) => {
|
|
@@ -291,6 +296,115 @@ describe('updateDocumentPermissions', () => {
|
|
|
291
296
|
})
|
|
292
297
|
})
|
|
293
298
|
|
|
299
|
+
describe('shared drive recipient revocation', () => {
|
|
300
|
+
const SHARED_DRIVE = {
|
|
301
|
+
_id: 'sharing_123',
|
|
302
|
+
id: 'sharing_123',
|
|
303
|
+
type: 'io.cozy.sharings',
|
|
304
|
+
attributes: {
|
|
305
|
+
drive: true,
|
|
306
|
+
owner: true,
|
|
307
|
+
description: 'Shared folder',
|
|
308
|
+
rules: [
|
|
309
|
+
{
|
|
310
|
+
title: 'Shared folder',
|
|
311
|
+
doctype: 'io.cozy.files',
|
|
312
|
+
values: ['folder_123'],
|
|
313
|
+
add: 'none',
|
|
314
|
+
update: 'none',
|
|
315
|
+
remove: 'none'
|
|
316
|
+
}
|
|
317
|
+
],
|
|
318
|
+
members: [
|
|
319
|
+
{
|
|
320
|
+
status: 'owner',
|
|
321
|
+
email: 'owner@cozy.local',
|
|
322
|
+
instance: 'http://cozy.local'
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
status: 'pending',
|
|
326
|
+
email: 'recipient@cozy.local',
|
|
327
|
+
instance: 'http://recipient.cozy.local'
|
|
328
|
+
}
|
|
329
|
+
]
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
const NEW_SHARING = {
|
|
333
|
+
...SHARED_DRIVE,
|
|
334
|
+
_id: 'sharing_456',
|
|
335
|
+
id: 'sharing_456'
|
|
336
|
+
}
|
|
337
|
+
const document = {
|
|
338
|
+
_id: 'folder_123',
|
|
339
|
+
id: 'folder_123',
|
|
340
|
+
path: '/Shared folder'
|
|
341
|
+
}
|
|
342
|
+
const recipient = {
|
|
343
|
+
_id: 'contact_123',
|
|
344
|
+
id: 'contact_123',
|
|
345
|
+
_type: 'io.cozy.contacts',
|
|
346
|
+
email: 'recipient@cozy.local'
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
let provider
|
|
350
|
+
let sharingCol
|
|
351
|
+
|
|
352
|
+
beforeEach(() => {
|
|
353
|
+
const mockClient = createMockClient({})
|
|
354
|
+
mockClient.getStackClient = () => ({ uri: 'http://cozy.local' })
|
|
355
|
+
mockClient.collection = jest.fn().mockReturnValue({})
|
|
356
|
+
|
|
357
|
+
provider = new SharingProvider({ client: mockClient })
|
|
358
|
+
provider.state = {
|
|
359
|
+
...provider.state,
|
|
360
|
+
...reducer(undefined, receiveSharings({ sharings: [SHARED_DRIVE] }))
|
|
361
|
+
}
|
|
362
|
+
provider.dispatch = jest.fn(action => {
|
|
363
|
+
provider.state = {
|
|
364
|
+
...provider.state,
|
|
365
|
+
...reducer(provider.state, action)
|
|
366
|
+
}
|
|
367
|
+
})
|
|
368
|
+
sharingCol = {
|
|
369
|
+
addRecipients: jest.fn(),
|
|
370
|
+
create: jest.fn().mockResolvedValue({ data: NEW_SHARING }),
|
|
371
|
+
revokeRecipient: jest.fn().mockResolvedValue({})
|
|
372
|
+
}
|
|
373
|
+
provider.sharingCol = sharingCol
|
|
374
|
+
})
|
|
375
|
+
|
|
376
|
+
it('forgets the shared drive when revoking its last recipient', async () => {
|
|
377
|
+
await provider.revoke(document, SHARED_DRIVE.id, 1)
|
|
378
|
+
|
|
379
|
+
expect(sharingCol.revokeRecipient).toHaveBeenCalledWith(SHARED_DRIVE, 1)
|
|
380
|
+
expect(getDocumentSharing(provider.state, document.id)).toBeNull()
|
|
381
|
+
})
|
|
382
|
+
|
|
383
|
+
it('creates a new sharing when re-adding after the last recipient was revoked', async () => {
|
|
384
|
+
await provider.revoke(document, SHARED_DRIVE.id, 1)
|
|
385
|
+
|
|
386
|
+
await provider.share({
|
|
387
|
+
document,
|
|
388
|
+
recipients: [recipient],
|
|
389
|
+
readOnlyRecipients: [],
|
|
390
|
+
description: 'Shared folder',
|
|
391
|
+
openSharing: true,
|
|
392
|
+
sharedDrive: true
|
|
393
|
+
})
|
|
394
|
+
|
|
395
|
+
expect(sharingCol.addRecipients).not.toHaveBeenCalled()
|
|
396
|
+
expect(sharingCol.create).toHaveBeenCalledWith({
|
|
397
|
+
document,
|
|
398
|
+
recipients: [recipient],
|
|
399
|
+
readOnlyRecipients: [],
|
|
400
|
+
description: 'Shared folder',
|
|
401
|
+
previewPath: '/preview',
|
|
402
|
+
openSharing: true,
|
|
403
|
+
sharedDrive: true
|
|
404
|
+
})
|
|
405
|
+
})
|
|
406
|
+
})
|
|
407
|
+
|
|
294
408
|
describe('fetchSharedDriveSharingLinks', () => {
|
|
295
409
|
const PERM_DRIVE_FILE = {
|
|
296
410
|
type: 'io.cozy.permissions',
|
|
@@ -24,16 +24,51 @@ import { FADE_IN_DURATION } from '../../helpers/recipients'
|
|
|
24
24
|
import { useSharingContext } from '../../hooks/useSharingContext'
|
|
25
25
|
import { ShareRestrictionModal } from '../ShareRestrictionModal/ShareRestrictionModal'
|
|
26
26
|
|
|
27
|
+
const LinkRecipientTextReadOnly = ({ textPrimary, textSecondary }) => {
|
|
28
|
+
return (
|
|
29
|
+
<ListItemText
|
|
30
|
+
primary={
|
|
31
|
+
<div className="u-flex u-flex-items-center">
|
|
32
|
+
<Typography>{textPrimary}</Typography>
|
|
33
|
+
</div>
|
|
34
|
+
}
|
|
35
|
+
secondary={textSecondary}
|
|
36
|
+
/>
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const LinkRecipientText = ({ onClick, textPrimary, textSecondary }) => {
|
|
41
|
+
return (
|
|
42
|
+
<ListItemText
|
|
43
|
+
onClick={onClick}
|
|
44
|
+
primary={
|
|
45
|
+
<div className="u-flex u-flex-items-center">
|
|
46
|
+
<Typography className="u-c-pointer ">{textPrimary}</Typography>
|
|
47
|
+
<IconButton size="small" className="u-ml-half">
|
|
48
|
+
<Icon icon={Gear} />
|
|
49
|
+
</IconButton>
|
|
50
|
+
</div>
|
|
51
|
+
}
|
|
52
|
+
secondary={textSecondary}
|
|
53
|
+
/>
|
|
54
|
+
)
|
|
55
|
+
}
|
|
56
|
+
|
|
27
57
|
const LinkRecipient = props => {
|
|
28
58
|
const { t, f, lang } = useI18n()
|
|
29
59
|
const { isMobile } = useBreakpoints()
|
|
30
60
|
const { getDocumentPermissions } = useSharingContext()
|
|
31
61
|
|
|
32
|
-
const {
|
|
62
|
+
const {
|
|
63
|
+
recipientConfirmationData,
|
|
64
|
+
verifyRecipient,
|
|
65
|
+
fadeIn,
|
|
66
|
+
document,
|
|
67
|
+
isReadOnly
|
|
68
|
+
} = props
|
|
33
69
|
|
|
34
70
|
const [openShareRestrictionModal, setOpenShareRestrictionModal] =
|
|
35
71
|
useState(false)
|
|
36
|
-
|
|
37
72
|
const permissions = getDocumentPermissions(document?._id)
|
|
38
73
|
const hasPassword = checkIsPermissionHasPassword(permissions)
|
|
39
74
|
const expiresDate = getPermissionExpiresDate(permissions)
|
|
@@ -57,7 +92,7 @@ const LinkRecipient = props => {
|
|
|
57
92
|
verifyRecipient={verifyRecipient}
|
|
58
93
|
/>
|
|
59
94
|
) : (
|
|
60
|
-
<LinkRecipientPermissions {...props}
|
|
95
|
+
<LinkRecipientPermissions {...props} />
|
|
61
96
|
)
|
|
62
97
|
|
|
63
98
|
return (
|
|
@@ -68,18 +103,18 @@ const LinkRecipient = props => {
|
|
|
68
103
|
<Icon icon={LinkIcon} />
|
|
69
104
|
</Avatar>
|
|
70
105
|
</ListItemIcon>
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
106
|
+
{isReadOnly ? (
|
|
107
|
+
<LinkRecipientTextReadOnly
|
|
108
|
+
textPrimary={textPrimary}
|
|
109
|
+
textSecondary={textSecondary}
|
|
110
|
+
/>
|
|
111
|
+
) : (
|
|
112
|
+
<LinkRecipientText
|
|
113
|
+
onClick={() => setOpenShareRestrictionModal(true)}
|
|
114
|
+
textPrimary={textPrimary}
|
|
115
|
+
textSecondary={textSecondary}
|
|
116
|
+
/>
|
|
117
|
+
)}
|
|
83
118
|
{RightPart}
|
|
84
119
|
{openShareRestrictionModal && (
|
|
85
120
|
<ShareRestrictionModal
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
divider
|
|
8
8
|
} from 'cozy-ui/transpiled/react/ActionsMenu/Actions'
|
|
9
9
|
import DropdownButton from 'cozy-ui/transpiled/react/DropdownButton'
|
|
10
|
+
import Typography from 'cozy-ui/transpiled/react/Typography'
|
|
10
11
|
import { useI18n } from 'twake-i18n'
|
|
11
12
|
|
|
12
13
|
import { revokeLink } from './actions/revokeLink'
|
|
@@ -17,7 +18,7 @@ import { checkIsReadOnlyPermissions } from '../../helpers/permissions'
|
|
|
17
18
|
import { useSharingContext } from '../../hooks/useSharingContext'
|
|
18
19
|
import { WRITE_PERMS, READ_ONLY_PERMS } from '../ShareRestrictionModal/helpers'
|
|
19
20
|
|
|
20
|
-
const LinkRecipientPermissions = ({
|
|
21
|
+
const LinkRecipientPermissions = ({ document, isReadOnly }) => {
|
|
21
22
|
const { t } = useI18n()
|
|
22
23
|
const buttonRef = useRef()
|
|
23
24
|
const [isMenuDisplayed, setMenuDisplayed] = useState(false)
|
|
@@ -56,30 +57,36 @@ const LinkRecipientPermissions = ({ className, document }) => {
|
|
|
56
57
|
handleRevocation
|
|
57
58
|
})
|
|
58
59
|
|
|
60
|
+
const shouldShowMenu = !isReadOnly
|
|
61
|
+
|
|
59
62
|
return (
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
<
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
63
|
+
<>
|
|
64
|
+
{!shouldShowMenu && (
|
|
65
|
+
<Typography variant="body2">{t(`Share.type.${type}`)}</Typography>
|
|
66
|
+
)}
|
|
67
|
+
{shouldShowMenu && (
|
|
68
|
+
<>
|
|
69
|
+
<DropdownButton
|
|
70
|
+
ref={buttonRef}
|
|
71
|
+
textVariant="body2"
|
|
72
|
+
onClick={toggleMenu}
|
|
73
|
+
>
|
|
74
|
+
{t(`Share.type.${type}`)}
|
|
75
|
+
</DropdownButton>
|
|
76
|
+
<ActionsMenu
|
|
77
|
+
ref={buttonRef}
|
|
78
|
+
open={isMenuDisplayed}
|
|
79
|
+
actions={actions}
|
|
80
|
+
autoClose
|
|
81
|
+
onClose={hideMenu}
|
|
82
|
+
/>
|
|
83
|
+
</>
|
|
84
|
+
)}
|
|
85
|
+
</>
|
|
78
86
|
)
|
|
79
87
|
}
|
|
80
88
|
|
|
81
89
|
LinkRecipientPermissions.propTypes = {
|
|
82
|
-
className: PropTypes.string,
|
|
83
90
|
document: PropTypes.object.isRequired
|
|
84
91
|
}
|
|
85
92
|
|
|
@@ -21,11 +21,11 @@ const log = minilog('MemberRecipientPermissions')
|
|
|
21
21
|
|
|
22
22
|
const MemberRecipientPermissions = ({
|
|
23
23
|
isOwner,
|
|
24
|
+
isReadOnly,
|
|
24
25
|
status,
|
|
25
26
|
instance,
|
|
26
27
|
type,
|
|
27
28
|
document,
|
|
28
|
-
className,
|
|
29
29
|
onRevoke,
|
|
30
30
|
onRevokeSelf,
|
|
31
31
|
sharingId,
|
|
@@ -44,6 +44,7 @@ const MemberRecipientPermissions = ({
|
|
|
44
44
|
instance !== undefined && instance === client.options.uri
|
|
45
45
|
const contactIsOwner = status === 'owner'
|
|
46
46
|
const shouldShowMenu =
|
|
47
|
+
!isReadOnly &&
|
|
47
48
|
!revoking &&
|
|
48
49
|
!contactIsOwner &&
|
|
49
50
|
((instanceMatchesClient && !isOwner) || isOwner)
|
|
@@ -107,10 +108,14 @@ const MemberRecipientPermissions = ({
|
|
|
107
108
|
)
|
|
108
109
|
|
|
109
110
|
return (
|
|
110
|
-
|
|
111
|
+
<>
|
|
111
112
|
{revoking && <Spinner />}
|
|
112
|
-
{!shouldShowMenu &&
|
|
113
|
-
<Typography variant="body2">
|
|
113
|
+
{!shouldShowMenu && (
|
|
114
|
+
<Typography variant="body2">
|
|
115
|
+
{contactIsOwner
|
|
116
|
+
? t(`Share.status.${status}`)
|
|
117
|
+
: t(`Share.type.${type}`)}
|
|
118
|
+
</Typography>
|
|
114
119
|
)}
|
|
115
120
|
{shouldShowMenu && (
|
|
116
121
|
<>
|
|
@@ -140,7 +145,7 @@ const MemberRecipientPermissions = ({
|
|
|
140
145
|
</IconButton>
|
|
141
146
|
</>
|
|
142
147
|
)}
|
|
143
|
-
|
|
148
|
+
</>
|
|
144
149
|
)
|
|
145
150
|
}
|
|
146
151
|
|
|
@@ -10,6 +10,7 @@ const RecipientList = ({
|
|
|
10
10
|
recipientsToBeConfirmed,
|
|
11
11
|
isOwner,
|
|
12
12
|
isSharedDrive,
|
|
13
|
+
isReadOnly,
|
|
13
14
|
document,
|
|
14
15
|
documentType,
|
|
15
16
|
onRevoke,
|
|
@@ -49,6 +50,7 @@ const RecipientList = ({
|
|
|
49
50
|
key={recipient.index}
|
|
50
51
|
isOwner={isOwner}
|
|
51
52
|
isSharedDrive={isSharedDrive}
|
|
53
|
+
isReadOnly={isReadOnly}
|
|
52
54
|
document={document}
|
|
53
55
|
documentType={documentType}
|
|
54
56
|
onRevoke={onRevoke}
|
|
@@ -22,8 +22,6 @@ export const ShareModal = withLocales(props => {
|
|
|
22
22
|
isOwner,
|
|
23
23
|
canReshare,
|
|
24
24
|
documentType,
|
|
25
|
-
getOwner,
|
|
26
|
-
getSharingType,
|
|
27
25
|
getRecipients,
|
|
28
26
|
revokeSelf,
|
|
29
27
|
allLoaded
|
|
@@ -48,11 +46,9 @@ export const ShareModal = withLocales(props => {
|
|
|
48
46
|
|
|
49
47
|
return (
|
|
50
48
|
<SharingDetailsModal
|
|
49
|
+
recipients={getRecipients(document.id)}
|
|
51
50
|
document={document}
|
|
52
51
|
documentType={documentType}
|
|
53
|
-
owner={getOwner(document.id)}
|
|
54
|
-
sharingType={getSharingType(document.id)}
|
|
55
|
-
recipients={getRecipients(document.id)}
|
|
56
52
|
onRevoke={revokeSelf}
|
|
57
53
|
onRevokeSelf={handleRevokeSelf}
|
|
58
54
|
{...rest}
|
|
@@ -1,29 +1,21 @@
|
|
|
1
|
-
import cx from 'classnames'
|
|
2
1
|
import PropTypes from 'prop-types'
|
|
3
2
|
import React from 'react'
|
|
4
3
|
|
|
5
4
|
import { Dialog } from 'cozy-ui/transpiled/react/CozyDialogs'
|
|
6
|
-
import Divider from 'cozy-ui/transpiled/react/Divider'
|
|
7
|
-
import useBreakpoints from 'cozy-ui/transpiled/react/providers/Breakpoints'
|
|
8
5
|
import { useI18n } from 'twake-i18n'
|
|
9
6
|
|
|
10
|
-
import { getDisplayName } from '../../models'
|
|
11
7
|
import styles from '../../styles/share.styl'
|
|
12
|
-
import OwnerIdentity from '../Identity/OwnerIdentity'
|
|
13
8
|
import WhoHasAccess from '../WhoHasAccess'
|
|
14
9
|
|
|
15
10
|
export const SharingDetailsModal = ({
|
|
16
11
|
onClose,
|
|
17
|
-
sharingType,
|
|
18
|
-
owner,
|
|
19
12
|
recipients,
|
|
20
13
|
document,
|
|
21
14
|
documentType = 'Document',
|
|
22
15
|
onRevoke,
|
|
23
16
|
onRevokeSelf
|
|
24
17
|
}) => {
|
|
25
|
-
const { t
|
|
26
|
-
const { isMobile } = useBreakpoints()
|
|
18
|
+
const { t } = useI18n()
|
|
27
19
|
|
|
28
20
|
return (
|
|
29
21
|
<Dialog
|
|
@@ -34,50 +26,14 @@ export const SharingDetailsModal = ({
|
|
|
34
26
|
title={t(`${documentType}.share.details.title`)}
|
|
35
27
|
content={
|
|
36
28
|
<div className={styles['share-modal-content']}>
|
|
37
|
-
<div
|
|
38
|
-
className={cx(
|
|
39
|
-
styles['share-details'],
|
|
40
|
-
isMobile ? 'u-ph-1 u-pt-1-half' : 'u-ph-2 u-pt-1'
|
|
41
|
-
)}
|
|
42
|
-
>
|
|
43
|
-
{owner ? (
|
|
44
|
-
<OwnerIdentity
|
|
45
|
-
name={t(`${documentType}.share.sharedBy`, {
|
|
46
|
-
name: getDisplayName(owner)
|
|
47
|
-
})}
|
|
48
|
-
url={owner.instance}
|
|
49
|
-
/>
|
|
50
|
-
) : null}
|
|
51
|
-
<div className={styles['share-details-created']}>
|
|
52
|
-
{t(`${documentType}.share.details.createdAt`, {
|
|
53
|
-
date: f(document.created_at || null, 'do LLLL yyyy')
|
|
54
|
-
})}
|
|
55
|
-
</div>
|
|
56
|
-
<div className={styles['share-details-perm']}>
|
|
57
|
-
{t(
|
|
58
|
-
`${documentType}.share.details.${
|
|
59
|
-
sharingType === 'one-way' ? 'ro' : 'rw'
|
|
60
|
-
}`
|
|
61
|
-
)}
|
|
62
|
-
</div>
|
|
63
|
-
<div className={styles['share-details-perm-desc']}>
|
|
64
|
-
{t(
|
|
65
|
-
`${documentType}.share.details.desc.${
|
|
66
|
-
sharingType === 'one-way' ? 'ro' : 'rw'
|
|
67
|
-
}`
|
|
68
|
-
)}
|
|
69
|
-
</div>
|
|
70
|
-
</div>
|
|
71
|
-
<Divider className="u-mv-1" />
|
|
72
29
|
<WhoHasAccess
|
|
73
|
-
|
|
74
|
-
count: recipients.length
|
|
75
|
-
})}
|
|
30
|
+
isReadOnly
|
|
76
31
|
recipients={recipients}
|
|
77
32
|
document={document}
|
|
78
33
|
documentType={documentType}
|
|
79
34
|
onRevoke={onRevoke}
|
|
80
35
|
onRevokeSelf={onRevokeSelf}
|
|
36
|
+
link="ok"
|
|
81
37
|
/>
|
|
82
38
|
</div>
|
|
83
39
|
}
|
|
@@ -29,6 +29,7 @@ const RecipientWaitingForConfirmationAlert = ({ recipientsToBeConfirmed }) => {
|
|
|
29
29
|
const WhoHasAccess = ({
|
|
30
30
|
isOwner = false,
|
|
31
31
|
isSharedDrive = false,
|
|
32
|
+
isReadOnly = false,
|
|
32
33
|
showOwner = true,
|
|
33
34
|
recipients,
|
|
34
35
|
recipientsToBeConfirmed = [],
|
|
@@ -53,6 +54,7 @@ const WhoHasAccess = ({
|
|
|
53
54
|
<List>
|
|
54
55
|
{link && (
|
|
55
56
|
<LinkRecipient
|
|
57
|
+
isReadOnly={isReadOnly}
|
|
56
58
|
document={document}
|
|
57
59
|
documentType={documentType}
|
|
58
60
|
onRevoke={onRevoke}
|
|
@@ -69,6 +71,7 @@ const WhoHasAccess = ({
|
|
|
69
71
|
recipientsToBeConfirmed={recipientsToBeConfirmed}
|
|
70
72
|
isOwner={isOwner}
|
|
71
73
|
isSharedDrive={isSharedDrive}
|
|
74
|
+
isReadOnly={isReadOnly}
|
|
72
75
|
document={document}
|
|
73
76
|
documentType={documentType}
|
|
74
77
|
onRevoke={onRevoke}
|
package/src/state.js
CHANGED
|
@@ -184,10 +184,7 @@ const byDocId = (state = {}, action) => {
|
|
|
184
184
|
case REVOKE_GROUP:
|
|
185
185
|
case REVOKE_RECIPIENT:
|
|
186
186
|
case UPDATE_SHARING:
|
|
187
|
-
if (
|
|
188
|
-
!isSharingADrive(action.sharing) &&
|
|
189
|
-
areAllRecipientsRevoked(action.sharing)
|
|
190
|
-
) {
|
|
187
|
+
if (areAllRecipientsRevoked(action.sharing)) {
|
|
191
188
|
return forgetSharing(state, action.sharing)
|
|
192
189
|
}
|
|
193
190
|
return state
|
|
@@ -258,10 +255,7 @@ const sharings = (state = [], action) => {
|
|
|
258
255
|
case UPDATE_SHARING:
|
|
259
256
|
case REVOKE_GROUP:
|
|
260
257
|
case REVOKE_RECIPIENT:
|
|
261
|
-
if (
|
|
262
|
-
!isSharingADrive(action.sharing) &&
|
|
263
|
-
areAllRecipientsRevoked(action.sharing)
|
|
264
|
-
) {
|
|
258
|
+
if (areAllRecipientsRevoked(action.sharing)) {
|
|
265
259
|
return state.filter(s => s.id !== action.sharing.id)
|
|
266
260
|
}
|
|
267
261
|
return state.map(s => {
|
|
@@ -287,10 +281,8 @@ const sharedPaths = (state = [], action) => {
|
|
|
287
281
|
return newState
|
|
288
282
|
case REVOKE_GROUP:
|
|
289
283
|
case REVOKE_RECIPIENT:
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
areAllRecipientsRevoked(action.sharing)
|
|
293
|
-
) {
|
|
284
|
+
case UPDATE_SHARING:
|
|
285
|
+
if (areAllRecipientsRevoked(action.sharing)) {
|
|
294
286
|
return state.filter(p => p !== action.path)
|
|
295
287
|
}
|
|
296
288
|
return state
|
|
@@ -351,7 +343,7 @@ export const canReshare = (state, docId, instanceUri) => {
|
|
|
351
343
|
const sharing = getDocumentSharing(state, docId)
|
|
352
344
|
const me = sharing.attributes.members.find(matchingInstanceName(instanceUri))
|
|
353
345
|
if (sharing.drive) {
|
|
354
|
-
return me && !me.read_only
|
|
346
|
+
return !sharing.org_drive && me && !me.read_only
|
|
355
347
|
} else {
|
|
356
348
|
return sharing.attributes.open_sharing === true && me && !me.read_only
|
|
357
349
|
}
|
package/src/state.spec.js
CHANGED
|
@@ -6,6 +6,7 @@ import reducer, {
|
|
|
6
6
|
revokeSharingLink,
|
|
7
7
|
getRecipients,
|
|
8
8
|
getRecipientsFromSharing,
|
|
9
|
+
revokeGroup,
|
|
9
10
|
revokeRecipient,
|
|
10
11
|
revokeSelf,
|
|
11
12
|
updateSharing,
|
|
@@ -143,7 +144,7 @@ describe('Sharing state', () => {
|
|
|
143
144
|
expect(state.sharings).toEqual([SHARING_1, SHARED_DRIVE_WITHOUT_RECIPIENTS])
|
|
144
145
|
})
|
|
145
146
|
|
|
146
|
-
it('should
|
|
147
|
+
it('should forget shared drives without recipients when updating', () => {
|
|
147
148
|
const SHARED_DRIVE_WITHOUT_RECIPIENTS = {
|
|
148
149
|
...SHARING_1,
|
|
149
150
|
id: 'shared_drive_no_recipients',
|
|
@@ -184,16 +185,65 @@ describe('Sharing state', () => {
|
|
|
184
185
|
}
|
|
185
186
|
}
|
|
186
187
|
const state = reducer(initialState, updateSharing(updatedSharing))
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
188
|
+
|
|
189
|
+
expect(state.byDocId).toEqual({})
|
|
190
|
+
expect(state.sharings).toEqual([])
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
it('should forget a shared drive when revoking its last group', () => {
|
|
194
|
+
const SHARED_DRIVE_WITH_GROUP = {
|
|
195
|
+
...SHARING_1,
|
|
196
|
+
id: 'shared_drive_group',
|
|
197
|
+
attributes: {
|
|
198
|
+
...SHARING_1.attributes,
|
|
199
|
+
drive: true,
|
|
200
|
+
members: [
|
|
201
|
+
{
|
|
202
|
+
status: 'owner',
|
|
203
|
+
name: 'Jane Doe',
|
|
204
|
+
email: 'jane@doe.com',
|
|
205
|
+
instance: 'http://cozy.tools:8080'
|
|
206
|
+
}
|
|
207
|
+
],
|
|
208
|
+
groups: [
|
|
209
|
+
{
|
|
210
|
+
name: 'Last group',
|
|
211
|
+
addedBy: 0
|
|
212
|
+
}
|
|
213
|
+
],
|
|
214
|
+
rules: [
|
|
215
|
+
{
|
|
216
|
+
title: 'My Shared Drive',
|
|
217
|
+
doctype: 'io.cozy.files',
|
|
218
|
+
values: ['shared_drive_group_folder'],
|
|
219
|
+
add: 'sync',
|
|
220
|
+
update: 'sync',
|
|
221
|
+
remove: 'sync'
|
|
222
|
+
}
|
|
223
|
+
]
|
|
191
224
|
}
|
|
192
|
-
}
|
|
193
|
-
|
|
225
|
+
}
|
|
226
|
+
const sharedDrivePath = '/shared_drive_group_folder'
|
|
227
|
+
const initialState = reducer(
|
|
228
|
+
reducer(
|
|
229
|
+
undefined,
|
|
230
|
+
receiveSharings({
|
|
231
|
+
sharings: [SHARED_DRIVE_WITH_GROUP]
|
|
232
|
+
})
|
|
233
|
+
),
|
|
234
|
+
addSharing(SHARED_DRIVE_WITH_GROUP, sharedDrivePath)
|
|
235
|
+
)
|
|
236
|
+
const state = reducer(
|
|
237
|
+
initialState,
|
|
238
|
+
revokeGroup(SHARED_DRIVE_WITH_GROUP, 0, sharedDrivePath)
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
expect(state.byDocId).toEqual({})
|
|
242
|
+
expect(state.sharings).toEqual([])
|
|
243
|
+
expect(state.sharedPaths).toEqual([])
|
|
194
244
|
})
|
|
195
245
|
|
|
196
|
-
it('should
|
|
246
|
+
it('should forget a shared drive when revoking last recipient', () => {
|
|
197
247
|
const SHARED_DRIVE_WITH_ONE_RECIPIENT = {
|
|
198
248
|
...SHARING_1,
|
|
199
249
|
id: 'shared_drive_one_recipient',
|
|
@@ -236,30 +286,14 @@ describe('Sharing state', () => {
|
|
|
236
286
|
),
|
|
237
287
|
addSharing(SHARED_DRIVE_WITH_ONE_RECIPIENT, sharedDrivePath)
|
|
238
288
|
)
|
|
239
|
-
const sharingAfterRevoke = {
|
|
240
|
-
...SHARED_DRIVE_WITH_ONE_RECIPIENT,
|
|
241
|
-
attributes: {
|
|
242
|
-
...SHARED_DRIVE_WITH_ONE_RECIPIENT.attributes,
|
|
243
|
-
members: [
|
|
244
|
-
SHARED_DRIVE_WITH_ONE_RECIPIENT.attributes.members[0],
|
|
245
|
-
{
|
|
246
|
-
...SHARED_DRIVE_WITH_ONE_RECIPIENT.attributes.members[1],
|
|
247
|
-
status: 'revoked'
|
|
248
|
-
}
|
|
249
|
-
]
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
289
|
const state = reducer(
|
|
253
290
|
initialState,
|
|
254
|
-
revokeRecipient(
|
|
291
|
+
revokeRecipient(SHARED_DRIVE_WITH_ONE_RECIPIENT, 1, sharedDrivePath)
|
|
255
292
|
)
|
|
256
|
-
|
|
257
|
-
expect(state.byDocId).toEqual({
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
permissions: []
|
|
261
|
-
}
|
|
262
|
-
})
|
|
293
|
+
|
|
294
|
+
expect(state.byDocId).toEqual({})
|
|
295
|
+
expect(state.sharings).toEqual([])
|
|
296
|
+
expect(state.sharedPaths).toEqual([])
|
|
263
297
|
})
|
|
264
298
|
|
|
265
299
|
it('should index received permissions', () => {
|