diff --git a/kernel/api/transaction.go b/kernel/api/transaction.go index 9dc32e747..1b425b28a 100644 --- a/kernel/api/transaction.go +++ b/kernel/api/transaction.go @@ -63,22 +63,6 @@ func performTransactions(c *gin.Context) { return } - setAttrsOps := model.ExtractSetAttrsOps(&transactions) - for _, setAttrsOp := range setAttrsOps { - attrs := map[string]string{} - if err = gulu.JSON.UnmarshalJSON([]byte(setAttrsOp.Data.(string)), &attrs); nil != err { - return - } - if err = model.SetBlockAttrs(setAttrsOp.ID, attrs); nil != err { - if errors.Is(err, filelock.ErrUnableAccessFile) { - ret.Code = 1 - return - } - logging.LogFatalf("set block attrs failed: %s", err) - return - } - } - if err = model.PerformTransactions(&transactions); errors.Is(err, filelock.ErrUnableAccessFile) { ret.Code = 1 return diff --git a/kernel/model/transaction.go b/kernel/model/transaction.go index 81b9547de..e635ef759 100644 --- a/kernel/model/transaction.go +++ b/kernel/model/transaction.go @@ -28,6 +28,7 @@ import ( "github.com/88250/gulu" "github.com/88250/lute/ast" "github.com/88250/lute/editor" + "github.com/88250/lute/lex" "github.com/88250/lute/parse" "github.com/emirpasic/gods/sets/hashset" "github.com/siyuan-note/filelock" @@ -56,22 +57,6 @@ func IsUnfoldHeading(transactions *[]*Transaction) bool { return false } -func ExtractSetAttrsOps(transactions *[]*Transaction) (ret []*Operation) { - for _, tx := range *transactions { - var setAttrsOps, tmp []*Operation - for _, op := range tx.DoOperations { - if "setAttrs" == op.Action { - setAttrsOps = append(setAttrsOps, op) - } else { - tmp = append(tmp, op) - } - } - ret = append(ret, setAttrsOps...) - tx.DoOperations = tmp - } - return -} - const txFixDelay = 10 var ( @@ -241,6 +226,8 @@ func performTx(tx *Transaction) (ret *TxErr) { ret = tx.doFoldHeading(op) case "unfoldHeading": ret = tx.doUnfoldHeading(op) + case "setAttrs": + ret = tx.setAttrs(op) } if nil != ret { @@ -967,6 +954,54 @@ func (tx *Transaction) doCreate(operation *Operation) (ret *TxErr) { return } +func (tx *Transaction) setAttrs(operation *Operation) (ret *TxErr) { + id := operation.ID + tree, err := tx.loadTree(id) + if nil != err { + logging.LogErrorf("load tree [id=%s] failed: %s", id, err) + return &TxErr{code: TxErrCodeBlockNotFound, id: id} + } + + node := treenode.GetNodeInTree(tree, id) + if nil == node { + logging.LogErrorf("get node [%s] in tree [%s] failed", id, tree.Root.ID) + return &TxErr{code: TxErrCodeBlockNotFound, id: id} + } + + attrs := map[string]string{} + if err = gulu.JSON.UnmarshalJSON([]byte(operation.Data.(string)), &attrs); nil != err { + logging.LogErrorf("unmarshal attrs failed: %s", err) + return &TxErr{code: TxErrCodeBlockNotFound, id: id} + } + + var invalidNames []string + for name := range attrs { + for i := 0; i < len(name); i++ { + if !lex.IsASCIILetterNumHyphen(name[i]) { + logging.LogWarnf("invalid attr name [%s]", name) + invalidNames = append(invalidNames, name) + } + } + } + for _, name := range invalidNames { + delete(attrs, name) + } + + for name, value := range attrs { + if "" == value { + node.RemoveIALAttr(name) + } else { + node.SetIALAttr(name, value) + } + } + + if err = indexWriteJSONQueue(tree); nil != err { + return + } + cache.PutBlockIAL(id, parse.IAL2Map(node.KramdownIAL)) + return +} + func refreshUpdated(n *ast.Node) { updated := util.CurrentTimeSecondsStr() n.SetIALAttr("updated", updated)