Merge remote-tracking branch 'origin/dev' into dev

This commit is contained in:
Vanessa 2023-12-24 16:54:25 +08:00
commit 5db30cdcd9
6 changed files with 264 additions and 1 deletions

46
API.md
View file

@ -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 <kbd>Settings - About</kbd>, 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`

View file

@ -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`

View file

@ -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)

View file

@ -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)

View file

@ -862,10 +862,15 @@ func updateAttributeViewColRelation(operation *Operation) (err error) {
if !destAdded {
if operation.IsTwoWay {
name := strings.TrimSpace(operation.Name)
if "" == name {
name = srcAv.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},
},
@ -1893,13 +1898,43 @@ 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 {
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 == removedKey.Relation.BackKeyID {
view.Table.Columns = append(view.Table.Columns[:i], view.Table.Columns[i+1:]...)
break
}
}
}
}
av.SaveAttributeView(destAv)
util.BroadcastByType("protyle", "refreshAttributeView", 0, "", map[string]interface{}{"id": destAv.ID})
}
}
for _, view := range attrView.Views {
switch view.LayoutType {
case av.LayoutTypeTable:

View file

@ -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":