A privacy-first, self-hosted, fully open source personal knowledge management software, written in typescript and golang. (PERSONAL FORK)
1// SiYuan - Refactor your thinking
2// Copyright (c) 2020-present, b3log.org
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU Affero General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU Affero General Public License for more details.
13//
14// You should have received a copy of the GNU Affero General Public License
15// along with this program. If not, see <https://www.gnu.org/licenses/>.
16
17package model
18
19import (
20 "path"
21 "strings"
22
23 "github.com/88250/lute/ast"
24 "github.com/88250/lute/parse"
25 "github.com/siyuan-note/siyuan/kernel/sql"
26 "github.com/siyuan-note/siyuan/kernel/treenode"
27 "github.com/siyuan-note/siyuan/kernel/util"
28)
29
30func ListItem2Doc(srcListItemID, targetBoxID, targetPath, previousPath string) (srcRootBlockID, newTargetPath string, err error) {
31 FlushTxQueue()
32
33 srcTree, _ := LoadTreeByBlockID(srcListItemID)
34 if nil == srcTree {
35 err = ErrBlockNotFound
36 return
37 }
38 srcRootBlockID = srcTree.Root.ID
39
40 listItemNode := treenode.GetNodeInTree(srcTree, srcListItemID)
41 if nil == listItemNode {
42 err = ErrBlockNotFound
43 return
44 }
45
46 box := Conf.Box(targetBoxID)
47 listItemText := sql.GetContainerText(listItemNode)
48 listItemText = util.FilterFileName(listItemText)
49
50 moveToRoot := "/" == targetPath
51 toHP := path.Join("/", listItemText)
52 toFolder := "/"
53
54 if "" != previousPath {
55 previousDoc := treenode.GetBlockTreeRootByPath(targetBoxID, previousPath)
56 if nil == previousDoc {
57 err = ErrBlockNotFound
58 return
59 }
60 parentPath := path.Dir(previousPath)
61 if "/" != parentPath {
62 parentPath = strings.TrimSuffix(parentPath, "/") + ".sy"
63 parentDoc := treenode.GetBlockTreeRootByPath(targetBoxID, parentPath)
64 if nil == parentDoc {
65 err = ErrBlockNotFound
66 return
67 }
68 toHP = path.Join(parentDoc.HPath, listItemText)
69 toFolder = path.Join(path.Dir(parentPath), parentDoc.ID)
70 }
71 } else {
72 if !moveToRoot {
73 parentDoc := treenode.GetBlockTreeRootByPath(targetBoxID, targetPath)
74 if nil == parentDoc {
75 err = ErrBlockNotFound
76 return
77 }
78 toHP = path.Join(parentDoc.HPath, listItemText)
79 toFolder = path.Join(path.Dir(targetPath), parentDoc.ID)
80 }
81 }
82
83 newTargetPath = path.Join(toFolder, srcListItemID+".sy")
84 if !box.Exist(toFolder) {
85 if err = box.MkdirAll(toFolder); err != nil {
86 return
87 }
88 }
89
90 var children []*ast.Node
91 for c := listItemNode.FirstChild; nil != c; c = c.Next {
92 if c.IsMarker() {
93 continue
94 }
95 children = append(children, c)
96 }
97 if 1 > len(children) {
98 newNode := treenode.NewParagraph("")
99 children = append(children, newNode)
100 }
101
102 luteEngine := util.NewLute()
103 newTree := &parse.Tree{Root: &ast.Node{Type: ast.NodeDocument, ID: srcListItemID}, Context: &parse.Context{ParseOption: luteEngine.ParseOptions}}
104 for _, c := range children {
105 newTree.Root.AppendChild(c)
106 }
107 newTree.ID = srcListItemID
108 newTree.Path = newTargetPath
109 newTree.HPath = toHP
110 listItemNode.SetIALAttr("type", "doc")
111 listItemNode.SetIALAttr("id", srcListItemID)
112 listItemNode.SetIALAttr("title", listItemText)
113 listItemNode.RemoveIALAttr("fold")
114 newTree.Root.KramdownIAL = listItemNode.KramdownIAL
115 srcLiParent := listItemNode.Parent
116 listItemNode.Unlink()
117 if nil != srcLiParent && nil == srcLiParent.FirstChild {
118 srcLiParent.Unlink()
119 }
120 srcTree.Root.SetIALAttr("updated", util.CurrentTimeSecondsStr())
121 if nil == srcTree.Root.FirstChild {
122 srcTree.Root.AppendChild(treenode.NewParagraph(""))
123 }
124 treenode.RemoveBlockTreesByRootID(srcTree.ID)
125 if err = indexWriteTreeUpsertQueue(srcTree); err != nil {
126 return "", "", err
127 }
128
129 newTree.Box, newTree.Path = targetBoxID, newTargetPath
130 newTree.Root.SetIALAttr("updated", util.CurrentTimeSecondsStr())
131 newTree.Root.Spec = "1"
132 if "" != previousPath {
133 box.addSort(previousPath, newTree.ID)
134 } else {
135 box.addMinSort(path.Dir(newTargetPath), newTree.ID)
136 }
137 if err = indexWriteTreeUpsertQueue(newTree); err != nil {
138 return "", "", err
139 }
140 IncSync()
141 go func() {
142 RefreshBacklink(srcTree.ID)
143 RefreshBacklink(newTree.ID)
144 ResetVirtualBlockRefCache()
145 }()
146 return
147}