From bac0f58b568d09ac79b6372b40cb6cf038f63892 Mon Sep 17 00:00:00 2001 From: Bruno Michel Date: Tue, 25 Jun 2024 17:02:50 +0200 Subject: [PATCH 1/5] Add a route to allow listing the trash bin on NextCloud --- docs/nextcloud.md | 41 +++++++++++++++++++++++ model/nextcloud/nextcloud.go | 63 +++++++++++++++++++++++++++++++----- pkg/webdav/webdav.go | 4 +++ web/remote/nextcloud.go | 23 ++++++++++++- 4 files changed, 122 insertions(+), 9 deletions(-) diff --git a/docs/nextcloud.md b/docs/nextcloud.md index 95bc9cbdabd..398265e26da 100644 --- a/docs/nextcloud.md +++ b/docs/nextcloud.md @@ -430,3 +430,44 @@ HTTP/1.1 204 No Content - 400 Bad Request, when the account is not configured for NextCloud - 401 Unauthorized, when authentication to the NextCloud fails - 404 Not Found, when the account is not found or the file is not found on the Cozy + +## GET /remote/nextcloud/:account/trash/* + +This route can be used to list the files and directories inside the trashbin +of NextCloud. + +### Request (list) + +```http +GET /remote/nextcloud/4ab2155707bb6613a8b9463daf00381b/trash/ HTTP/1.1 +Host: cozy.example.net +Authorization: Bearer eyJhbG... +``` + +### Response (list) + +```http +HTTP/1.1 200 OK +Content-Type: application/vnd.api+json +``` + +```json +{ + "data": [ + { + "type": "io.cozy.remote.nextcloud.files", + "id": "613281", + "attributes": { + "type": "directory", + "name": "Old", + "updated_at": "Tue, 25 Jun 2024 14:31:44 GMT", + "etag": "1719326384" + }, + "meta": {}, + "links": { + "self": "https://nextcloud.example.net/apps/files/trashbin/613281?dir=/Old" + } + } + ] +} +``` diff --git a/model/nextcloud/nextcloud.go b/model/nextcloud/nextcloud.go index f3446b42c0c..e9c1220837e 100644 --- a/model/nextcloud/nextcloud.go +++ b/model/nextcloud/nextcloud.go @@ -62,6 +62,7 @@ var _ jsonapi.Object = (*File)(nil) type NextCloud struct { inst *instance.Instance accountID string + userID string webdav *webdav.Client } @@ -99,6 +100,7 @@ func New(inst *instance.Instance, accountID string) (*NextCloud, error) { Host: u.Host, Username: username, Password: password, + BasePath: "/remote.php/dav", Logger: logger, } nc := &NextCloud{ @@ -106,41 +108,46 @@ func New(inst *instance.Instance, accountID string) (*NextCloud, error) { accountID: accountID, webdav: webdav, } - if err := nc.fillBasePath(&doc); err != nil { + if err := nc.fillUserID(&doc); err != nil { return nil, err } return nc, nil } func (nc *NextCloud) Download(path string) (*webdav.Download, error) { - return nc.webdav.Get(path) + return nc.webdav.Get("/files/" + nc.userID + "/" + path) } func (nc *NextCloud) Upload(path, mime string, contentLength int64, body io.Reader) error { headers := map[string]string{ echo.HeaderContentType: mime, } + path = "/files/" + nc.userID + "/" + path return nc.webdav.Put(path, contentLength, headers, body) } func (nc *NextCloud) Mkdir(path string) error { - return nc.webdav.Mkcol(path) + return nc.webdav.Mkcol("/files/" + nc.userID + "/" + path) } func (nc *NextCloud) Delete(path string) error { - return nc.webdav.Delete(path) + return nc.webdav.Delete("/files/" + nc.userID + "/" + path) } func (nc *NextCloud) Move(oldPath, newPath string) error { + oldPath = "/files/" + nc.userID + "/" + oldPath + newPath = "/files/" + nc.userID + "/" + newPath return nc.webdav.Move(oldPath, newPath) } func (nc *NextCloud) Copy(oldPath, newPath string) error { + oldPath = "/files/" + nc.userID + "/" + oldPath + newPath = "/files/" + nc.userID + "/" + newPath return nc.webdav.Copy(oldPath, newPath) } func (nc *NextCloud) ListFiles(path string) ([]jsonapi.Object, error) { - items, err := nc.webdav.List(path) + items, err := nc.webdav.List("/files/" + nc.userID + "/" + path) if err != nil { return nil, err } @@ -167,7 +174,36 @@ func (nc *NextCloud) ListFiles(path string) ([]jsonapi.Object, error) { return files, nil } +func (nc *NextCloud) ListTrash(path string) ([]jsonapi.Object, error) { + items, err := nc.webdav.List("/trashbin/" + nc.userID + "/trash/" + path) + if err != nil { + return nil, err + } + + var files []jsonapi.Object + for _, item := range items { + var mime, class string + if item.Type == "file" { + mime, class = vfs.ExtractMimeAndClassFromFilename(item.TrashName) + } + file := &File{ + DocID: item.ID, + Type: item.Type, + Name: item.TrashName, + Size: item.Size, + Mime: mime, + Class: class, + UpdatedAt: item.LastModified, + ETag: item.ETag, + url: nc.buildTrashURL(item, path), + } + files = append(files, file) + } + return files, nil +} + func (nc *NextCloud) Downstream(path, dirID string, kind OperationKind, cozyMetadata *vfs.FilesCozyMetadata) (*vfs.FileDoc, error) { + path = "/files/" + nc.userID + "/" + path dl, err := nc.webdav.Get(path) if err != nil { return nil, err @@ -215,6 +251,7 @@ func (nc *NextCloud) Downstream(path, dirID string, kind OperationKind, cozyMeta } func (nc *NextCloud) Upstream(path, from string, kind OperationKind) error { + path = "/files/" + nc.userID + "/" + path fs := nc.inst.VFS() doc, err := fs.FileByID(from) if err != nil { @@ -238,10 +275,10 @@ func (nc *NextCloud) Upstream(path, from string, kind OperationKind) error { return nil } -func (nc *NextCloud) fillBasePath(accountDoc *couchdb.JSONDoc) error { +func (nc *NextCloud) fillUserID(accountDoc *couchdb.JSONDoc) error { userID, _ := accountDoc.M["webdav_user_id"].(string) if userID != "" { - nc.webdav.BasePath = "/remote.php/dav/files/" + userID + nc.userID = userID return nil } @@ -249,7 +286,7 @@ func (nc *NextCloud) fillBasePath(accountDoc *couchdb.JSONDoc) error { if err != nil { return err } - nc.webdav.BasePath = "/remote.php/dav/files/" + userID + nc.userID = userID // Try to persist the userID to avoid fetching it for every WebDAV request accountDoc.M["webdav_user_id"] = userID @@ -269,6 +306,16 @@ func (nc *NextCloud) buildURL(item webdav.Item, path string) string { return u.String() } +func (nc *NextCloud) buildTrashURL(item webdav.Item, path string) string { + u := &url.URL{ + Scheme: nc.webdav.Scheme, + Host: nc.webdav.Host, + Path: "/apps/files/trashbin/" + item.ID, + RawQuery: "dir=/" + path, + } + return u.String() +} + // https://docs.nextcloud.com/server/latest/developer_manual/client_apis/OCS/ocs-status-api.html#fetch-your-own-status func (nc *NextCloud) fetchUserID() (string, error) { logger := nc.webdav.Logger diff --git a/pkg/webdav/webdav.go b/pkg/webdav/webdav.go index 5b14573b073..d1c1329c123 100644 --- a/pkg/webdav/webdav.go +++ b/pkg/webdav/webdav.go @@ -243,6 +243,7 @@ func (c *Client) List(path string) ([]Item, error) { ID: props.FileID, Type: "directory", Name: props.Name, + TrashName: props.TrashName, LastModified: props.LastModified, ETag: props.ETag, } @@ -264,6 +265,7 @@ type Item struct { ID string Type string Name string + TrashName string Size uint64 ContentType string LastModified string @@ -284,6 +286,7 @@ type props struct { Status string `xml:"status"` Type xml.Name `xml:"prop>resourcetype>collection"` Name string `xml:"prop>displayname"` + TrashName string `xml:"prop>trashbin-filename"` Size string `xml:"prop>getcontentlength"` ContentType string `xml:"prop>getcontenttype"` LastModified string `xml:"prop>getlastmodified"` @@ -301,6 +304,7 @@ const ListFilesPayload = ` + ` diff --git a/web/remote/nextcloud.go b/web/remote/nextcloud.go index 64a6013f402..80d19428480 100644 --- a/web/remote/nextcloud.go +++ b/web/remote/nextcloud.go @@ -23,9 +23,29 @@ import ( "github.com/ncw/swift/v2" ) +func nextcloudGetTrash(c echo.Context) error { + inst := middlewares.GetInstance(c) + if err := middlewares.AllowWholeType(c, permission.GET, consts.Files); err != nil { + return err + } + + accountID := c.Param("account") + nc, err := nextcloud.New(inst, accountID) + if err != nil { + return wrapNextcloudErrors(err) + } + + path := c.Param("*") + files, err := nc.ListTrash(path) + if err != nil { + return wrapNextcloudErrors(err) + } + return jsonapi.DataList(c, http.StatusOK, files, nil) +} + func nextcloudGet(c echo.Context) error { inst := middlewares.GetInstance(c) - if err := middlewares.AllowWholeType(c, permission.PUT, consts.Files); err != nil { + if err := middlewares.AllowWholeType(c, permission.GET, consts.Files); err != nil { return err } @@ -247,6 +267,7 @@ func nextcloudUpstream(c echo.Context) error { func nextcloudRoutes(router *echo.Group) { group := router.Group("/nextcloud/:account") + group.GET("/trash/*", nextcloudGetTrash) group.GET("/*", nextcloudGet) group.PUT("/*", nextcloudPut) group.DELETE("/*", nextcloudDelete) From f1974c0505119e8eb4d15acdcfbb694221d6fa9a Mon Sep 17 00:00:00 2001 From: Bruno Michel Date: Wed, 26 Jun 2024 11:18:04 +0200 Subject: [PATCH 2/5] Add a route to restore from the trash bin on NextCloud --- docs/nextcloud.md | 39 ++++++++++++++++++++++++++++++++++++ model/nextcloud/nextcloud.go | 6 ++++++ pkg/webdav/webdav.go | 4 +--- web/remote/nextcloud.go | 20 ++++++++++++++++++ 4 files changed, 66 insertions(+), 3 deletions(-) diff --git a/docs/nextcloud.md b/docs/nextcloud.md index 398265e26da..dd8f92ec8f7 100644 --- a/docs/nextcloud.md +++ b/docs/nextcloud.md @@ -471,3 +471,42 @@ Content-Type: application/vnd.api+json ] } ``` + +#### Status codes + +- 200 OK, for a success +- 401 Unauthorized, when authentication to the NextCloud fails +- 404 Not Found, when the account is not found or the directory is not found on the NextCloud + +## POST /remote/nextcloud/:account/restore/*path + +This route can be used to restore a file/directory from the trashbin on the +NextCloud. + +The `:account` parameter is the identifier of the NextCloud `io.cozy.account`. + +The `*path` parameter is the path of the file on the NextCloud. + +**Note:** a permission on `POST io.cozy.files` is required to use this route. + +### Request + +```http +POST /remote/nextcloud/4ab2155707bb6613a8b9463daf00381b/restore/Old HTTP/1.1 +Host: cozy.example.net +Authorization: Bearer eyJhbG... +``` + +### Response + +```http +HTTP/1.1 204 No Content +``` + +#### Status codes + +- 204 No Content, when the file/directory has been restored +- 400 Bad Request, when the account is not configured for NextCloud +- 401 Unauthorized, when authentication to the NextCloud fails +- 404 Not Found, when the account is not found or the file/directory is not found on the NextCloud +- 409 Conflict, when a directory or file already exists where the file/directory should be restored on the NextCloud. diff --git a/model/nextcloud/nextcloud.go b/model/nextcloud/nextcloud.go index e9c1220837e..b0df34e3185 100644 --- a/model/nextcloud/nextcloud.go +++ b/model/nextcloud/nextcloud.go @@ -146,6 +146,12 @@ func (nc *NextCloud) Copy(oldPath, newPath string) error { return nc.webdav.Copy(oldPath, newPath) } +func (nc *NextCloud) Restore(path string) error { + path = "/trashbin/" + nc.userID + "/" + path + dst := "/trashbin/" + nc.userID + "/restore/" + filepath.Base(path) + return nc.webdav.Move(path, dst) +} + func (nc *NextCloud) ListFiles(path string) ([]jsonapi.Object, error) { items, err := nc.webdav.List("/files/" + nc.userID + "/" + path) if err != nil { diff --git a/pkg/webdav/webdav.go b/pkg/webdav/webdav.go index d1c1329c123..753169fe108 100644 --- a/pkg/webdav/webdav.go +++ b/pkg/webdav/webdav.go @@ -68,12 +68,11 @@ func (c *Client) Move(oldPath, newPath string) error { u := url.URL{ Scheme: c.Scheme, Host: c.Host, - User: url.UserPassword(c.Username, c.Password), Path: c.BasePath + fixSlashes(newPath), } headers := map[string]string{ "Destination": u.String(), - "Overwrite": "F", + "Overwrite": "T", } res, err := c.req("MOVE", oldPath, 0, headers, nil) if err != nil { @@ -98,7 +97,6 @@ func (c *Client) Copy(oldPath, newPath string) error { u := url.URL{ Scheme: c.Scheme, Host: c.Host, - User: url.UserPassword(c.Username, c.Password), Path: c.BasePath + fixSlashes(newPath), } headers := map[string]string{ diff --git a/web/remote/nextcloud.go b/web/remote/nextcloud.go index 80d19428480..2b46a33fd03 100644 --- a/web/remote/nextcloud.go +++ b/web/remote/nextcloud.go @@ -265,6 +265,25 @@ func nextcloudUpstream(c echo.Context) error { return c.NoContent(http.StatusNoContent) } +func nextcloudRestore(c echo.Context) error { + inst := middlewares.GetInstance(c) + if err := middlewares.AllowWholeType(c, permission.POST, consts.Files); err != nil { + return err + } + + accountID := c.Param("account") + nc, err := nextcloud.New(inst, accountID) + if err != nil { + return wrapNextcloudErrors(err) + } + + path := c.Param("*") + if err := nc.Restore(path); err != nil { + return wrapNextcloudErrors(err) + } + return c.NoContent(http.StatusNoContent) +} + func nextcloudRoutes(router *echo.Group) { group := router.Group("/nextcloud/:account") group.GET("/trash/*", nextcloudGetTrash) @@ -275,6 +294,7 @@ func nextcloudRoutes(router *echo.Group) { group.POST("/copy/*", nextcloudCopy) group.POST("/downstream/*", nextcloudDownstream) group.POST("/upstream/*", nextcloudUpstream) + group.POST("/restore/*", nextcloudRestore) } func wrapNextcloudErrors(err error) error { From d3419daeb76d4ff2e4d1e31fad3f92128b77661d Mon Sep 17 00:00:00 2001 From: Bruno Michel Date: Wed, 26 Jun 2024 17:24:45 +0200 Subject: [PATCH 3/5] Add a route to empty the trash bin on NextCloud --- docs/nextcloud.md | 18 ++++++++++++++++++ model/nextcloud/nextcloud.go | 4 ++++ web/remote/nextcloud.go | 19 +++++++++++++++++++ 3 files changed, 41 insertions(+) diff --git a/docs/nextcloud.md b/docs/nextcloud.md index dd8f92ec8f7..ac3080d4ae0 100644 --- a/docs/nextcloud.md +++ b/docs/nextcloud.md @@ -510,3 +510,21 @@ HTTP/1.1 204 No Content - 401 Unauthorized, when authentication to the NextCloud fails - 404 Not Found, when the account is not found or the file/directory is not found on the NextCloud - 409 Conflict, when a directory or file already exists where the file/directory should be restored on the NextCloud. + +## DELETE /remote/nextcloud/:account/trash + +This route can be used to empty the trash bin on NextCloud. + +### Request + +```http +DELETE /remote/nextcloud/4ab2155707bb6613a8b9463daf00381b/trash HTTP/1.1 +Host: cozy.example.net +Authorization: Bearer eyJhbG... +``` + +### Response + +```http +HTTP/1.1 204 No Content +``` diff --git a/model/nextcloud/nextcloud.go b/model/nextcloud/nextcloud.go index b0df34e3185..6f2531617cf 100644 --- a/model/nextcloud/nextcloud.go +++ b/model/nextcloud/nextcloud.go @@ -152,6 +152,10 @@ func (nc *NextCloud) Restore(path string) error { return nc.webdav.Move(path, dst) } +func (nc *NextCloud) EmptyTrash() error { + return nc.webdav.Delete("/trashbin/" + nc.userID + "/trash") +} + func (nc *NextCloud) ListFiles(path string) ([]jsonapi.Object, error) { items, err := nc.webdav.List("/files/" + nc.userID + "/" + path) if err != nil { diff --git a/web/remote/nextcloud.go b/web/remote/nextcloud.go index 2b46a33fd03..e69d92cd155 100644 --- a/web/remote/nextcloud.go +++ b/web/remote/nextcloud.go @@ -43,6 +43,24 @@ func nextcloudGetTrash(c echo.Context) error { return jsonapi.DataList(c, http.StatusOK, files, nil) } +func nextcloudEmptyTrash(c echo.Context) error { + inst := middlewares.GetInstance(c) + if err := middlewares.AllowWholeType(c, permission.DELETE, consts.Files); err != nil { + return err + } + + accountID := c.Param("account") + nc, err := nextcloud.New(inst, accountID) + if err != nil { + return wrapNextcloudErrors(err) + } + + if err := nc.EmptyTrash(); err != nil { + return wrapNextcloudErrors(err) + } + return c.NoContent(http.StatusNoContent) +} + func nextcloudGet(c echo.Context) error { inst := middlewares.GetInstance(c) if err := middlewares.AllowWholeType(c, permission.GET, consts.Files); err != nil { @@ -287,6 +305,7 @@ func nextcloudRestore(c echo.Context) error { func nextcloudRoutes(router *echo.Group) { group := router.Group("/nextcloud/:account") group.GET("/trash/*", nextcloudGetTrash) + group.DELETE("/trash", nextcloudEmptyTrash) group.GET("/*", nextcloudGet) group.PUT("/*", nextcloudPut) group.DELETE("/*", nextcloudDelete) From 7be0b1fa699a14c7f6b7f488d2bda37c5fcfd8cc Mon Sep 17 00:00:00 2001 From: Bruno Michel Date: Wed, 26 Jun 2024 17:30:23 +0200 Subject: [PATCH 4/5] Add a route to delete a file in the trash bin of NextCloud --- docs/nextcloud.md | 25 +++++++++++++++++++++++++ model/nextcloud/nextcloud.go | 4 ++++ web/remote/nextcloud.go | 20 ++++++++++++++++++++ 3 files changed, 49 insertions(+) diff --git a/docs/nextcloud.md b/docs/nextcloud.md index ac3080d4ae0..e405f3e8d75 100644 --- a/docs/nextcloud.md +++ b/docs/nextcloud.md @@ -511,6 +511,31 @@ HTTP/1.1 204 No Content - 404 Not Found, when the account is not found or the file/directory is not found on the NextCloud - 409 Conflict, when a directory or file already exists where the file/directory should be restored on the NextCloud. +## DELETE /remote/nextcloud/:account/trash/* + +This route can be used to delete a file in the trash. + +### Request + +```http +DELETE /remote/nextcloud/4ab2155707bb6613a8b9463daf00381b/trash/document-v1.docx HTTP/1.1 +Host: cozy.example.net +Authorization: Bearer eyJhbG... +``` + +### Response + +```http +HTTP/1.1 204 No Content +``` + +#### Status codes + +- 204 No Content, when the file/directory has been put in the trash +- 400 Bad Request, when the account is not configured for NextCloud, or the `To` parameter is missing +- 401 Unauthorized, when authentication to the NextCloud fails +- 404 Not Found, when the account is not found or the file/directory is not found on the NextCloud + ## DELETE /remote/nextcloud/:account/trash This route can be used to empty the trash bin on NextCloud. diff --git a/model/nextcloud/nextcloud.go b/model/nextcloud/nextcloud.go index 6f2531617cf..a368d19df23 100644 --- a/model/nextcloud/nextcloud.go +++ b/model/nextcloud/nextcloud.go @@ -152,6 +152,10 @@ func (nc *NextCloud) Restore(path string) error { return nc.webdav.Move(path, dst) } +func (nc *NextCloud) DeleteTrash(path string) error { + return nc.webdav.Delete("/trashbin/" + nc.userID + "/" + path) +} + func (nc *NextCloud) EmptyTrash() error { return nc.webdav.Delete("/trashbin/" + nc.userID + "/trash") } diff --git a/web/remote/nextcloud.go b/web/remote/nextcloud.go index e69d92cd155..907f5e15994 100644 --- a/web/remote/nextcloud.go +++ b/web/remote/nextcloud.go @@ -43,6 +43,25 @@ func nextcloudGetTrash(c echo.Context) error { return jsonapi.DataList(c, http.StatusOK, files, nil) } +func nextcloudDeleteTrash(c echo.Context) error { + inst := middlewares.GetInstance(c) + if err := middlewares.AllowWholeType(c, permission.DELETE, consts.Files); err != nil { + return err + } + + accountID := c.Param("account") + nc, err := nextcloud.New(inst, accountID) + if err != nil { + return wrapNextcloudErrors(err) + } + + path := "/trash/" + c.Param("*") + if err := nc.DeleteTrash(path); err != nil { + return wrapNextcloudErrors(err) + } + return c.NoContent(http.StatusNoContent) +} + func nextcloudEmptyTrash(c echo.Context) error { inst := middlewares.GetInstance(c) if err := middlewares.AllowWholeType(c, permission.DELETE, consts.Files); err != nil { @@ -305,6 +324,7 @@ func nextcloudRestore(c echo.Context) error { func nextcloudRoutes(router *echo.Group) { group := router.Group("/nextcloud/:account") group.GET("/trash/*", nextcloudGetTrash) + group.DELETE("/trash/*", nextcloudDeleteTrash) group.DELETE("/trash", nextcloudEmptyTrash) group.GET("/*", nextcloudGet) group.PUT("/*", nextcloudPut) From 71372fcc087ab93906f6b7e1beac1f9a11b946f7 Mon Sep 17 00:00:00 2001 From: Bruno Michel Date: Thu, 27 Jun 2024 12:30:12 +0200 Subject: [PATCH 5/5] Fix delete/restore for NextCloud The path for files/directories inside the NextCloud trashbin are NOT their parent path + their name, but also contains a suffix. So, we need to add the path to the response to allow the clients to make operations on them. --- docs/nextcloud.md | 9 +++++++-- model/nextcloud/nextcloud.go | 6 +++++- pkg/webdav/webdav.go | 2 ++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/docs/nextcloud.md b/docs/nextcloud.md index e405f3e8d75..fef8fce107a 100644 --- a/docs/nextcloud.md +++ b/docs/nextcloud.md @@ -45,6 +45,7 @@ Content-Type: application/vnd.api+json "attributes": { "type": "directory", "name": "Images", + "path": "/Documents/Images", "updated_at": "Thu, 02 May 2024 09:29:53 GMT", "etag": "\"66335d11c4b91\"" }, @@ -59,6 +60,7 @@ Content-Type: application/vnd.api+json "attributes": { "type": "file", "name": "BugBounty.pdf", + "path": "/Documents/BugBounty.pdf", "size": 2947, "mime": "application/pdf", "class": "pdf", @@ -76,6 +78,7 @@ Content-Type: application/vnd.api+json "attributes": { "type": "directory", "name": "Music", + "name": "/Documents/Music", "updated_at": "Thu, 02 May 2024 09:28:37 GMT", "etag": "\"66335cc55204b\"" }, @@ -90,6 +93,7 @@ Content-Type: application/vnd.api+json "attributes": { "type": "directory", "name": "Video", + "path": "/Documents/Video", "updated_at": "Thu, 02 May 2024 09:29:53 GMT", "etag": "\"66335d11c2318\"" }, @@ -460,6 +464,7 @@ Content-Type: application/vnd.api+json "attributes": { "type": "directory", "name": "Old", + "path": "/trash/Old.d93571568", "updated_at": "Tue, 25 Jun 2024 14:31:44 GMT", "etag": "1719326384" }, @@ -492,7 +497,7 @@ The `*path` parameter is the path of the file on the NextCloud. ### Request ```http -POST /remote/nextcloud/4ab2155707bb6613a8b9463daf00381b/restore/Old HTTP/1.1 +POST /remote/nextcloud/4ab2155707bb6613a8b9463daf00381b/restore/trash/Old.d93571568 HTTP/1.1 Host: cozy.example.net Authorization: Bearer eyJhbG... ``` @@ -518,7 +523,7 @@ This route can be used to delete a file in the trash. ### Request ```http -DELETE /remote/nextcloud/4ab2155707bb6613a8b9463daf00381b/trash/document-v1.docx HTTP/1.1 +DELETE /remote/nextcloud/4ab2155707bb6613a8b9463daf00381b/trash/document-v1.docx.d64283654 HTTP/1.1 Host: cozy.example.net Authorization: Bearer eyJhbG... ``` diff --git a/model/nextcloud/nextcloud.go b/model/nextcloud/nextcloud.go index a368d19df23..6e807f13988 100644 --- a/model/nextcloud/nextcloud.go +++ b/model/nextcloud/nextcloud.go @@ -35,6 +35,7 @@ type File struct { DocID string `json:"id,omitempty"` Type string `json:"type"` Name string `json:"name"` + Path string `json:"path"` Size uint64 `json:"size,omitempty"` Mime string `json:"mime,omitempty"` Class string `json:"class,omitempty"` @@ -176,6 +177,7 @@ func (nc *NextCloud) ListFiles(path string) ([]jsonapi.Object, error) { DocID: item.ID, Type: item.Type, Name: item.Name, + Path: "/" + filepath.Join(path, filepath.Base(item.Href)), Size: item.Size, Mime: mime, Class: class, @@ -189,7 +191,8 @@ func (nc *NextCloud) ListFiles(path string) ([]jsonapi.Object, error) { } func (nc *NextCloud) ListTrash(path string) ([]jsonapi.Object, error) { - items, err := nc.webdav.List("/trashbin/" + nc.userID + "/trash/" + path) + path = "/trash/" + path + items, err := nc.webdav.List("/trashbin/" + nc.userID + path) if err != nil { return nil, err } @@ -204,6 +207,7 @@ func (nc *NextCloud) ListTrash(path string) ([]jsonapi.Object, error) { DocID: item.ID, Type: item.Type, Name: item.TrashName, + Path: filepath.Join(path, filepath.Base(item.Href)), Size: item.Size, Mime: mime, Class: class, diff --git a/pkg/webdav/webdav.go b/pkg/webdav/webdav.go index 753169fe108..aa4fb0a24a0 100644 --- a/pkg/webdav/webdav.go +++ b/pkg/webdav/webdav.go @@ -240,6 +240,7 @@ func (c *Client) List(path string) ([]Item, error) { item := Item{ ID: props.FileID, Type: "directory", + Href: href, Name: props.Name, TrashName: props.TrashName, LastModified: props.LastModified, @@ -262,6 +263,7 @@ func (c *Client) List(path string) ([]Item, error) { type Item struct { ID string Type string + Href string Name string TrashName string Size uint64