diff --git a/kernel/api/av.go b/kernel/api/av.go index f981e98c3..2f46fbcc7 100644 --- a/kernel/api/av.go +++ b/kernel/api/av.go @@ -27,6 +27,36 @@ import ( "github.com/siyuan-note/siyuan/kernel/util" ) +func batchReplaceAttributeViewBlocks(c *gin.Context) { + // Add kernel API `/api/av/batchReplaceAttributeViewBlocks` https://github.com/siyuan-note/siyuan/issues/15313 + ret := gulu.Ret.NewResult() + defer c.JSON(http.StatusOK, ret) + + arg, ok := util.JsonArg(c, ret) + if !ok { + return + } + + avID := arg["avID"].(string) + isDetached := arg["isDetached"].(bool) + oldNewArg := arg["oldNew"].([]interface{}) + var oldNew []map[string]string + for _, v := range oldNewArg { + for o, n := range v.(map[string]interface{}) { + oldNew = append(oldNew, map[string]string{o: n.(string)}) + } + } + + err := model.BatchReplaceAttributeViewBlocks(avID, isDetached, oldNew) + if err != nil { + ret.Code = -1 + ret.Msg = err.Error() + return + } + + model.ReloadAttrView(avID) +} + func setAttrViewGroup(c *gin.Context) { ret := gulu.Ret.NewResult() defer c.JSON(http.StatusOK, ret) @@ -737,6 +767,7 @@ func setAttributeViewBlockAttr(c *gin.Context) { } func batchSetAttributeViewBlockAttrs(c *gin.Context) { + // Add kernel API `/api/av/batchSetAttributeViewBlockAttrs` https://github.com/siyuan-note/siyuan/issues/15310 ret := gulu.Ret.NewResult() defer c.JSON(http.StatusOK, ret) diff --git a/kernel/api/router.go b/kernel/api/router.go index 628859b2b..8adceed9e 100644 --- a/kernel/api/router.go +++ b/kernel/api/router.go @@ -457,6 +457,7 @@ func ServeAPI(ginServer *gin.Engine) { ginServer.Handle("POST", "/api/av/getCurrentAttrViewImages", model.CheckAuth, getCurrentAttrViewImages) ginServer.Handle("POST", "/api/av/changeAttrViewLayout", model.CheckAuth, changeAttrViewLayout) ginServer.Handle("POST", "/api/av/setAttrViewGroup", model.CheckAuth, setAttrViewGroup) + ginServer.Handle("POST", "/api/av/batchReplaceAttributeViewBlocks", model.CheckAuth, batchReplaceAttributeViewBlocks) ginServer.Handle("POST", "/api/ai/chatGPT", model.CheckAuth, model.CheckAdminRole, chatGPT) ginServer.Handle("POST", "/api/ai/chatGPTWithAction", model.CheckAuth, model.CheckAdminRole, chatGPTWithAction) diff --git a/kernel/model/attribute_view.go b/kernel/model/attribute_view.go index 327fa0561..9ee356c38 100644 --- a/kernel/model/attribute_view.go +++ b/kernel/model/attribute_view.go @@ -3837,32 +3837,44 @@ func RemoveAttributeViewKey(avID, keyID string, removeRelationDest bool) (err er } func (tx *Transaction) doReplaceAttrViewBlock(operation *Operation) (ret *TxErr) { - err := replaceAttributeViewBlock(operation, tx) + err := replaceAttributeViewBlock(operation.AvID, operation.PreviousID, operation.NextID, operation.IsDetached, tx) if err != nil { return &TxErr{code: TxErrHandleAttributeView, id: operation.AvID} } return } -func replaceAttributeViewBlock(operation *Operation, tx *Transaction) (err error) { - attrView, err := av.ParseAttributeView(operation.AvID) +func replaceAttributeViewBlock(avID, oldBlockID, newBlockID string, isDetached bool, tx *Transaction) (err error) { + attrView, err := av.ParseAttributeView(avID) if err != nil { return } + if err = replaceAttributeViewBlock0(attrView, oldBlockID, newBlockID, isDetached, tx); nil != err { + return + } + + if err = av.SaveAttributeView(attrView); nil != err { + return + } + return +} + +func replaceAttributeViewBlock0(attrView *av.AttributeView, oldBlockID, newBlockID string, isDetached bool, tx *Transaction) (err error) { + avID := attrView.ID var node *ast.Node var tree *parse.Tree - if !operation.IsDetached { - node, tree, _ = getNodeByBlockID(tx, operation.NextID) + if !isDetached { + node, tree, _ = getNodeByBlockID(tx, newBlockID) } now := util.CurrentTimeMillis() // 检查是否已经存在绑定块,如果存在的话则重新绑定 for _, keyValues := range attrView.KeyValues { for _, value := range keyValues.Values { - if av.KeyTypeBlock == value.Type && nil != value.Block && value.BlockID == operation.NextID { - if !operation.IsDetached { - bindBlockAv0(tx, operation.AvID, node, tree) + if av.KeyTypeBlock == value.Type && nil != value.Block && value.BlockID == newBlockID { + if !isDetached { + bindBlockAv0(tx, avID, node, tree) value.IsDetached = false icon, content := getNodeAvBlockText(node) content = util.UnescapeHTML(content) @@ -3881,38 +3893,38 @@ func replaceAttributeViewBlock(operation *Operation, tx *Transaction) (err error if av.KeyTypeRelation == value.Type { if nil != value.Relation { for i, relBlockID := range value.Relation.BlockIDs { - if relBlockID == operation.PreviousID { - value.Relation.BlockIDs[i] = operation.NextID + if relBlockID == oldBlockID { + value.Relation.BlockIDs[i] = newBlockID changedAvIDs = append(changedAvIDs, attrView.ID) } } } } - if value.BlockID != operation.PreviousID { + if value.BlockID != oldBlockID { continue } - if av.KeyTypeBlock == value.Type && value.BlockID != operation.NextID { + if av.KeyTypeBlock == value.Type && value.BlockID != newBlockID { // 换绑 - unbindBlockAv(tx, operation.AvID, value.BlockID) + unbindBlockAv(tx, avID, value.BlockID) } - value.BlockID = operation.NextID + value.BlockID = newBlockID if av.KeyTypeBlock == value.Type && nil != value.Block { - value.Block.ID = operation.NextID - value.IsDetached = operation.IsDetached - if !operation.IsDetached { + value.Block.ID = newBlockID + value.IsDetached = isDetached + if !isDetached { icon, content := getNodeAvBlockText(node) content = util.UnescapeHTML(content) value.Block.Icon, value.Block.Content = icon, content } } - if av.KeyTypeBlock == value.Type && !operation.IsDetached { - bindBlockAv(tx, operation.AvID, operation.NextID) + if av.KeyTypeBlock == value.Type && !isDetached { + bindBlockAv(tx, avID, newBlockID) - avIDs := replaceRelationAvValues(operation.AvID, operation.PreviousID, operation.NextID) + avIDs := replaceRelationAvValues(avID, oldBlockID, newBlockID) changedAvIDs = append(changedAvIDs, avIDs...) } } @@ -3921,23 +3933,41 @@ func replaceAttributeViewBlock(operation *Operation, tx *Transaction) (err error replacedRowID := false for _, v := range attrView.Views { for i, itemID := range v.ItemIDs { - if itemID == operation.PreviousID { - v.ItemIDs[i] = operation.NextID + if itemID == oldBlockID { + v.ItemIDs[i] = newBlockID replacedRowID = true break } } if !replacedRowID { - v.ItemIDs = append(v.ItemIDs, operation.NextID) + v.ItemIDs = append(v.ItemIDs, newBlockID) } } - err = av.SaveAttributeView(attrView) - changedAvIDs = gulu.Str.RemoveDuplicatedElem(changedAvIDs) - for _, avID := range changedAvIDs { - ReloadAttrView(avID) + for _, id := range changedAvIDs { + ReloadAttrView(id) + } + return +} + +func BatchReplaceAttributeViewBlocks(avID string, isDetached bool, oldNew []map[string]string) (err error) { + attrView, err := av.ParseAttributeView(avID) + if err != nil { + return + } + + for _, oldNewMap := range oldNew { + for oldBlockID, newBlockID := range oldNewMap { + if err = replaceAttributeViewBlock0(attrView, oldBlockID, newBlockID, isDetached, nil); nil != err { + return + } + } + } + + if err = av.SaveAttributeView(attrView); nil != err { + return } return }