mount.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  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. package model
  17. import (
  18. "errors"
  19. "fmt"
  20. "os"
  21. "path/filepath"
  22. "strings"
  23. "time"
  24. "unicode/utf8"
  25. "github.com/88250/gulu"
  26. "github.com/88250/lute/ast"
  27. "github.com/siyuan-note/filelock"
  28. "github.com/siyuan-note/logging"
  29. "github.com/siyuan-note/siyuan/kernel/treenode"
  30. "github.com/siyuan-note/siyuan/kernel/util"
  31. )
  32. func CreateBox(name string) (id string, err error) {
  33. name = gulu.Str.RemoveInvisible(name)
  34. if 512 < utf8.RuneCountInString(name) {
  35. // 限制笔记本名和文档名最大长度为 `512` https://github.com/siyuan-note/siyuan/issues/6299
  36. err = errors.New(Conf.Language(106))
  37. return
  38. }
  39. id = ast.NewNodeID()
  40. boxLocalPath := filepath.Join(util.DataDir, id)
  41. err = os.MkdirAll(boxLocalPath, 0755)
  42. if nil != err {
  43. return
  44. }
  45. box := &Box{ID: id, Name: name}
  46. boxConf := box.GetConf()
  47. boxConf.Name = name
  48. box.SaveConf(boxConf)
  49. IncSync()
  50. return
  51. }
  52. func RenameBox(boxID, name string) (err error) {
  53. box := Conf.Box(boxID)
  54. if nil == box {
  55. return errors.New(Conf.Language(0))
  56. }
  57. boxConf := box.GetConf()
  58. boxConf.Name = name
  59. box.Name = name
  60. box.SaveConf(boxConf)
  61. IncSync()
  62. return
  63. }
  64. func RemoveBox(boxID string) (err error) {
  65. WaitForWritingFiles()
  66. if util.IsReservedFilename(boxID) {
  67. return errors.New(fmt.Sprintf("can not remove [%s] caused by it is a reserved file", boxID))
  68. }
  69. localPath := filepath.Join(util.DataDir, boxID)
  70. if !gulu.File.IsExist(localPath) {
  71. return
  72. }
  73. if !gulu.File.IsDir(localPath) {
  74. return errors.New(fmt.Sprintf("can not remove [%s] caused by it is not a dir", boxID))
  75. }
  76. if !IsUserGuide(boxID) {
  77. var historyDir string
  78. historyDir, err = GetHistoryDir(HistoryOpDelete)
  79. if nil != err {
  80. logging.LogErrorf("get history dir failed: %s", err)
  81. return
  82. }
  83. p := strings.TrimPrefix(localPath, util.DataDir)
  84. historyPath := filepath.Join(historyDir, p)
  85. if err = filelock.Copy(localPath, historyPath); nil != err {
  86. logging.LogErrorf("gen sync history failed: %s", err)
  87. return
  88. }
  89. copyBoxAssetsToDataAssets(boxID)
  90. }
  91. unmount0(boxID)
  92. if err = filelock.Remove(localPath); nil != err {
  93. return
  94. }
  95. IncSync()
  96. return
  97. }
  98. func Unmount(boxID string) {
  99. WaitForWritingFiles()
  100. unmount0(boxID)
  101. evt := util.NewCmdResult("unmount", 0, util.PushModeBroadcast)
  102. evt.Data = map[string]interface{}{
  103. "box": boxID,
  104. }
  105. util.PushEvent(evt)
  106. }
  107. func unmount0(boxID string) {
  108. box := Conf.Box(boxID)
  109. if nil == box {
  110. return
  111. }
  112. boxConf := box.GetConf()
  113. boxConf.Closed = true
  114. box.SaveConf(boxConf)
  115. box.Unindex()
  116. }
  117. func Mount(boxID string) (alreadyMount bool, err error) {
  118. WaitForWritingFiles()
  119. localPath := filepath.Join(util.DataDir, boxID)
  120. var reMountGuide bool
  121. if IsUserGuide(boxID) {
  122. // 重新挂载帮助文档
  123. guideBox := Conf.Box(boxID)
  124. if nil != guideBox {
  125. unmount0(guideBox.ID)
  126. reMountGuide = true
  127. }
  128. if err = filelock.Remove(localPath); nil != err {
  129. return
  130. }
  131. p := filepath.Join(util.WorkingDir, "guide", boxID)
  132. if err = filelock.Copy(p, localPath); nil != err {
  133. return
  134. }
  135. if box := Conf.Box(boxID); nil != box {
  136. boxConf := box.GetConf()
  137. boxConf.Closed = true
  138. box.SaveConf(boxConf)
  139. }
  140. if Conf.OpenHelp {
  141. Conf.OpenHelp = false
  142. Conf.Save()
  143. }
  144. go func() {
  145. time.Sleep(time.Second * 3)
  146. util.PushErrMsg(Conf.Language(52), 7000)
  147. // 每次打开帮助文档时自动检查版本更新并提醒 https://github.com/siyuan-note/siyuan/issues/5057
  148. time.Sleep(time.Second * 10)
  149. CheckUpdate(true)
  150. }()
  151. }
  152. if !gulu.File.IsDir(localPath) {
  153. return false, errors.New("can not open file, just support open folder only")
  154. }
  155. for _, box := range Conf.GetOpenedBoxes() {
  156. if box.ID == boxID {
  157. return true, nil
  158. }
  159. }
  160. box := &Box{ID: boxID}
  161. boxConf := box.GetConf()
  162. boxConf.Closed = false
  163. box.SaveConf(boxConf)
  164. box.Index()
  165. // 缓存根一级的文档树展开
  166. ListDocTree(box.ID, "/", util.SortModeUnassigned, false, Conf.FileTree.MaxListCount)
  167. treenode.SaveBlockTree(false)
  168. util.ClearPushProgress(100)
  169. if IsUserGuide(boxID) {
  170. go func() {
  171. var startID string
  172. i := 0
  173. for ; i < 70; i++ {
  174. time.Sleep(100 * time.Millisecond)
  175. guideStartID := map[string]string{
  176. "20210808180117-czj9bvb": "20200812220555-lj3enxa",
  177. "20211226090932-5lcq56f": "20211226115423-d5z1joq",
  178. "20210808180117-6v0mkxr": "20200923234011-ieuun1p",
  179. }
  180. startID = guideStartID[boxID]
  181. if nil != treenode.GetBlockTree(startID) {
  182. util.BroadcastByType("main", "openFileById", 0, "", map[string]interface{}{
  183. "id": startID,
  184. })
  185. break
  186. }
  187. }
  188. }()
  189. }
  190. if reMountGuide {
  191. return true, nil
  192. }
  193. return false, nil
  194. }
  195. func IsUserGuide(boxID string) bool {
  196. return "20210808180117-czj9bvb" == boxID || "20210808180117-6v0mkxr" == boxID || "20211226090932-5lcq56f" == boxID
  197. }