From f3956f10dc3029cc317a077c8d28270688652fee Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Sun, 24 Dec 2023 14:19:00 +0800 Subject: [PATCH 1/9] :art: Add Relation column to database table view https://github.com/siyuan-note/siyuan/issues/9888 --- kernel/model/attribute_view.go | 36 ++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/kernel/model/attribute_view.go b/kernel/model/attribute_view.go index 795551b61..4c23cbd8d 100644 --- a/kernel/model/attribute_view.go +++ b/kernel/model/attribute_view.go @@ -1893,13 +1893,45 @@ func removeAttributeViewColumn(operation *Operation) (err error) { return } + var removedKey *av.Key for i, keyValues := range attrView.KeyValues { if keyValues.Key.ID == operation.ID { attrView.KeyValues = append(attrView.KeyValues[:i], attrView.KeyValues[i+1:]...) + removedKey = keyValues.Key break } } + // 删除双向关联的目标列 + if nil != removedKey && nil != removedKey.Relation && removedKey.Relation.IsTwoWay { + destAv, _ := av.ParseAttributeView(removedKey.Relation.AvID) + if nil != destAv { + destKeyValues, _ := destAv.GetKeyValues(removedKey.Relation.BackKeyID) + if nil != destKeyValues { + for i, value := range destKeyValues.Values { + if value.KeyID == removedKey.Relation.BackKeyID { + destKeyValues.Values = append(destKeyValues.Values[:i], destKeyValues.Values[i+1:]...) + break + } + } + + for _, view := range destAv.Views { + switch view.LayoutType { + case av.LayoutTypeTable: + for i, column := range view.Table.Columns { + if column.ID == operation.ID { + view.Table.Columns = append(view.Table.Columns[:i], view.Table.Columns[i+1:]...) + break + } + } + } + } + + util.BroadcastByType("protyle", "refreshAttributeView", 0, "", map[string]interface{}{"id": destAv.ID}) + } + } + } + for _, view := range attrView.Views { switch view.LayoutType { case av.LayoutTypeTable: @@ -2055,12 +2087,12 @@ func UpdateAttributeViewCell(tx *Transaction, avID, keyID, rowID, cellID string, // 将游离行绑定到新建的块上 bindBlockAv(tx, avID, rowID) } - } else { // 之前绑定了块 + } else { // 之前绑定了块 if isUpdatingBlockKey { // 正在更新主键 if val.IsDetached { // 现在是游离行 // 将绑定的块从属性视图中移除 unbindBlockAv(tx, avID, rowID) - } else { // 现在绑定了块 + } else { // 现在绑定了块 if oldBoundBlockID != val.BlockID { // 之前绑定的块和现在绑定的块不一样 // 换绑块 unbindBlockAv(tx, avID, oldBoundBlockID) From c1bec499b497b6a7168eac64ee8748b532336373 Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Sun, 24 Dec 2023 14:24:25 +0800 Subject: [PATCH 2/9] :art: Add Relation column to database table view https://github.com/siyuan-note/siyuan/issues/9888 --- kernel/model/attribute_view.go | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/kernel/model/attribute_view.go b/kernel/model/attribute_view.go index 4c23cbd8d..08292e3e3 100644 --- a/kernel/model/attribute_view.go +++ b/kernel/model/attribute_view.go @@ -1906,23 +1906,20 @@ func removeAttributeViewColumn(operation *Operation) (err error) { if nil != removedKey && nil != removedKey.Relation && removedKey.Relation.IsTwoWay { destAv, _ := av.ParseAttributeView(removedKey.Relation.AvID) if nil != destAv { - destKeyValues, _ := destAv.GetKeyValues(removedKey.Relation.BackKeyID) - if nil != destKeyValues { - for i, value := range destKeyValues.Values { - if value.KeyID == removedKey.Relation.BackKeyID { - destKeyValues.Values = append(destKeyValues.Values[:i], destKeyValues.Values[i+1:]...) - break - } + for i, keyValues := range destAv.KeyValues { + if keyValues.Key.ID == removedKey.Relation.BackKeyID { + destAv.KeyValues = append(destAv.KeyValues[:i], destAv.KeyValues[i+1:]...) + break } + } - for _, view := range destAv.Views { - switch view.LayoutType { - case av.LayoutTypeTable: - for i, column := range view.Table.Columns { - if column.ID == operation.ID { - view.Table.Columns = append(view.Table.Columns[:i], view.Table.Columns[i+1:]...) - break - } + for _, view := range destAv.Views { + switch view.LayoutType { + case av.LayoutTypeTable: + for i, column := range view.Table.Columns { + if column.ID == removedKey.ID { + view.Table.Columns = append(view.Table.Columns[:i], view.Table.Columns[i+1:]...) + break } } } @@ -2087,12 +2084,12 @@ func UpdateAttributeViewCell(tx *Transaction, avID, keyID, rowID, cellID string, // 将游离行绑定到新建的块上 bindBlockAv(tx, avID, rowID) } - } else { // 之前绑定了块 + } else { // 之前绑定了块 if isUpdatingBlockKey { // 正在更新主键 if val.IsDetached { // 现在是游离行 // 将绑定的块从属性视图中移除 unbindBlockAv(tx, avID, rowID) - } else { // 现在绑定了块 + } else { // 现在绑定了块 if oldBoundBlockID != val.BlockID { // 之前绑定的块和现在绑定的块不一样 // 换绑块 unbindBlockAv(tx, avID, oldBoundBlockID) From c0353ca5bc4aab1b91a2e81b0188ca1fb42c65d8 Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Sun, 24 Dec 2023 14:24:42 +0800 Subject: [PATCH 3/9] :art: Add Relation column to database table view https://github.com/siyuan-note/siyuan/issues/9888 --- kernel/model/attribute_view.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/model/attribute_view.go b/kernel/model/attribute_view.go index 08292e3e3..1074c5696 100644 --- a/kernel/model/attribute_view.go +++ b/kernel/model/attribute_view.go @@ -1923,9 +1923,9 @@ func removeAttributeViewColumn(operation *Operation) (err error) { } } } - - util.BroadcastByType("protyle", "refreshAttributeView", 0, "", map[string]interface{}{"id": destAv.ID}) } + + util.BroadcastByType("protyle", "refreshAttributeView", 0, "", map[string]interface{}{"id": destAv.ID}) } } From 0d57661632d8e371946232bd7768d20097d47ab6 Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Sun, 24 Dec 2023 14:27:56 +0800 Subject: [PATCH 4/9] :art: Add Relation column to database table view https://github.com/siyuan-note/siyuan/issues/9888 --- kernel/model/attribute_view.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/model/attribute_view.go b/kernel/model/attribute_view.go index 1074c5696..d165e0534 100644 --- a/kernel/model/attribute_view.go +++ b/kernel/model/attribute_view.go @@ -1917,7 +1917,7 @@ func removeAttributeViewColumn(operation *Operation) (err error) { switch view.LayoutType { case av.LayoutTypeTable: for i, column := range view.Table.Columns { - if column.ID == removedKey.ID { + if column.ID == removedKey.Relation.BackKeyID { view.Table.Columns = append(view.Table.Columns[:i], view.Table.Columns[i+1:]...) break } @@ -1925,6 +1925,7 @@ func removeAttributeViewColumn(operation *Operation) (err error) { } } + av.SaveAttributeView(destAv) util.BroadcastByType("protyle", "refreshAttributeView", 0, "", map[string]interface{}{"id": destAv.ID}) } } From 848289ef5456babe0a21132d279b62c29b2a27df Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Sun, 24 Dec 2023 14:40:05 +0800 Subject: [PATCH 5/9] :art: Add Relation column to database table view https://github.com/siyuan-note/siyuan/issues/9888 --- kernel/model/attribute_view.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/kernel/model/attribute_view.go b/kernel/model/attribute_view.go index d165e0534..53d8024dc 100644 --- a/kernel/model/attribute_view.go +++ b/kernel/model/attribute_view.go @@ -18,6 +18,7 @@ package model import ( "bytes" + "errors" "os" "path/filepath" "sort" @@ -790,6 +791,11 @@ func updateAttributeViewColRelation(operation *Operation) (err error) { // operation.BackRelationKeyID 双向关联的目标关联列 ID // operation.Name 双向关联的目标关联列名称 + if "" == operation.AvID || "" == operation.ID || "" == operation.KeyID { + err = errors.New("invalid operation") + return + } + srcAv, err := av.ParseAttributeView(operation.AvID) if nil != err { return @@ -862,10 +868,15 @@ func updateAttributeViewColRelation(operation *Operation) (err error) { if !destAdded { if operation.IsTwoWay { + name := strings.TrimSpace(operation.Name) + if "" == name { + name = destAv.Name + } + destAv.KeyValues = append(destAv.KeyValues, &av.KeyValues{ Key: &av.Key{ ID: operation.BackRelationKeyID, - Name: operation.Name, + Name: name, Type: av.KeyTypeRelation, Relation: &av.Relation{AvID: operation.AvID, IsTwoWay: operation.IsTwoWay, BackKeyID: operation.KeyID}, }, From 8693670d60a4e0cc68b397563f4e30428620af0d Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Sun, 24 Dec 2023 15:23:13 +0800 Subject: [PATCH 6/9] :art: Add Relation column to database table view https://github.com/siyuan-note/siyuan/issues/9888 --- kernel/model/attribute_view.go | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/kernel/model/attribute_view.go b/kernel/model/attribute_view.go index 53d8024dc..710eecaea 100644 --- a/kernel/model/attribute_view.go +++ b/kernel/model/attribute_view.go @@ -18,7 +18,6 @@ package model import ( "bytes" - "errors" "os" "path/filepath" "sort" @@ -791,11 +790,6 @@ func updateAttributeViewColRelation(operation *Operation) (err error) { // operation.BackRelationKeyID 双向关联的目标关联列 ID // operation.Name 双向关联的目标关联列名称 - if "" == operation.AvID || "" == operation.ID || "" == operation.KeyID { - err = errors.New("invalid operation") - return - } - srcAv, err := av.ParseAttributeView(operation.AvID) if nil != err { return @@ -870,7 +864,7 @@ func updateAttributeViewColRelation(operation *Operation) (err error) { if operation.IsTwoWay { name := strings.TrimSpace(operation.Name) if "" == name { - name = destAv.Name + name = srcAv.Name } destAv.KeyValues = append(destAv.KeyValues, &av.KeyValues{ From b3d51ed94f29125079e675f9206c976fa07d3fef Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Sun, 24 Dec 2023 15:23:30 +0800 Subject: [PATCH 7/9] :art: Improve kernel stability https://github.com/siyuan-note/siyuan/issues/9912 --- kernel/model/transaction.go | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/kernel/model/transaction.go b/kernel/model/transaction.go index 35153b835..aaa101fd1 100644 --- a/kernel/model/transaction.go +++ b/kernel/model/transaction.go @@ -21,6 +21,7 @@ import ( "errors" "fmt" "path/filepath" + "runtime/debug" "strings" "sync" "sync/atomic" @@ -168,6 +169,19 @@ func performTx(tx *Transaction) (ret *TxErr) { return } + defer func() { + if e := recover(); nil != e { + stack := debug.Stack() + msg := fmt.Sprintf("PANIC RECOVERED: %v\n\t%s\n", e, stack) + logging.LogErrorf(msg) + + if 0 == tx.state.Load() { + tx.rollback() + return + } + } + }() + for _, op := range tx.DoOperations { switch op.Action { case "create": From 9403eef8b7314f28cce2a38e3075f088ff2449e1 Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Sun, 24 Dec 2023 16:19:52 +0800 Subject: [PATCH 8/9] :art: Add internal kernel API `/api/block/foldBlock` and `/api/block/unfoldBlock` https://github.com/siyuan-note/siyuan/issues/9962 --- kernel/api/block_op.go | 120 +++++++++++++++++++++++++++++++++++++++++ kernel/api/router.go | 2 + 2 files changed, 122 insertions(+) diff --git a/kernel/api/block_op.go b/kernel/api/block_op.go index 469cfbf36..446dd0f01 100644 --- a/kernel/api/block_op.go +++ b/kernel/api/block_op.go @@ -29,6 +29,126 @@ import ( "github.com/siyuan-note/siyuan/kernel/util" ) +func unfoldBlock(c *gin.Context) { + ret := gulu.Ret.NewResult() + defer c.JSON(http.StatusOK, ret) + + arg, ok := util.JsonArg(c, ret) + if !ok { + return + } + + id := arg["id"].(string) + if util.InvalidIDPattern(id, ret) { + return + } + + bt := treenode.GetBlockTree(id) + if nil == bt { + ret.Code = -1 + ret.Msg = "block tree not found [id=" + id + "]" + return + } + + if bt.Type == "d" { + ret.Code = -1 + ret.Msg = "document can not be unfolded" + return + } + + var transactions []*model.Transaction + if "h" == bt.Type { + transactions = []*model.Transaction{ + { + DoOperations: []*model.Operation{ + { + Action: "unfoldHeading", + ID: id, + }, + }, + }, + } + } else { + data, _ := gulu.JSON.MarshalJSON(map[string]interface{}{"unfold": "1"}) + transactions = []*model.Transaction{ + { + DoOperations: []*model.Operation{ + { + Action: "setAttrs", + ID: id, + Data: string(data), + }, + }, + }, + } + } + + model.PerformTransactions(&transactions) + model.WaitForWritingFiles() + + broadcastTransactions(transactions) +} + +func foldBlock(c *gin.Context) { + ret := gulu.Ret.NewResult() + defer c.JSON(http.StatusOK, ret) + + arg, ok := util.JsonArg(c, ret) + if !ok { + return + } + + id := arg["id"].(string) + if util.InvalidIDPattern(id, ret) { + return + } + + bt := treenode.GetBlockTree(id) + if nil == bt { + ret.Code = -1 + ret.Msg = "block tree not found [id=" + id + "]" + return + } + + if bt.Type == "d" { + ret.Code = -1 + ret.Msg = "document can not be folded" + return + } + + var transactions []*model.Transaction + if "h" == bt.Type { + transactions = []*model.Transaction{ + { + DoOperations: []*model.Operation{ + { + Action: "foldHeading", + ID: id, + }, + }, + }, + } + } else { + data, _ := gulu.JSON.MarshalJSON(map[string]interface{}{"fold": "1"}) + transactions = []*model.Transaction{ + { + DoOperations: []*model.Operation{ + { + Action: "setAttrs", + ID: id, + Data: string(data), + }, + }, + }, + } + } + + model.PerformTransactions(&transactions) + model.WaitForWritingFiles() + + broadcastTransactions(transactions) +} + func moveBlock(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 e3b5ff7df..fa2c50019 100644 --- a/kernel/api/router.go +++ b/kernel/api/router.go @@ -181,6 +181,8 @@ func ServeAPI(ginServer *gin.Engine) { ginServer.Handle("POST", "/api/block/updateBlock", model.CheckAuth, model.CheckReadonly, updateBlock) ginServer.Handle("POST", "/api/block/deleteBlock", model.CheckAuth, model.CheckReadonly, deleteBlock) ginServer.Handle("POST", "/api/block/moveBlock", model.CheckAuth, model.CheckReadonly, moveBlock) + ginServer.Handle("POST", "/api/block/foldBlock", model.CheckAuth, model.CheckReadonly, foldBlock) + ginServer.Handle("POST", "/api/block/unfoldBlock", model.CheckAuth, model.CheckReadonly, unfoldBlock) ginServer.Handle("POST", "/api/block/setBlockReminder", model.CheckAuth, model.CheckReadonly, setBlockReminder) ginServer.Handle("POST", "/api/block/getHeadingLevelTransaction", model.CheckAuth, getHeadingLevelTransaction) ginServer.Handle("POST", "/api/block/getHeadingDeleteTransaction", model.CheckAuth, getHeadingDeleteTransaction) From 584168acdb56ca2fe21038ef386b38a8f569fcee Mon Sep 17 00:00:00 2001 From: Daniel <845765@qq.com> Date: Sun, 24 Dec 2023 16:24:11 +0800 Subject: [PATCH 9/9] :art: Add kernel API `/api/block/foldBlock` and `/api/block/unfoldBlock` https://github.com/siyuan-note/siyuan/issues/9962 --- API.md | 46 ++++++++++++++++++++++++++++++++++++++++++++++ API_zh_CN.md | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+) diff --git a/API.md b/API.md index 3d0fc0b12..3cb22c74b 100644 --- a/API.md +++ b/API.md @@ -28,6 +28,8 @@ * [Update a block](#Update-a-block) * [Delete a block](#Delete-a-block) * [Move a block](#Move-a-block) + * [Fold a block](#Fold-a-block) + * [Unfold a block](#Unfold-a-block) * [Get a block kramdown](#Get-a-block-kramdown) * [Get child blocks](#get-child-blocks) * [Transfer block ref](#transfer-block-ref) @@ -783,6 +785,50 @@ View API token in Settings - About, request header: `Authorization: T } ``` +### Fold a block + +* `/api/block/foldBlock` +* Parameters + + ```json + { + "id": "20231224160424-2f5680o" + } + ``` + + * `id`: Block ID to fold +* Return value + + ```json + { + "code": 0, + "msg": "", + "data": null + } + ``` + +### Unfold a block + +* `/api/block/unfoldBlock` +* Parameters + + ```json + { + "id": "20231224160424-2f5680o" + } + ``` + + * `id`: Block ID to unfold +* Return value + + ```json + { + "code": 0, + "msg": "", + "data": null + } + ``` + ### Get a block kramdown * `/api/block/getBlockKramdown` diff --git a/API_zh_CN.md b/API_zh_CN.md index a66d9d8dc..d708bec67 100644 --- a/API_zh_CN.md +++ b/API_zh_CN.md @@ -29,6 +29,8 @@ * [更新块](#更新块) * [删除块](#删除块) * [移动块](#移动块) + * [折叠块](#折叠块) + * [展开块](#展开块) * [获取块 kramdown 源码](#获取块-kramdown-源码) * [获取子块](#获取子块) * [转移块引用](#转移块引用) @@ -776,6 +778,50 @@ } ``` +### 折叠块 + +* `/api/block/foldBlock` +* 参数 + + ```json + { + "id": "20231224160424-2f5680o" + } + ``` + + * `id`:待折叠块的 ID +* 返回值 + + ```json + { + "code": 0, + "msg": "", + "data": null + } + ``` + +### 展开块 + +* `/api/block/unfoldBlock` +* 参数 + + ```json + { + "id": "20231224160424-2f5680o" + } + ``` + + * `id`:待展开块的 ID +* 返回值 + + ```json + { + "code": 0, + "msg": "", + "data": null + } + ``` + ### 获取块 kramdown 源码 * `/api/block/getBlockKramdown`