cozy-sharing 37.2.9 → 37.2.11
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/AGENTS.md +21 -0
- package/CHANGELOG.md +8 -0
- package/README.md +201 -41
- package/package.json +3 -3
package/AGENTS.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# cozy-sharing — agent rules
|
|
2
|
+
|
|
3
|
+
## Keep README.md in sync with the code
|
|
4
|
+
|
|
5
|
+
The implementation is the behavioral source of truth. `README.md` documents the implemented behavior and usage, and MUST be updated in the same commit/PR whenever the behavior or usage changes.
|
|
6
|
+
|
|
7
|
+
### When to update the README
|
|
8
|
+
|
|
9
|
+
- **Feature flags** — a flag is added, removed, renamed, or its default changes → update the "Feature flags" table.
|
|
10
|
+
- **Feature behavior or conditions** — a permission rule, a restriction (e.g. `hasSharedParent` / `hasSharedChild`), a link lifecycle step, or a gating condition changes → update the matching "Feature conditions" table.
|
|
11
|
+
- **Architecture** — the `ShareModal` routing, a new modal/dialog, or the `SharingProvider` context shape changes → update "Architecture".
|
|
12
|
+
|
|
13
|
+
### When NOT to update
|
|
14
|
+
|
|
15
|
+
- Pure refactors with no behavior change.
|
|
16
|
+
- Bug fixes that don't change documented behavior.
|
|
17
|
+
- Test-only or style-only changes.
|
|
18
|
+
|
|
19
|
+
### Rule of thumb
|
|
20
|
+
|
|
21
|
+
If the code and the README disagree after your change, the change is incomplete — fix the README before committing.
|
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,14 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [37.2.11](https://github.com/cozy/cozy-libs/compare/cozy-sharing@37.2.10...cozy-sharing@37.2.11) (2026-07-30)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package cozy-sharing
|
|
9
|
+
|
|
10
|
+
## [37.2.10](https://github.com/cozy/cozy-libs/compare/cozy-sharing@37.2.9...cozy-sharing@37.2.10) (2026-07-30)
|
|
11
|
+
|
|
12
|
+
**Note:** Version bump only for package cozy-sharing
|
|
13
|
+
|
|
6
14
|
## [37.2.9](https://github.com/cozy/cozy-libs/compare/cozy-sharing@37.2.8...cozy-sharing@37.2.9) (2026-07-29)
|
|
7
15
|
|
|
8
16
|
**Note:** Version bump only for package cozy-sharing
|
package/README.md
CHANGED
|
@@ -1,83 +1,230 @@
|
|
|
1
|
-
#
|
|
1
|
+
# cozy-sharing
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
React library that provides sharing UI and logic for Cozy / Twake Drive applications.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
It wraps the Cozy Stack sharing API and exposes ready-to-use components (modal, button, banners), hooks, and context for building sharing experiences.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
***
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
- import the stylesheet.css: `import 'cozy-sharing/dist/stylesheet.css'`
|
|
9
|
+
## Installation
|
|
11
10
|
|
|
12
|
-
|
|
11
|
+
```bash
|
|
12
|
+
yarn add cozy-sharing
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
In your app, you must:
|
|
16
|
+
|
|
17
|
+
- wrap the component tree with `SharingProvider`
|
|
18
|
+
- import the stylesheet: `import 'cozy-sharing/dist/stylesheet.css'`
|
|
19
|
+
|
|
20
|
+
***
|
|
13
21
|
|
|
14
|
-
|
|
22
|
+
## Usage
|
|
15
23
|
|
|
24
|
+
### Provider
|
|
25
|
+
|
|
26
|
+
```jsx
|
|
27
|
+
import SharingProvider from 'cozy-sharing'
|
|
28
|
+
|
|
29
|
+
<SharingProvider doctype="io.cozy.files" documentType="Files">
|
|
30
|
+
<App />
|
|
31
|
+
</SharingProvider>
|
|
16
32
|
```
|
|
33
|
+
|
|
34
|
+
**Props:**
|
|
35
|
+
|
|
36
|
+
| Prop | Required | Description |
|
|
37
|
+
|------|----------|-------------|
|
|
38
|
+
| `doctype` | yes | The cozy doctype (e.g. `io.cozy.files`) |
|
|
39
|
+
| `documentType` | no | Human-readable type for i18n keys (default `'Document'`) |
|
|
40
|
+
| `onShared` | no | Callback fired after a share is created |
|
|
41
|
+
| `isPublic` | no | Set to `true` when rendering a public page (disables fetching) |
|
|
42
|
+
| `previewPath` | no | Custom preview path sent to the share creation |
|
|
43
|
+
|
|
44
|
+
### Built-in components
|
|
45
|
+
|
|
46
|
+
Surrounding application code (imports, state, props) is omitted for brevity.
|
|
47
|
+
|
|
48
|
+
```jsx
|
|
17
49
|
import { ShareModal } from 'cozy-sharing'
|
|
18
50
|
|
|
19
51
|
const ToggleModal = () => {
|
|
20
|
-
const [
|
|
21
|
-
|
|
52
|
+
const [isOpen, setIsOpen] = useState(false)
|
|
22
53
|
return (
|
|
23
54
|
<div>
|
|
24
|
-
<Button onClick={() =>
|
|
25
|
-
{
|
|
55
|
+
<Button onClick={() => setIsOpen(true)}>Share</Button>
|
|
56
|
+
{isOpen && <ShareModal document={doc} onClose={() => setIsOpen(false)} />}
|
|
26
57
|
</div>
|
|
27
58
|
)
|
|
28
59
|
}
|
|
29
60
|
```
|
|
30
61
|
|
|
31
|
-
|
|
62
|
+
### Render-prop components
|
|
32
63
|
|
|
33
|
-
```
|
|
64
|
+
```jsx
|
|
34
65
|
import { SharedDocument } from 'cozy-sharing'
|
|
35
66
|
|
|
67
|
+
const MyComp = () => (
|
|
68
|
+
<SharedDocument docId="123">
|
|
69
|
+
{({ isShared, link }) => (isShared ? link : 'Not shared yet')}
|
|
70
|
+
</SharedDocument>
|
|
71
|
+
)
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Hooks
|
|
75
|
+
|
|
76
|
+
```jsx
|
|
77
|
+
import { useSharingContext } from 'cozy-sharing'
|
|
78
|
+
|
|
36
79
|
const MyComp = () => {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
{({ isShared, link }) => (
|
|
40
|
-
{isShared ? link : 'Not shared yet'}
|
|
41
|
-
)}
|
|
42
|
-
</SharedDocument>
|
|
43
|
-
)
|
|
80
|
+
const { share } = useSharingContext()
|
|
81
|
+
return <Button onClick={() => share({ document, recipients, ... })}>Share</Button>
|
|
44
82
|
}
|
|
45
83
|
```
|
|
46
84
|
|
|
47
|
-
|
|
85
|
+
***
|
|
48
86
|
|
|
49
|
-
|
|
87
|
+
## How it works
|
|
50
88
|
|
|
51
|
-
|
|
52
|
-
import { SharingContext } from 'cozy-sharing'
|
|
89
|
+
### Permission model
|
|
53
90
|
|
|
54
|
-
|
|
55
|
-
const { share } = useContext(SharingContext)
|
|
91
|
+
The UI exposes two roles, but the stack has more granular verb control:
|
|
56
92
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
93
|
+
| Role | Verbs | Description |
|
|
94
|
+
|------|-------|-------------|
|
|
95
|
+
| **Viewer** | `GET` | Read-only access |
|
|
96
|
+
| **Editor** | `GET, POST, PUT, PATCH` | Read + write + share management |
|
|
97
|
+
|
|
98
|
+
The `write` permission allows editing content, uploading files, deleting files (to the owner's trash), and managing share members (add/remove members, change permissions, view/manage link).
|
|
99
|
+
|
|
100
|
+
### Link sharing (public URL)
|
|
101
|
+
|
|
102
|
+
Creates a public URL accessible without a Twake account. One link per resource at any time.
|
|
103
|
+
|
|
104
|
+
**Options (set in `ShareRestrictionModal` or `ShareLinkAccessModal`):**
|
|
105
|
+
|
|
106
|
+
| Option | Default | Details |
|
|
107
|
+
|--------|---------|---------|
|
|
108
|
+
| Permission | `readOnly` | Can be changed to `write` |
|
|
109
|
+
| Expiration date | Off | Toggle; defaults to 30 days ahead when enabled; expires at end of that day |
|
|
110
|
+
| Password | Off | Minimum 4 characters; owner communicates it out-of-band |
|
|
111
|
+
|
|
112
|
+
**Lifecycle:**
|
|
113
|
+
|
|
114
|
+
- Deactivating a link revokes the current permissions. Reactivating generates a **new URL** — the old one stays invalid.
|
|
115
|
+
- Deleting the root resource revokes all associated shares (link and email).
|
|
116
|
+
- Link sharing coexists with email sharing on the same resource.
|
|
60
117
|
|
|
61
|
-
|
|
118
|
+
### Email sharing (cozy-to-cozy)
|
|
119
|
+
|
|
120
|
+
Invites specific users or groups with individualized permissions. Two technical modes:
|
|
121
|
+
|
|
122
|
+
- **Cozy-to-cozy sharing** (classic) — data is **replicated** to each recipient's Cozy.
|
|
123
|
+
- **Shared drives** (shared folders, team drives) — recipients access files at the **owner's instance** (no replication).
|
|
124
|
+
|
|
125
|
+
In both cases, content appears in the recipient's **Sharing tab**.
|
|
126
|
+
|
|
127
|
+
**By recipient status:**
|
|
128
|
+
|
|
129
|
+
| Recipient status | Behavior |
|
|
130
|
+
|-----------------|----------|
|
|
131
|
+
| Trusted contact (same org or previously accepted) | Content appears directly in the Sharing tab; notification sent |
|
|
132
|
+
| Has Twake account, not in contacts | Invitation email → login → Sharing tab |
|
|
133
|
+
| **No Twake account** | ❌ Not supported today |
|
|
134
|
+
| **Different SSO** | ❌ Not supported today |
|
|
135
|
+
| Not trusted (guest in org) | ❌ Only if added manually; not automated today |
|
|
136
|
+
|
|
137
|
+
### Cohabitation
|
|
138
|
+
|
|
139
|
+
Link and email sharing can coexist on the same resource. The recipient's permission is the most permissive when both apply.
|
|
140
|
+
|
|
141
|
+
***
|
|
142
|
+
|
|
143
|
+
## Feature conditions
|
|
144
|
+
|
|
145
|
+
### Link sharing
|
|
146
|
+
|
|
147
|
+
| Condition | Rule | Source |
|
|
148
|
+
|-----------|------|--------|
|
|
149
|
+
| Who can create/manage | Owner or Editor (`canReshare` = owner, or `open_sharing && !read_only`) | `canReshare()` in `state.js` |
|
|
150
|
+
| Document type restriction | Hidden when `documentType === 'Organizations'` | `ShareModal.jsx:33` |
|
|
151
|
+
| Albums | Link-only, read-only permissions only | `ShareModal.jsx:30`, `link.js:3` |
|
|
152
|
+
| Editing rights box | Hidden when `drive.federated-shared-folder.enabled` flag is true | `BoxEditingRights.jsx:54` |
|
|
153
|
+
| Expiration date toggle default | Controlled by `sharing.date-toggle.enabled` flag (default: false) | `ShareRestrictionModal.jsx:62` |
|
|
154
|
+
| Link deactivation | Revokes the permission document; regenerates a new URL on reactivation | `SharingProvider.jsx:635` |
|
|
155
|
+
| Password | 4+ characters; no limit on attempts | `ShareLinkSettings.jsx:10` |
|
|
156
|
+
| Guest access | Via `/public` URL with `sharecode`; no auth required | `helpers.js:106` |
|
|
157
|
+
|
|
158
|
+
### Email sharing
|
|
159
|
+
|
|
160
|
+
| Condition | Rule | Source |
|
|
161
|
+
|-----------|------|--------|
|
|
162
|
+
| Who can share | Owner or Editor (`canReshare`) | `SharingProvider.jsx` |
|
|
163
|
+
| **Core constraint** | Disabled if `hasSharedParent` OR `hasSharedChild` — a subfolder of an email-shared folder cannot be shared by email independently | `ShareModal.jsx:32`, `state.js:508-523` |
|
|
164
|
+
| Recipients limit | Default 100; overridable via `sharing.recipients-limit` flag | `helpers/recipients.js:33` |
|
|
165
|
+
| Recipient display mode | `sharing.show-recipient-groups` flag: `true` = groups as units, `false` = spread group members | `ShareByEmail.jsx:31` |
|
|
166
|
+
| Read-only sharing | If the existing sharing is read-only, only the `readOnly` option is offered | `ShareByEmail.jsx:46-57` |
|
|
167
|
+
| Contacts shown | Only contacts with a defined `email` or `cozy` URL | `helpers/recipients.js:72-73` |
|
|
168
|
+
| Group sharing | All members get the same permission; dynamic membership (add/remove) propagates | `state.js:414-451` |
|
|
169
|
+
|
|
170
|
+
### Shared drives & federated folders
|
|
171
|
+
|
|
172
|
+
| Condition | Rule | Source |
|
|
173
|
+
|-----------|------|--------|
|
|
174
|
+
| Org shared drive | `isOrgSharedDrive` = `sharing.drive === true && sharing.org_drive === true` | `state.js:324` |
|
|
175
|
+
| `canLeave` | `false` for org shared drives | `state.js:335` |
|
|
176
|
+
| `canReshare` | `!org_drive && !read_only` for drives; `open_sharing && !read_only` for folders | `state.js:342` |
|
|
177
|
+
| Federated folder modal | Enabled by `drive.federated-shared-folder.enabled` flag | `ShareModal.jsx:39` |
|
|
178
|
+
| Email sharing in federated folder | Disabled for files inside a federated shared folder (has `driveId` but is not the root) | `FederatedFolderModal.jsx:122-126` |
|
|
179
|
+
| Link sharing in federated folder | Uses `getFederatedShareLink` which resolves the owner's instance URL | `FederatedFolderModal.jsx:108-110` |
|
|
180
|
+
|
|
181
|
+
### Share management
|
|
182
|
+
|
|
183
|
+
| Condition | Rule | Source |
|
|
184
|
+
|-----------|------|--------|
|
|
185
|
+
| Editable modal | `isEditable = !byDocId[doc] \|\| isOwner(doc) \|\| canReshare(doc)` | `ShareModal.jsx:35-36` |
|
|
186
|
+
| Non-editable view | `SharingDetailsModal` — read-only view of members, with revoke-self | `ShareModal.jsx:53-62` |
|
|
187
|
+
| Updating member type | `updateSharingMemberType` calls `setReadOnly` / `setReadWrite` on the stack | `SharingProvider.jsx:361-420` |
|
|
188
|
+
| Revoke self | Available to all recipients | `SharingProvider.jsx:349-353` |
|
|
189
|
+
| Revoke group | Available to owner/editor; removes all group members at once | `SharingProvider.jsx:336-347` |
|
|
190
|
+
|
|
191
|
+
### Native mobile sharing
|
|
192
|
+
|
|
193
|
+
| Condition | Rule | Source |
|
|
194
|
+
|-----------|------|--------|
|
|
195
|
+
| Availability | Only in flagship app (`isFlagshipApp()`) when `shareFiles` intent is available | `NativeFileSharingProvider.jsx:26-27` |
|
|
196
|
+
| Restriction | Files only (not directories) | `shareNative.js:42` |
|
|
197
|
+
|
|
198
|
+
***
|
|
199
|
+
|
|
200
|
+
## Feature flags
|
|
201
|
+
|
|
202
|
+
| Flag | Default | Effect |
|
|
203
|
+
|------|---------|--------|
|
|
204
|
+
| `cozy.hide-sharing-cozy-to-cozy` | `false` | When `true`, hides cozy-to-cozy sharing entirely; only link sharing is available |
|
|
205
|
+
| `drive.federated-shared-folder.enabled` | `false` | Enables federated folder modal (shared drives across instances) |
|
|
206
|
+
| `sharing.date-toggle.enabled` | `false` | Default state of the expiration date toggle when creating a new link |
|
|
207
|
+
| `sharing.show-recipient-groups` | `false` | When `true`, groups are shown as distinct recipients; when `false`, group members are spread |
|
|
208
|
+
| `sharing.recipients-limit` | `100` | Maximum number of recipients per document |
|
|
209
|
+
| `signup-url` / `signup.url` | `https://sign-up.twake.app` | URL used for the sharing banner "create account" call-to-action |
|
|
210
|
+
|
|
211
|
+
## Development
|
|
212
|
+
|
|
213
|
+
### Share and send mail in development
|
|
62
214
|
|
|
63
215
|
Cozy apps let users [share documents from cozy to cozy](https://github.com/cozy/cozy-stack/blob/master/docs/sharing.md#cozy-to-cozy-sharing).
|
|
64
216
|
|
|
65
|
-
Meet Alice and Bob.
|
|
66
|
-
Alice wants to share a folder with Bob.
|
|
67
|
-
Alice clicks on the share button and fills in the email input with Bob's email address.
|
|
68
|
-
Bob receives an email with a *« Accept the sharing »* button.
|
|
69
|
-
Bob clicks on that button and is redirected to Alice's cozy to enter his own cozy url to link both cozys.
|
|
70
|
-
Bob sees Alice's shared folder in his own cozy.
|
|
217
|
+
Meet Alice and Bob. Alice wants to share a folder with Bob. Alice clicks on the share button and fills in the email input with Bob's email address. Bob receives an email with a *"Accept the sharing"* button. Bob clicks on that button and is redirected to Alice's cozy to enter his own cozy url to link both cozys. Bob sees Alice's shared folder in his own cozy.
|
|
71
218
|
|
|
72
|
-
|
|
219
|
+
But how could we do this scenario on development environment?
|
|
73
220
|
|
|
74
|
-
|
|
221
|
+
#### With the docker image
|
|
75
222
|
|
|
76
223
|
If you develop with the [cozy-app-dev docker image](https://github.com/cozy/cozy-stack/blob/master/docs/client-app-dev.md#with-docker), [MailHog](https://github.com/mailhog/MailHog) is running inside it to catch emails.
|
|
77
224
|
|
|
78
225
|
If cozy-stack has to send an email, MailHog catches it and exposes it on its web interface on <http://cozy.tools:8025/>.
|
|
79
226
|
|
|
80
|
-
|
|
227
|
+
#### With the binary cozy-stack
|
|
81
228
|
|
|
82
229
|
If you develop with the [cozy-stack CLI](https://github.com/cozy/cozy-stack/blob/master/docs/cli/cozy-stack.md), you have to run [MailHog](https://github.com/mailhog/MailHog) on your computer and tell `cozy-stack serve` where to find the mail server with some [options](https://github.com/cozy/cozy-stack/blob/master/docs/cli/cozy-stack_serve.md#options):
|
|
83
230
|
|
|
@@ -89,6 +236,19 @@ If you develop with the [cozy-stack CLI](https://github.com/cozy/cozy-stack/blob
|
|
|
89
236
|
|
|
90
237
|
Then simply run `mailhog` and open <http://cozy.tools:8025/>.
|
|
91
238
|
|
|
92
|
-
|
|
239
|
+
#### Retrieve sent emails
|
|
93
240
|
|
|
94
241
|
With MailHog, **every email** sent by cozy-stack is caught. That means the email address *does not have to be a real one*, ie. `bob@cozy`, `bob@cozy.tools` are perfectly fine. It *could be a real one*, but the email will not reach the real recipient's inbox, say `contact@cozycloud.cc`.
|
|
242
|
+
|
|
243
|
+
***
|
|
244
|
+
|
|
245
|
+
## Architecture
|
|
246
|
+
|
|
247
|
+
The share modal's component tree is documented in [`docs/share-modal-architecture.md`](docs/share-modal-architecture.md).
|
|
248
|
+
|
|
249
|
+
Key decisions:
|
|
250
|
+
|
|
251
|
+
- `ShareModal` checks `isEditable` → `EditableSharingModal` (editable) or `SharingDetailsModal` (read-only).
|
|
252
|
+
- `EditableSharingModal` renders `ShareModal` (the dumb component), which decides `ShareDialogCozyToCozy` vs `ShareDialogOnlyByLink` based on flags and context.
|
|
253
|
+
- `ShareDialogTwoStepsConfirmationContainer` wraps the cozy-to-cozy dialog when recipients need confirmation (untrusted contacts).
|
|
254
|
+
- Federated mode: when `drive.federated-shared-folder.enabled` is true, `FederatedFolderModal` replaces the standard `EditableSharingModal`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cozy-sharing",
|
|
3
|
-
"version": "37.2.
|
|
3
|
+
"version": "37.2.11",
|
|
4
4
|
"description": "Provides sharing login for React applications.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"author": "Cozy",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"cozy-intent": "^2.31.1",
|
|
60
60
|
"cozy-minilog": "^3.10.1",
|
|
61
61
|
"cozy-ui": "^140.5.0",
|
|
62
|
-
"cozy-ui-plus": "^12.3.
|
|
62
|
+
"cozy-ui-plus": "^12.3.5",
|
|
63
63
|
"jest": "30.3.0",
|
|
64
64
|
"jest-environment-jsdom": "30.3.0",
|
|
65
65
|
"react": "16.12.0",
|
|
@@ -85,5 +85,5 @@
|
|
|
85
85
|
"sideEffects": [
|
|
86
86
|
"*.css"
|
|
87
87
|
],
|
|
88
|
-
"gitHead": "
|
|
88
|
+
"gitHead": "163abe4aea6ccd37e5b6a256f30b081be81c0661"
|
|
89
89
|
}
|