From f21f0ea60bea800e337011c1684cca716be9d1b7 Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Thu, 5 Dec 2024 10:49:54 +0800 Subject: [PATCH] :technologist: Add a kernel API `/api/filetree/moveDocsByID` https://github.com/siyuan-note/siyuan/issues/13247 --- API.md | 75 +++++++++++++++++++++++++++++++----------- API_zh_CN.md | 44 +++++++++++++++++++------ kernel/api/filetree.go | 57 ++++++++++++++++++++++++++++++-- kernel/api/router.go | 1 + 4 files changed, 145 insertions(+), 32 deletions(-) diff --git a/API.md b/API.md index dd23d9a32..097312642 100644 --- a/API.md +++ b/API.md @@ -73,7 +73,8 @@ * Endpoint: `http://127.0.0.1:6806` * Both are POST methods -* An interface with parameters is required, the parameter is a JSON string, placed in the body, and the header Content-Type is `application/json` +* An interface with parameters is required, the parameter is a JSON string, placed in the body, and the header + Content-Type is `application/json` * Return value ````json @@ -328,7 +329,8 @@ View API token in Settings - About, request header: `Authorization: T ``` * `notebook`: Notebook ID - * `path`: Document path, which needs to start with / and separate levels with / (path here corresponds to the database hpath field) + * `path`: Document path, which needs to start with / and separate levels with / (path here corresponds to the + database hpath field) * `markdown`: GFM Markdown content * Return value @@ -381,8 +383,8 @@ Rename a document by `id`: } ``` - * `id`: Document ID - * `title`: New document title + * `id`: Document ID + * `title`: New document title * Return value ```json @@ -416,7 +418,7 @@ Rename a document by `id`: "data": null } ``` - + Remove a document by `id`: * `/api/filetree/removeDocByID` @@ -428,7 +430,7 @@ Remove a document by `id`: } ``` - * `id`: Document ID + * `id`: Document ID * Return value ```json @@ -465,6 +467,30 @@ Remove a document by `id`: } ``` +Move documents by `id`: + +* `/api/filetree/moveDocsByID` +* Parameters + + ```json + { + "fromIDs": ["20210917220056-yxtyl7i"], + "toID": "20210817205410-2kvfpfn" + } + ``` + + * `fromIDs`: Source docs' IDs + * `toID`: Target parent ID +* Return value + + ```json + { + "code": 0, + "msg": "", + "data": null + } + ``` + ### Get human-readable path based on path * `/api/filetree/getHPathByPath` @@ -510,7 +536,7 @@ Remove a document by `id`: "data": "/foo/bar" } ``` - + ### Get storage path based on ID * `/api/filetree/getPathByID` @@ -522,7 +548,7 @@ Remove a document by `id`: } ``` - * `id`: Block ID + * `id`: Block ID * Return value ```json @@ -545,8 +571,8 @@ Remove a document by `id`: } ``` - * `path`: Human-readable path - * `notebook`: Notebook ID + * `path`: Human-readable path + * `notebook`: Notebook ID * Return value ```json @@ -570,7 +596,8 @@ Remove a document by `id`: * `"/assets/"`: workspace/data/assets/ folder * `"/assets/sub/"`: workspace/data/assets/sub/ folder - Under normal circumstances, it is recommended to use the first method, which is stored in the assets folder of the workspace, putting in a subdirectory has some side effects, please refer to the assets chapter of the user guide. + Under normal circumstances, it is recommended to use the first method, which is stored in the assets folder of the + workspace, putting in a subdirectory has some side effects, please refer to the assets chapter of the user guide. * `file[]`: Uploaded file list * Return value @@ -588,7 +615,9 @@ Remove a document by `id`: ``` * `errFiles`: List of filenames with errors in upload processing - * `succMap`: For successfully processed files, the key is the file name when uploading, and the value is assets/foo-id.png, which is used to replace the asset link address in the existing Markdown content with the uploaded address + * `succMap`: For successfully processed files, the key is the file name when uploading, and the value is + assets/foo-id.png, which is used to replace the asset link address in the existing Markdown content with the + uploaded address ## Blocks @@ -613,7 +642,8 @@ Remove a document by `id`: * `previousID`: The ID of the previous block, used to anchor the insertion position * `parentID`: The ID of the parent block, used to anchor the insertion position - `nextID`, `previousID`, and `parentID` must have at least one value, using priority: `nextID` > `previousID` > `parentID` + `nextID`, `previousID`, and `parentID` must have at least one value, using priority: `nextID` > `previousID` > + `parentID` * Return value ```json @@ -820,7 +850,8 @@ Remove a document by `id`: * `id`: Block ID to move * `previousID`: The ID of the previous block, used to anchor the insertion position - * `parentID`: The ID of the parent block, used to anchor the insertion position, `previousID` and `parentID` cannot be empty at the same time, if they exist at the same time, `previousID` will be used first + * `parentID`: The ID of the parent block, used to anchor the insertion position, `previousID` and `parentID` cannot + be empty at the same time, if they exist at the same time, `previousID` will be used first * Return value ```json @@ -860,7 +891,7 @@ Remove a document by `id`: } ``` - * `id`: Block ID to fold + * `id`: Block ID to fold * Return value ```json @@ -882,7 +913,7 @@ Remove a document by `id`: } ``` - * `id`: Block ID to unfold + * `id`: Block ID to unfold * Return value ```json @@ -1380,7 +1411,8 @@ Remove a document by `id`: "timeout": 7000 } ``` - * `timeout`: The duration of the message display in milliseconds. This field can be omitted, the default is 7000 milliseconds + * `timeout`: The duration of the message display in milliseconds. This field can be omitted, the default is 7000 + milliseconds * Return value ```json @@ -1405,7 +1437,8 @@ Remove a document by `id`: "timeout": 7000 } ``` - * `timeout`: The duration of the message display in milliseconds. This field can be omitted, the default is 7000 milliseconds + * `timeout`: The duration of the message display in milliseconds. This field can be omitted, the default is 7000 + milliseconds * Return value ```json @@ -1457,7 +1490,8 @@ Remove a document by `id`: * `base32` | `base32-std` * `base32-hex` * `hex` - * `responseEncoding`: The encoding scheme used by `body` in response data, default is `text`, optional values are as follows + * `responseEncoding`: The encoding scheme used by `body` in response data, default is `text`, optional values are as + follows * `text` * `base64` | `base64-std` @@ -1484,7 +1518,8 @@ Remove a document by `id`: } ``` - * `bodyEncoding`: The encoding scheme used by `body`, is consistent with field `responseEncoding` in request, default is `text`, optional values are as follows + * `bodyEncoding`: The encoding scheme used by `body`, is consistent with field `responseEncoding` in request, + default is `text`, optional values are as follows * `text` * `base64` | `base64-std` diff --git a/API_zh_CN.md b/API_zh_CN.md index 31780b93c..e97735a79 100644 --- a/API_zh_CN.md +++ b/API_zh_CN.md @@ -381,8 +381,8 @@ } ``` - * `id`:文档 ID - * `title`:新标题 + * `id`:文档 ID + * `title`:新标题 * 返回值 ```json @@ -416,7 +416,7 @@ "data": null } ``` - + 通过 `id` 删除文档: * `/api/filetree/removeDocByID` @@ -428,7 +428,7 @@ } ``` - * `id`:文档 ID + * `id`:文档 ID * 返回值 ```json @@ -465,6 +465,30 @@ } ``` +通过 `id` 移动文档: + +* `/api/filetree/moveDocsByID` +* 参数 + + ```json + { + "fromIDs": ["20210917220056-yxtyl7i"], + "toID": "20210817205410-2kvfpfn" + } + ``` + + * `fromIDs`:源文档 ID + * `toID`:目标父文档 ID +* 返回值 + + ```json + { + "code": 0, + "msg": "", + "data": null + } + ``` + ### 根据路径获取人类可读路径 * `/api/filetree/getHPathByPath` @@ -522,7 +546,7 @@ } ``` - * `id`:块 ID + * `id`:块 ID * 返回值 ```json @@ -545,8 +569,8 @@ } ``` - * `path`:人类可读路径 - * `notebook`:笔记本 ID + * `path`:人类可读路径 + * `notebook`:笔记本 ID * 返回值 ```json @@ -860,7 +884,7 @@ } ``` - * `id`:待折叠块的 ID + * `id`:待折叠块的 ID * 返回值 ```json @@ -882,7 +906,7 @@ } ``` - * `id`:待展开块的 ID + * `id`:待展开块的 ID * 返回值 ```json @@ -1063,7 +1087,7 @@ ] } ``` - + ### 提交事务 * `/api/sqlite/flushTransaction` diff --git a/kernel/api/filetree.go b/kernel/api/filetree.go index 7dee01c70..030c8dc3e 100644 --- a/kernel/api/filetree.go +++ b/kernel/api/filetree.go @@ -472,9 +472,7 @@ func moveDocs(c *gin.Context) { if util.InvalidIDPattern(toNotebook, ret) { return } - callback := arg["callback"] - err := model.MoveDocs(fromPaths, toNotebook, toPath, callback) if err != nil { ret.Code = -1 @@ -484,6 +482,61 @@ func moveDocs(c *gin.Context) { } } +func moveDocsByID(c *gin.Context) { + ret := gulu.Ret.NewResult() + defer c.JSON(http.StatusOK, ret) + + arg, ok := util.JsonArg(c, ret) + if !ok { + return + } + + fromIDsArg := arg["fromIDs"].([]any) + var fromIDs []string + for _, fromIDArg := range fromIDsArg { + fromID := fromIDArg.(string) + if util.InvalidIDPattern(fromID, ret) { + return + } + fromIDs = append(fromIDs, fromID) + } + toID := arg["toID"].(string) + if util.InvalidIDPattern(toID, ret) { + return + } + + var fromPaths []string + for _, fromID := range fromIDs { + tree, err := model.LoadTreeByBlockID(fromID) + if err != nil { + ret.Code = -1 + ret.Msg = err.Error() + ret.Data = map[string]interface{}{"closeTimeout": 7000} + return + } + fromPaths = append(fromPaths, tree.Path) + } + fromPaths = gulu.Str.RemoveDuplicatedElem(fromPaths) + + toTree, err := model.LoadTreeByBlockID(toID) + if err != nil { + ret.Code = -1 + ret.Msg = err.Error() + ret.Data = map[string]interface{}{"closeTimeout": 7000} + return + } + toNotebook := toTree.Box + toPath := toTree.Path + callback := arg["callback"] + err = model.MoveDocs(fromPaths, toNotebook, toPath, callback) + if err != nil { + ret.Code = -1 + ret.Msg = err.Error() + ret.Data = map[string]interface{}{"closeTimeout": 7000} + return + } +} + func removeDoc(c *gin.Context) { ret := gulu.Ret.NewResult() defer c.JSON(http.StatusOK, ret) diff --git a/kernel/api/router.go b/kernel/api/router.go index ec2d5dbd0..da62f40ed 100644 --- a/kernel/api/router.go +++ b/kernel/api/router.go @@ -109,6 +109,7 @@ func ServeAPI(ginServer *gin.Engine) { ginServer.Handle("POST", "/api/filetree/removeDocByID", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, removeDocByID) ginServer.Handle("POST", "/api/filetree/removeDocs", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, removeDocs) ginServer.Handle("POST", "/api/filetree/moveDocs", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, moveDocs) + ginServer.Handle("POST", "/api/filetree/moveDocsByID", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, moveDocsByID) ginServer.Handle("POST", "/api/filetree/duplicateDoc", model.CheckAuth, model.CheckAdminRole, model.CheckReadonly, duplicateDoc) ginServer.Handle("POST", "/api/filetree/getHPathByPath", model.CheckAuth, getHPathByPath) ginServer.Handle("POST", "/api/filetree/getHPathsByPaths", model.CheckAuth, getHPathsByPaths)