Skip to content

Commit ddbb981

Browse files
authored
Merge pull request #3087 from cozy/notes-archives
Add images to notes
2 parents 365ef02 + db8e0df commit ddbb981

File tree

24 files changed

+1087
-110
lines changed

24 files changed

+1087
-110
lines changed

docs/files.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,8 @@ more informations about the `not_synchronized_on` field.
125125
### GET /files/:file-id
126126

127127
Get a directory or a file informations. In the case of a directory, it contains
128-
the list of files and sub-directories inside it.
128+
the list of files and sub-directories inside it. For a note, its images are
129+
included.
129130

130131
Contents is paginated following [jsonapi conventions](jsonapi.md#pagination).
131132
The default limit is 30 entries.

docs/notes.md

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1140,12 +1140,79 @@ Content-Type: application/vnd.api+json
11401140
}
11411141
```
11421142

1143+
### POST /notes/:id/images
1144+
1145+
This route can be used to upload an image for a note. The note will be
1146+
transformed in a tar archive in the VFS, with the image saved inside it.
1147+
1148+
This route can only be used to upload images (the content-type is checked) and
1149+
requires a POST permission on the note.
1150+
1151+
The filename of the image is given in the query string, via the `Name`
1152+
parameter. In case of conflict (another image has the same name), the stack
1153+
will rename this image.
1154+
1155+
If the image is larger than 768px, a thumbnail will be generated.
1156+
1157+
#### Request
1158+
1159+
```http
1160+
POST /notes/f48d9370-e1ec-0137-8547-543d7eb8149c/images?Name=diagram.jpg HTTP/1.1
1161+
Accept: application/vnd.api+json
1162+
Content-Length: 123456
1163+
Content-Type: image/jpeg
1164+
Host: cozy.example.com
1165+
<content>
1166+
```
1167+
1168+
#### Response
1169+
1170+
```http
1171+
HTTP/1.1 201 Created
1172+
Content-Type: application/vnd.api+json
1173+
```
1174+
1175+
```json
1176+
{
1177+
"data": {
1178+
"type": "io.cozy.notes.images",
1179+
"id": "f48d9370-e1ec-0137-8547-543d7eb8149c/e57d2ec0-d281-0139-2bed-543d7eb8149c",
1180+
"meta": {
1181+
"rev": "1-588ab661"
1182+
},
1183+
"attributes": {
1184+
"name": "diagram.jpg",
1185+
"mime": "image/jpeg",
1186+
"width": 1000,
1187+
"height": 1000,
1188+
"willBeResized": true,
1189+
"cozyMetadata": {
1190+
"doctypeVersion": "1",
1191+
"metadataVersion": 1,
1192+
"createdAt": "2021-07-12T10:58:00Z",
1193+
"createdByApp": "notes",
1194+
"createdOn": "https://cozy.example.com/",
1195+
"updatedAt": "2021-07-12T10:58:00Z",
1196+
"uploadedAt": "2021-07-12T10:58:00Z",
1197+
"uploadedOn": "https://cozy.example.com/",
1198+
"uploadedBy": {
1199+
"slug": "notes"
1200+
}
1201+
}
1202+
},
1203+
"links": {
1204+
"self": "/notes/f48d9370-e1ec-0137-8547-543d7eb8149c/images/e57d2ec0-d281-0139-2bed-543d7eb8149c/543d7eb8149c128b"
1205+
}
1206+
}
1207+
}
1208+
```
1209+
11431210
## Real-time via websockets
11441211

11451212
You can subscribe to the [realtime](realtime.md) API for a document with the
11461213
`io.cozy.notes.events` doctype, and the id of a note file. It requires a permission
11471214
on this file, and it will send the events for this notes: changes of the title, the
1148-
steps applied, and the telepointer updates.
1215+
steps applied, the telepointer updates, and images processed.
11491216

11501217
### Example
11511218

@@ -1175,4 +1242,12 @@ server > {"event": "UPDATED",
11751242
"payload": {"id": "f48d9370-e1ec-0137-8547-543d7eb8149c",
11761243
"type": "io.cozy.notes.events",
11771244
"doc": {"doctype": "io.cozy.notes.telepointers", "sessionID": "543781490137", "anchor": 7, "head": 12, "type": "textSelection"}}}
1245+
server > {"event": "UPDATED",
1246+
"payload": {"id": "f48d9370-e1ec-0137-8547-543d7eb8149c",
1247+
"type": "io.cozy.notes.events",
1248+
"doc": {"doctype": "io.cozy.notes.images",
1249+
"image_id": "e57d2ec0-d281-0139-2bed-543d7eb8149c",
1250+
"mime": "image/jpeg",
1251+
"width": 768,
1252+
"height": 768}}}
11781253
```

model/instance/instance.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"net/http"
77
"net/url"
8+
"path"
89
"strings"
910
"time"
1011

@@ -21,6 +22,7 @@ import (
2122
"github.com/cozy/cozy-stack/pkg/jsonapi"
2223
"github.com/cozy/cozy-stack/pkg/lock"
2324
"github.com/cozy/cozy-stack/pkg/logger"
25+
"github.com/spf13/afero"
2426

2527
"github.com/sirupsen/logrus"
2628
jwt "gopkg.in/dgrijalva/jwt-go.v3"
@@ -225,6 +227,34 @@ func (i *Instance) MakeVFS() error {
225227
return err
226228
}
227229

230+
// ThumbsFS returns the hidden filesystem for storing the thumbnails of the
231+
// photos/image
232+
func (i *Instance) ThumbsFS() vfs.Thumbser {
233+
fsURL := config.FsURL()
234+
switch fsURL.Scheme {
235+
case config.SchemeFile:
236+
baseFS := afero.NewBasePathFs(afero.NewOsFs(),
237+
path.Join(fsURL.Path, i.DirName(), vfs.ThumbsDirName))
238+
return vfsafero.NewThumbsFs(baseFS)
239+
case config.SchemeMem:
240+
baseFS := vfsafero.GetMemFS(i.DomainName() + "-thumbs")
241+
return vfsafero.NewThumbsFs(baseFS)
242+
case config.SchemeSwift, config.SchemeSwiftSecure:
243+
switch i.SwiftLayout {
244+
case 0:
245+
return vfsswift.NewThumbsFs(config.GetSwiftConnection(), i.Domain)
246+
case 1:
247+
return vfsswift.NewThumbsFsV2(config.GetSwiftConnection(), i)
248+
case 2:
249+
return vfsswift.NewThumbsFsV3(config.GetSwiftConnection(), i)
250+
default:
251+
panic(ErrInvalidSwiftLayout)
252+
}
253+
default:
254+
panic(fmt.Sprintf("instance: unknown storage provider %s", fsURL.Scheme))
255+
}
256+
}
257+
228258
// NotesLock returns a mutex for the notes on this instance.
229259
func (i *Instance) NotesLock() lock.ErrorRWLocker {
230260
return lock.ReadWrite(i, "notes")

model/instance/lifecycle/thumbs.go

Lines changed: 0 additions & 41 deletions
This file was deleted.

model/note/delete.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package note
2+
3+
import (
4+
"github.com/cozy/cozy-stack/model/instance"
5+
"github.com/cozy/cozy-stack/model/vfs"
6+
"github.com/cozy/cozy-stack/pkg/consts"
7+
"github.com/cozy/cozy-stack/pkg/couchdb"
8+
"github.com/cozy/cozy-stack/pkg/prefixer"
9+
)
10+
11+
func init() {
12+
vfs.DeleteNote = deleteNote
13+
}
14+
15+
func deleteNote(db prefixer.Prefixer, noteID string) {
16+
go func() {
17+
images, err := getImages(db, noteID)
18+
if err == nil {
19+
formats := []string{
20+
consts.NoteImageOriginalFormat,
21+
consts.NoteImageThumbFormat,
22+
}
23+
for _, img := range images {
24+
inst := &instance.Instance{
25+
Domain: db.DomainName(),
26+
Prefix: db.DBPrefix(),
27+
}
28+
_ = inst.ThumbsFS().RemoveNoteThumb(img.ID(), formats)
29+
_ = couchdb.DeleteDoc(db, img)
30+
}
31+
}
32+
33+
steps, err := getSteps(db, noteID, 0)
34+
if err == nil && len(steps) > 0 {
35+
docs := make([]couchdb.Doc, 0, len(steps))
36+
for i := range steps {
37+
docs = append(docs, &steps[i])
38+
}
39+
_ = couchdb.BulkDeleteDocs(db, consts.NotesSteps, docs)
40+
}
41+
}()
42+
}

model/note/event.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package note
22

33
import (
4+
"strings"
5+
46
"github.com/cozy/cozy-stack/model/instance"
57
"github.com/cozy/cozy-stack/pkg/consts"
68
"github.com/cozy/cozy-stack/pkg/couchdb"
@@ -96,4 +98,12 @@ func publishSteps(inst *instance.Instance, fileID string, steps []Step) {
9698
}
9799
}
98100

101+
// PublishThumbnail sends information about a resized image.
102+
func PublishThumbnail(inst *instance.Instance, event Event) {
103+
parts := strings.SplitN(event.ID(), "/", 2)
104+
event.SetID(parts[0]) // The note ID is the first part of the image ID
105+
event["image_id"] = parts[1] // The image ID is the second part
106+
event.publish(inst)
107+
}
108+
99109
var _ jsonapi.Object = &Event{}

0 commit comments

Comments
 (0)