A privacy-first, self-hosted, fully open source personal knowledge management software, written in typescript and golang. (PERSONAL FORK)
at lambda-fork/main 207 lines 5.9 kB view raw
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 "errors" 21 "fmt" 22 "sort" 23 "strings" 24 25 "github.com/88250/gulu" 26 "github.com/88250/lute/parse" 27 "github.com/siyuan-note/logging" 28 "github.com/siyuan-note/siyuan/kernel/av" 29 "github.com/siyuan-note/siyuan/kernel/cache" 30 "github.com/siyuan-note/siyuan/kernel/sql" 31 "github.com/siyuan-note/siyuan/kernel/treenode" 32 "github.com/siyuan-note/siyuan/kernel/util" 33) 34 35func RemoveBookmark(bookmark string) (err error) { 36 util.PushEndlessProgress(Conf.Language(116)) 37 38 bookmarks := sql.QueryBookmarkBlocksByKeyword(bookmark) 39 treeBlocks := map[string][]string{} 40 for _, tag := range bookmarks { 41 if blocks, ok := treeBlocks[tag.RootID]; !ok { 42 treeBlocks[tag.RootID] = []string{tag.ID} 43 } else { 44 treeBlocks[tag.RootID] = append(blocks, tag.ID) 45 } 46 } 47 48 for treeID, blocks := range treeBlocks { 49 util.PushEndlessProgress("[" + treeID + "]") 50 tree, e := LoadTreeByBlockID(treeID) 51 if nil != e { 52 util.PushClearProgress() 53 return e 54 } 55 56 for _, blockID := range blocks { 57 node := treenode.GetNodeInTree(tree, blockID) 58 if nil == node { 59 continue 60 } 61 62 if bookmarkAttrVal := node.IALAttr("bookmark"); bookmarkAttrVal == bookmark { 63 node.RemoveIALAttr("bookmark") 64 cache.PutBlockIAL(node.ID, parse.IAL2Map(node.KramdownIAL)) 65 } 66 } 67 68 util.PushEndlessProgress(fmt.Sprintf(Conf.Language(111), util.EscapeHTML(tree.Root.IALAttr("title")))) 69 if err = writeTreeUpsertQueue(tree); err != nil { 70 util.ClearPushProgress(100) 71 return 72 } 73 util.RandomSleep(50, 150) 74 } 75 76 util.ReloadUI() 77 return 78} 79 80func RenameBookmark(oldBookmark, newBookmark string) (err error) { 81 if invalidChar := treenode.ContainsMarker(newBookmark); "" != invalidChar { 82 return errors.New(fmt.Sprintf(Conf.Language(112), invalidChar)) 83 } 84 85 newBookmark = strings.TrimSpace(newBookmark) 86 if "" == newBookmark { 87 return errors.New(Conf.Language(126)) 88 } 89 90 if oldBookmark == newBookmark { 91 return 92 } 93 94 util.PushEndlessProgress(Conf.Language(110)) 95 96 bookmarks := sql.QueryBookmarkBlocksByKeyword(oldBookmark) 97 treeBlocks := map[string][]string{} 98 for _, tag := range bookmarks { 99 if blocks, ok := treeBlocks[tag.RootID]; !ok { 100 treeBlocks[tag.RootID] = []string{tag.ID} 101 } else { 102 treeBlocks[tag.RootID] = append(blocks, tag.ID) 103 } 104 } 105 106 for treeID, blocks := range treeBlocks { 107 util.PushEndlessProgress("[" + treeID + "]") 108 tree, e := LoadTreeByBlockID(treeID) 109 if nil != e { 110 util.ClearPushProgress(100) 111 return e 112 } 113 114 for _, blockID := range blocks { 115 node := treenode.GetNodeInTree(tree, blockID) 116 if nil == node { 117 continue 118 } 119 120 if bookmarkAttrVal := node.IALAttr("bookmark"); bookmarkAttrVal == oldBookmark { 121 node.SetIALAttr("bookmark", newBookmark) 122 cache.PutBlockIAL(node.ID, parse.IAL2Map(node.KramdownIAL)) 123 } 124 } 125 126 util.PushEndlessProgress(fmt.Sprintf(Conf.Language(111), util.EscapeHTML(tree.Root.IALAttr("title")))) 127 if err = writeTreeUpsertQueue(tree); err != nil { 128 util.ClearPushProgress(100) 129 return 130 } 131 util.RandomSleep(50, 150) 132 } 133 134 util.ReloadUI() 135 return 136} 137 138type BookmarkLabel string 139type BookmarkBlocks []*Block 140 141type Bookmark struct { 142 Name BookmarkLabel `json:"name"` 143 Blocks []*Block `json:"blocks"` 144 Type string `json:"type"` // "bookmark" 145 Depth int `json:"depth"` 146 Count int `json:"count"` 147} 148 149type Bookmarks []*Bookmark 150 151func (s Bookmarks) Len() int { return len(s) } 152func (s Bookmarks) Swap(i, j int) { s[i], s[j] = s[j], s[i] } 153func (s Bookmarks) Less(i, j int) bool { return s[i].Name < s[j].Name } 154 155func BookmarkLabels() (ret []string) { 156 ret = sql.QueryBookmarkLabels() 157 return 158} 159 160func BuildBookmark() (ret *Bookmarks) { 161 FlushTxQueue() 162 sql.FlushQueue() 163 164 ret = &Bookmarks{} 165 sqlBlocks := sql.QueryBookmarkBlocks() 166 167 labelBlocks := map[BookmarkLabel]BookmarkBlocks{} 168 blocks := fromSQLBlocks(&sqlBlocks, "", 0) 169 luteEngine := NewLute() 170 for _, block := range blocks { 171 if "" != block.Name { 172 // Blocks in the bookmark panel display their name instead of content https://github.com/siyuan-note/siyuan/issues/8514 173 block.Content = block.Name 174 } else if "NodeAttributeView" == block.Type { 175 // Display database title in bookmark panel https://github.com/siyuan-note/siyuan/issues/11666 176 avID := gulu.Str.SubStringBetween(block.Markdown, "av-id=\"", "\"") 177 block.Content, _ = av.GetAttributeViewName(avID) 178 } else { 179 // Improve bookmark panel rendering https://github.com/siyuan-note/siyuan/issues/9361 180 tree, err := LoadTreeByBlockID(block.ID) 181 if err != nil { 182 logging.LogErrorf("parse block [%s] failed: %s", block.ID, err) 183 } else { 184 n := treenode.GetNodeInTree(tree, block.ID) 185 block.Content = renderOutline(n, luteEngine) 186 } 187 } 188 189 label := BookmarkLabel(block.IAL["bookmark"]) 190 if bs, ok := labelBlocks[label]; ok { 191 bs = append(bs, block) 192 labelBlocks[label] = bs 193 } else { 194 labelBlocks[label] = []*Block{block} 195 } 196 } 197 198 for label, bs := range labelBlocks { 199 for _, b := range bs { 200 b.Depth = 1 201 } 202 *ret = append(*ret, &Bookmark{Name: label, Blocks: bs, Type: "bookmark", Count: len(bs)}) 203 } 204 205 sort.Sort(ret) 206 return 207}