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 // 用于标识是否在事务合并中丢弃
}