diff --git a/API.md b/API.md index 0c8ba3877..48a386fa2 100644 --- a/API.md +++ b/API.md @@ -38,6 +38,7 @@ * [File](#File) * [Get file](#Get-file) * [Put file](#Put-file) + * [Remove file](#Remove-file) * [Export](#Export) * [Export Markdown](#Export-Markdown) * [Notification](#Notification) @@ -439,7 +440,7 @@ View API token in Settings - About, request header: `Authorization: T } ``` - * `id`:Block ID + * `id`: Block ID * Return value ```json @@ -854,7 +855,7 @@ View API token in Settings - About, request header: `Authorization: T * `isDir`: whether to create a folder, when `true` only create a folder, ignore `file` * `modTime`: last access and modification time, Unix time * `file`: the uploaded file -* return value +* Return value ```json { @@ -864,6 +865,28 @@ View API token in Settings - About, request header: `Authorization: T } ``` +### Remove file + +* `/api/file/removeFile` +* Parameters + + ```json + { + "path": "/data/20210808180117-6v0mkxr/20200923234011-ieuun1p.sy" + } + ``` + * `path`: the file path under the workspace path +* Return value + + ```json + { + "code": 0, + "msg": "", + "data": null + } + ``` + + ## Export ### Export Markdown @@ -946,7 +969,7 @@ View API token in Settings - About, request header: `Authorization: T } } ``` - * `id`:Message ID + * `id`: Message ID ## System diff --git a/API_zh_CN.md b/API_zh_CN.md index c77c5818b..13be50e40 100644 --- a/API_zh_CN.md +++ b/API_zh_CN.md @@ -38,6 +38,7 @@ * [文件](#文件) * [获取文件](#获取文件) * [写入文件](#写入文件) + * [删除文件](#删除文件) * [导出](#导出) * [导出 Markdown 文本](#导出-markdown-文本) * [通知](#通知) @@ -858,6 +859,27 @@ } ``` +### 删除文件 + +* `/api/file/removeFile` +* 参数 + + ```json + { + "path": "/data/20210808180117-6v0mkxr/20200923234011-ieuun1p.sy" + } + ``` + * `path`:工作空间路径下的文件路径 +* 返回值 + + ```json + { + "code": 0, + "msg": "", + "data": null + } + ``` + ## 导出 ### 导出 Markdown 文本 diff --git a/kernel/api/file.go b/kernel/api/file.go index db92708da..c63b0ad2c 100644 --- a/kernel/api/file.go +++ b/kernel/api/file.go @@ -110,6 +110,33 @@ func getFile(c *gin.Context) { } } +func removeFile(c *gin.Context) { + ret := gulu.Ret.NewResult() + arg, ok := util.JsonArg(c, ret) + if !ok { + c.JSON(http.StatusOK, ret) + return + } + + filePath := arg["path"].(string) + filePath = filepath.Join(util.WorkspaceDir, filePath) + _, err := os.Stat(filePath) + if os.IsNotExist(err) { + c.Status(404) + return + } + if nil != err { + logging.LogErrorf("stat [%s] failed: %s", filePath, err) + c.Status(500) + return + } + + if err = os.RemoveAll(filePath); nil != err { + c.Status(500) + return + } +} + func putFile(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 6e56ac488..8431c5a5a 100644 --- a/kernel/api/router.go +++ b/kernel/api/router.go @@ -175,6 +175,7 @@ func ServeAPI(ginServer *gin.Engine) { ginServer.Handle("POST", "/api/file/getFile", model.CheckAuth, getFile) ginServer.Handle("POST", "/api/file/putFile", model.CheckAuth, model.CheckReadonly, putFile) ginServer.Handle("POST", "/api/file/copyFile", model.CheckAuth, model.CheckReadonly, copyFile) + ginServer.Handle("POST", "/api/file/removeFile", model.CheckAuth, model.CheckReadonly, removeFile) ginServer.Handle("POST", "/api/ref/refreshBacklink", model.CheckAuth, refreshBacklink) ginServer.Handle("POST", "/api/ref/getBacklink", model.CheckAuth, getBacklink) diff --git a/kernel/av/av.go b/kernel/av/av.go index cad928641..f31381ceb 100644 --- a/kernel/av/av.go +++ b/kernel/av/av.go @@ -54,7 +54,7 @@ func NewAttributeView(id string) *AttributeView { return &AttributeView{ Spec: 0, ID: id, - Columns: []*Column{&Column{ID: ast.NewNodeID(), Name: "Block", Type: ColumnTypeBlock}}, + Columns: []*Column{{ID: ast.NewNodeID(), Name: "Block", Type: ColumnTypeBlock}}, Rows: []*Row{}, Type: AttributeViewTypeTable, Projections: []string{}, diff --git a/kernel/model/attribute_view.go b/kernel/model/attribute_view.go index 542a6524e..6ea9933d7 100644 --- a/kernel/model/attribute_view.go +++ b/kernel/model/attribute_view.go @@ -73,7 +73,23 @@ func (tx *Transaction) doRemoveAttrViewBlock(operation *Operation) (ret *TxErr) return } -func AddAttributeViewColumn(name string, typ string, columnIndex int, avID string) (err error) { +func (tx *Transaction) doAddAttrViewColumn(operation *Operation) (ret *TxErr) { + err := addAttributeViewColumn(operation.Name, operation.Typ, 1024, operation.ParentID) + if nil != err { + return &TxErr{code: TxErrWriteAttributeView, id: operation.ParentID, msg: err.Error()} + } + return +} + +func (tx *Transaction) doRemoveAttrViewColumn(operation *Operation) (ret *TxErr) { + err := removeAttributeViewColumn(operation.ID, operation.ParentID) + if nil != err { + return &TxErr{code: TxErrWriteAttributeView, id: operation.ParentID, msg: err.Error()} + } + return +} + +func addAttributeViewColumn(name string, typ string, columnIndex int, avID string) (err error) { attrView, err := av.ParseAttributeView(avID) if nil != err { return @@ -81,7 +97,7 @@ func AddAttributeViewColumn(name string, typ string, columnIndex int, avID strin switch av.ColumnType(typ) { case av.ColumnTypeText: - attrView.InsertColumn(columnIndex, &av.Column{ID: ast.NewNodeID(), Name: name, Type: av.ColumnTypeText}) + attrView.InsertColumn(columnIndex, &av.Column{ID: "av" + ast.NewNodeID(), Name: name, Type: av.ColumnTypeText}) default: msg := fmt.Sprintf("invalid column type [%s]", typ) logging.LogErrorf(msg) @@ -93,6 +109,23 @@ func AddAttributeViewColumn(name string, typ string, columnIndex int, avID strin return } +func removeAttributeViewColumn(columnID string, avID string) (err error) { + attrView, err := av.ParseAttributeView(avID) + if nil != err { + return + } + + for i, column := range attrView.Columns[1:] { + if column.ID == columnID { + attrView.Columns = append(attrView.Columns[:i], attrView.Columns[i+1:]...) + break + } + } + + err = av.SaveAttributeView(attrView) + return +} + func removeAttributeViewBlock(blockID, avID string, tree *parse.Tree, tx *Transaction) (err error) { node := treenode.GetNodeInTree(tree, blockID) if nil == node { @@ -107,6 +140,7 @@ func removeAttributeViewBlock(blockID, avID string, tree *parse.Tree, tx *Transa for i, row := range attrView.Rows { if row.Cells[0].Value == blockID { + // 从行中移除,但是不移除属性 attrView.Rows = append(attrView.Rows[:i], attrView.Rows[i+1:]...) break } @@ -150,11 +184,9 @@ func addAttributeViewBlock(blockID, avID string, tree *parse.Tree, tx *Transacti row.Cells = append(row.Cells, &av.Cell{ID: ast.NewNodeID(), Value: blockID}) if 1 < len(attrView.Columns) { // 将列作为属性添加到块中 - attrs := parse.IAL2Map(node.KramdownIAL) for _, col := range attrView.Columns[1:] { - colName := col.Name - attrs[colName] = "" + attrs["av"+col.ID] = "" } if err = setNodeAttrsWithTx(tx, node, tree, attrs); nil != err { diff --git a/kernel/model/blockial.go b/kernel/model/blockial.go index cf42e2ed6..222d9be73 100644 --- a/kernel/model/blockial.go +++ b/kernel/model/blockial.go @@ -19,13 +19,14 @@ package model import ( "errors" "fmt" - "github.com/88250/lute/parse" + "strings" "time" "github.com/88250/gulu" "github.com/88250/lute/ast" "github.com/88250/lute/html" "github.com/88250/lute/lex" + "github.com/88250/lute/parse" "github.com/araddon/dateparse" "github.com/siyuan-note/siyuan/kernel/cache" "github.com/siyuan-note/siyuan/kernel/treenode" @@ -166,6 +167,12 @@ func setNodeAttrs0(node *ast.Node, nameValues map[string]string) (oldAttrs map[s } for name, value := range nameValues { + if strings.HasPrefix(name, "av") { + // 属性视图设置的属性值可以为空 + node.SetIALAttr(name, value) + continue + } + if "" == value { node.RemoveIALAttr(name) } else { diff --git a/kernel/model/transaction.go b/kernel/model/transaction.go index 1711ec7c7..4cc293200 100644 --- a/kernel/model/transaction.go +++ b/kernel/model/transaction.go @@ -218,6 +218,10 @@ func performTx(tx *Transaction) (ret *TxErr) { ret = tx.doInsertAttrViewBlock(op) case "removeAttrViewBlock": ret = tx.doRemoveAttrViewBlock(op) + case "addAttrViewCol": + ret = tx.doAddAttrViewColumn(op) + case "removeAttrViewCol": + ret = tx.doRemoveAttrViewColumn(op) } if nil != ret { @@ -1026,9 +1030,12 @@ type Operation struct { ParentID string `json:"parentID"` PreviousID string `json:"previousID"` NextID string `json:"nextID"` - SrcIDs []string `json:"srcIDs"` // 用于将块拖拽到属性视图中 RetData interface{} `json:"retData"` + SrcIDs []string `json:"srcIDs"` // 用于将块拖拽到属性视图中 + Name string `json:"name"` // 用于属性视图列名 + Typ string `json:"type"` // 用于属性视图列类型 + discard bool // 用于标识是否在事务合并中丢弃 }