670 lines
13 KiB
Go
670 lines
13 KiB
Go
// SiYuan - Refactor your thinking
|
||
// Copyright (c) 2020-present, b3log.org
|
||
//
|
||
// This program is free software: you can redistribute it and/or modify
|
||
// it under the terms of the GNU Affero General Public License as published by
|
||
// the Free Software Foundation, either version 3 of the License, or
|
||
// (at your option) any later version.
|
||
//
|
||
// This program is distributed in the hope that it will be useful,
|
||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
// GNU Affero General Public License for more details.
|
||
//
|
||
// You should have received a copy of the GNU Affero General Public License
|
||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||
|
||
package api
|
||
|
||
import (
|
||
"errors"
|
||
"fmt"
|
||
"net/http"
|
||
"strings"
|
||
|
||
"github.com/88250/gulu"
|
||
"github.com/88250/lute/html"
|
||
"github.com/gin-gonic/gin"
|
||
"github.com/siyuan-note/logging"
|
||
"github.com/siyuan-note/siyuan/kernel/model"
|
||
"github.com/siyuan-note/siyuan/kernel/util"
|
||
)
|
||
|
||
func getBlockTreeInfos(c *gin.Context) {
|
||
ret := gulu.Ret.NewResult()
|
||
defer c.JSON(http.StatusOK, ret)
|
||
|
||
arg, ok := util.JsonArg(c, ret)
|
||
if !ok {
|
||
return
|
||
}
|
||
|
||
var ids []string
|
||
idsArg := arg["ids"].([]interface{})
|
||
for _, id := range idsArg {
|
||
ids = append(ids, id.(string))
|
||
}
|
||
|
||
ret.Data = model.GetBlockTreeInfos(ids)
|
||
}
|
||
|
||
func getBlockSiblingID(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)
|
||
parent, previous, next := model.GetBlockSiblingID(id)
|
||
ret.Data = map[string]string{
|
||
"parent": parent,
|
||
"next": next,
|
||
"previous": previous,
|
||
}
|
||
}
|
||
|
||
func transferBlockRef(c *gin.Context) {
|
||
ret := gulu.Ret.NewResult()
|
||
defer c.JSON(http.StatusOK, ret)
|
||
|
||
arg, ok := util.JsonArg(c, ret)
|
||
if !ok {
|
||
return
|
||
}
|
||
|
||
fromID := arg["fromID"].(string)
|
||
if util.InvalidIDPattern(fromID, ret) {
|
||
return
|
||
}
|
||
toID := arg["toID"].(string)
|
||
if util.InvalidIDPattern(toID, ret) {
|
||
return
|
||
}
|
||
|
||
reloadUI := true
|
||
if nil != arg["reloadUI"] {
|
||
reloadUI = arg["reloadUI"].(bool)
|
||
}
|
||
|
||
var refIDs []string
|
||
if nil != arg["refIDs"] {
|
||
for _, refID := range arg["refIDs"].([]interface{}) {
|
||
refIDs = append(refIDs, refID.(string))
|
||
}
|
||
}
|
||
|
||
err := model.TransferBlockRef(fromID, toID, refIDs)
|
||
if err != nil {
|
||
ret.Code = -1
|
||
ret.Msg = err.Error()
|
||
ret.Data = map[string]interface{}{"closeTimeout": 7000}
|
||
return
|
||
}
|
||
|
||
if reloadUI {
|
||
util.ReloadUI()
|
||
}
|
||
}
|
||
|
||
func swapBlockRef(c *gin.Context) {
|
||
ret := gulu.Ret.NewResult()
|
||
defer c.JSON(http.StatusOK, ret)
|
||
|
||
arg, ok := util.JsonArg(c, ret)
|
||
if !ok {
|
||
return
|
||
}
|
||
|
||
refID := arg["refID"].(string)
|
||
defID := arg["defID"].(string)
|
||
includeChildren := arg["includeChildren"].(bool)
|
||
err := model.SwapBlockRef(refID, defID, includeChildren)
|
||
if err != nil {
|
||
ret.Code = -1
|
||
ret.Msg = err.Error()
|
||
ret.Data = map[string]interface{}{"closeTimeout": 7000}
|
||
return
|
||
}
|
||
}
|
||
|
||
func getHeadingChildrenIDs(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)
|
||
ids := model.GetHeadingChildrenIDs(id)
|
||
ret.Data = ids
|
||
}
|
||
|
||
func getHeadingChildrenDOM(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)
|
||
dom := model.GetHeadingChildrenDOM(id)
|
||
ret.Data = dom
|
||
}
|
||
|
||
func getHeadingDeleteTransaction(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)
|
||
|
||
transaction, err := model.GetHeadingDeleteTransaction(id)
|
||
if err != nil {
|
||
ret.Code = -1
|
||
ret.Msg = err.Error()
|
||
ret.Data = map[string]interface{}{"closeTimeout": 7000}
|
||
return
|
||
}
|
||
|
||
ret.Data = transaction
|
||
}
|
||
|
||
func getHeadingLevelTransaction(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)
|
||
level := int(arg["level"].(float64))
|
||
|
||
transaction, err := model.GetHeadingLevelTransaction(id, level)
|
||
if err != nil {
|
||
ret.Code = -1
|
||
ret.Msg = err.Error()
|
||
ret.Data = map[string]interface{}{"closeTimeout": 7000}
|
||
return
|
||
}
|
||
|
||
ret.Data = transaction
|
||
}
|
||
|
||
func setBlockReminder(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)
|
||
timed := arg["timed"].(string) // yyyyMMddHHmmss
|
||
err := model.SetBlockReminder(id, timed)
|
||
if err != nil {
|
||
ret.Code = -1
|
||
ret.Msg = err.Error()
|
||
ret.Data = map[string]interface{}{"closeTimeout": 7000}
|
||
return
|
||
}
|
||
}
|
||
|
||
func checkBlockFold(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)
|
||
isFolded, isRoot := model.IsBlockFolded(id)
|
||
ret.Data = map[string]interface{}{
|
||
"isFolded": isFolded,
|
||
"isRoot": isRoot,
|
||
}
|
||
}
|
||
|
||
func checkBlockExist(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)
|
||
b, err := model.GetBlock(id, nil)
|
||
if errors.Is(err, model.ErrIndexing) {
|
||
ret.Code = 0
|
||
ret.Data = false
|
||
return
|
||
}
|
||
ret.Data = nil != b
|
||
}
|
||
|
||
func getDocInfo(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)
|
||
info := model.GetDocInfo(id)
|
||
if nil == info {
|
||
ret.Code = -1
|
||
ret.Msg = fmt.Sprintf(model.Conf.Language(15), id)
|
||
return
|
||
}
|
||
ret.Data = info
|
||
}
|
||
|
||
func getDocsInfo(c *gin.Context) {
|
||
ret := gulu.Ret.NewResult()
|
||
defer c.JSON(http.StatusOK, ret)
|
||
|
||
arg, ok := util.JsonArg(c, ret)
|
||
if !ok {
|
||
return
|
||
}
|
||
idsArg := arg["ids"].([]interface{})
|
||
var ids []string
|
||
for _, id := range idsArg {
|
||
ids = append(ids, id.(string))
|
||
}
|
||
queryRefCount := arg["refCount"].(bool)
|
||
queryAv := arg["av"].(bool)
|
||
info := model.GetDocsInfo(ids, queryRefCount, queryAv)
|
||
if nil == info {
|
||
ret.Code = -1
|
||
ret.Msg = fmt.Sprintf(model.Conf.Language(15), ids)
|
||
return
|
||
}
|
||
ret.Data = info
|
||
}
|
||
|
||
func getRecentUpdatedBlocks(c *gin.Context) {
|
||
ret := gulu.Ret.NewResult()
|
||
defer c.JSON(http.StatusOK, ret)
|
||
|
||
blocks := model.RecentUpdatedBlocks()
|
||
ret.Data = blocks
|
||
}
|
||
|
||
func getContentWordCount(c *gin.Context) {
|
||
ret := gulu.Ret.NewResult()
|
||
defer c.JSON(http.StatusOK, ret)
|
||
|
||
arg, ok := util.JsonArg(c, ret)
|
||
if !ok {
|
||
return
|
||
}
|
||
|
||
content := arg["content"].(string)
|
||
ret.Data = model.ContentStat(content)
|
||
}
|
||
|
||
func getBlocksWordCount(c *gin.Context) {
|
||
ret := gulu.Ret.NewResult()
|
||
defer c.JSON(http.StatusOK, ret)
|
||
|
||
arg, ok := util.JsonArg(c, ret)
|
||
if !ok {
|
||
return
|
||
}
|
||
|
||
idsArg := arg["ids"].([]interface{})
|
||
var ids []string
|
||
for _, id := range idsArg {
|
||
ids = append(ids, id.(string))
|
||
}
|
||
ret.Data = model.BlocksWordCount(ids)
|
||
}
|
||
|
||
func getTreeStat(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)
|
||
ret.Data = model.StatTree(id)
|
||
}
|
||
|
||
func getDOMText(c *gin.Context) {
|
||
ret := gulu.Ret.NewResult()
|
||
defer c.JSON(http.StatusOK, ret)
|
||
|
||
arg, ok := util.JsonArg(c, ret)
|
||
if !ok {
|
||
return
|
||
}
|
||
|
||
dom := arg["dom"].(string)
|
||
ret.Data = model.GetDOMText(dom)
|
||
}
|
||
|
||
func getRefText(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
|
||
}
|
||
|
||
refText := model.GetBlockRefText(id)
|
||
if "" == refText {
|
||
// 空块返回 id https://github.com/siyuan-note/siyuan/issues/10259
|
||
refText = id
|
||
ret.Data = refText
|
||
return
|
||
}
|
||
|
||
if strings.Count(refText, "\\") == len(refText) {
|
||
// 全部都是 \ 的话使用实体 https://github.com/siyuan-note/siyuan/issues/11473
|
||
refText = strings.ReplaceAll(refText, "\\", "\")
|
||
ret.Data = refText
|
||
return
|
||
}
|
||
|
||
ret.Data = refText
|
||
}
|
||
|
||
func getRefIDs(c *gin.Context) {
|
||
ret := gulu.Ret.NewResult()
|
||
defer c.JSON(http.StatusOK, ret)
|
||
|
||
arg, ok := util.JsonArg(c, ret)
|
||
if !ok {
|
||
return
|
||
}
|
||
|
||
if nil == arg["id"] {
|
||
arg["id"] = ""
|
||
}
|
||
|
||
id := arg["id"].(string)
|
||
refIDs, refTexts, defIDs := model.GetBlockRefs(id)
|
||
ret.Data = map[string][]string{
|
||
"refIDs": refIDs,
|
||
"refTexts": refTexts,
|
||
"defIDs": defIDs,
|
||
}
|
||
}
|
||
|
||
func getRefIDsByFileAnnotationID(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)
|
||
refIDs, refTexts := model.GetBlockRefIDsByFileAnnotationID(id)
|
||
ret.Data = map[string][]string{
|
||
"refIDs": refIDs,
|
||
"refTexts": refTexts,
|
||
}
|
||
}
|
||
|
||
func getBlockDefIDsByRefText(c *gin.Context) {
|
||
ret := gulu.Ret.NewResult()
|
||
defer c.JSON(http.StatusOK, ret)
|
||
|
||
arg, ok := util.JsonArg(c, ret)
|
||
if !ok {
|
||
return
|
||
}
|
||
|
||
anchor := arg["anchor"].(string)
|
||
excludeIDsArg := arg["excludeIDs"].([]interface{})
|
||
var excludeIDs []string
|
||
for _, excludeID := range excludeIDsArg {
|
||
excludeIDs = append(excludeIDs, excludeID.(string))
|
||
}
|
||
excludeIDs = nil // 不限制虚拟引用搜索自己 https://ld246.com/article/1633243424177
|
||
ids := model.GetBlockDefIDsByRefText(anchor, excludeIDs)
|
||
ret.Data = ids
|
||
}
|
||
|
||
func getBlockBreadcrumb(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)
|
||
excludeTypesArg := arg["excludeTypes"]
|
||
var excludeTypes []string
|
||
if nil != excludeTypesArg {
|
||
for _, excludeType := range excludeTypesArg.([]interface{}) {
|
||
excludeTypes = append(excludeTypes, excludeType.(string))
|
||
}
|
||
}
|
||
|
||
blockPath, err := model.BuildBlockBreadcrumb(id, excludeTypes)
|
||
if err != nil {
|
||
ret.Code = -1
|
||
ret.Msg = err.Error()
|
||
return
|
||
}
|
||
|
||
ret.Data = blockPath
|
||
}
|
||
|
||
func getBlockIndex(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)
|
||
index := model.GetBlockIndex(id)
|
||
ret.Data = index
|
||
}
|
||
|
||
func getBlocksIndexes(c *gin.Context) {
|
||
ret := gulu.Ret.NewResult()
|
||
defer c.JSON(http.StatusOK, ret)
|
||
|
||
arg, ok := util.JsonArg(c, ret)
|
||
if !ok {
|
||
return
|
||
}
|
||
|
||
idsArg := arg["ids"].([]interface{})
|
||
var ids []string
|
||
for _, id := range idsArg {
|
||
ids = append(ids, id.(string))
|
||
}
|
||
index := model.GetBlocksIndexes(ids)
|
||
ret.Data = index
|
||
}
|
||
|
||
func getBlockInfo(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)
|
||
|
||
// 仅在此处使用带重建索引的加载函数,其他地方不要使用
|
||
tree, err := model.LoadTreeByBlockIDWithReindex(id)
|
||
if errors.Is(err, model.ErrIndexing) {
|
||
ret.Code = 3
|
||
ret.Msg = model.Conf.Language(56)
|
||
return
|
||
}
|
||
|
||
block, _ := model.GetBlock(id, tree)
|
||
if nil == block {
|
||
ret.Code = -1
|
||
ret.Msg = fmt.Sprintf(model.Conf.Language(15), id)
|
||
return
|
||
}
|
||
|
||
var rootChildID string
|
||
b := block
|
||
for i := 0; i < 128; i++ {
|
||
parentID := b.ParentID
|
||
if "" == parentID {
|
||
rootChildID = b.ID
|
||
break
|
||
}
|
||
if b, _ = model.GetBlock(parentID, tree); nil == b {
|
||
logging.LogErrorf("not found parent")
|
||
break
|
||
}
|
||
}
|
||
|
||
root, err := model.GetBlock(block.RootID, tree)
|
||
if errors.Is(err, model.ErrIndexing) {
|
||
ret.Code = 3
|
||
ret.Data = model.Conf.Language(56)
|
||
return
|
||
}
|
||
rootTitle := root.IAL["title"]
|
||
rootTitle = html.UnescapeString(rootTitle)
|
||
ret.Data = map[string]string{
|
||
"box": block.Box,
|
||
"path": block.Path,
|
||
"rootID": block.RootID,
|
||
"rootTitle": rootTitle,
|
||
"rootChildID": rootChildID,
|
||
"rootIcon": root.IAL["icon"],
|
||
}
|
||
}
|
||
|
||
func getBlockDOM(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)
|
||
dom := model.GetBlockDOM(id)
|
||
ret.Data = map[string]string{
|
||
"id": id,
|
||
"dom": dom,
|
||
}
|
||
}
|
||
|
||
func getBlockKramdown(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
|
||
}
|
||
|
||
// md:Markdown 标记符模式,使用标记符导出
|
||
// textmark:文本标记模式,使用 span 标签导出
|
||
// https://github.com/siyuan-note/siyuan/issues/13183
|
||
mode := "md"
|
||
if modeArg := arg["mode"]; nil != modeArg {
|
||
mode = modeArg.(string)
|
||
if "md" != mode && "textmark" != mode {
|
||
ret.Code = -1
|
||
ret.Msg = "Invalid mode"
|
||
return
|
||
}
|
||
}
|
||
|
||
kramdown := model.GetBlockKramdown(id, mode)
|
||
ret.Data = map[string]string{
|
||
"id": id,
|
||
"kramdown": kramdown,
|
||
}
|
||
}
|
||
|
||
func getChildBlocks(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
|
||
}
|
||
|
||
ret.Data = model.GetChildBlocks(id)
|
||
}
|
||
|
||
func getTailChildBlocks(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
|
||
}
|
||
|
||
var n int
|
||
nArg := arg["n"]
|
||
if nil != nArg {
|
||
n = int(nArg.(float64))
|
||
}
|
||
if 1 > n {
|
||
n = 7
|
||
}
|
||
|
||
ret.Data = model.GetTailChildBlocks(id, n)
|
||
}
|