filetree.go 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851
  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 api
  17. import (
  18. "fmt"
  19. "math"
  20. "net/http"
  21. "path"
  22. "regexp"
  23. "strings"
  24. "unicode/utf8"
  25. "github.com/88250/gulu"
  26. "github.com/88250/lute/ast"
  27. "github.com/gin-gonic/gin"
  28. "github.com/siyuan-note/siyuan/kernel/filesys"
  29. "github.com/siyuan-note/siyuan/kernel/model"
  30. "github.com/siyuan-note/siyuan/kernel/util"
  31. )
  32. func upsertIndexes(c *gin.Context) {
  33. ret := gulu.Ret.NewResult()
  34. defer c.JSON(http.StatusOK, ret)
  35. arg, ok := util.JsonArg(c, ret)
  36. if !ok {
  37. return
  38. }
  39. pathsArg := arg["paths"].([]interface{})
  40. var paths []string
  41. for _, p := range pathsArg {
  42. paths = append(paths, p.(string))
  43. }
  44. model.UpsertIndexes(paths)
  45. }
  46. func removeIndexes(c *gin.Context) {
  47. ret := gulu.Ret.NewResult()
  48. defer c.JSON(http.StatusOK, ret)
  49. arg, ok := util.JsonArg(c, ret)
  50. if !ok {
  51. return
  52. }
  53. pathsArg := arg["paths"].([]interface{})
  54. var paths []string
  55. for _, p := range pathsArg {
  56. paths = append(paths, p.(string))
  57. }
  58. model.RemoveIndexes(paths)
  59. }
  60. func refreshFiletree(c *gin.Context) {
  61. ret := gulu.Ret.NewResult()
  62. defer c.JSON(http.StatusOK, ret)
  63. model.FullReindex()
  64. }
  65. func doc2Heading(c *gin.Context) {
  66. ret := gulu.Ret.NewResult()
  67. defer c.JSON(http.StatusOK, ret)
  68. arg, ok := util.JsonArg(c, ret)
  69. if !ok {
  70. return
  71. }
  72. srcID := arg["srcID"].(string)
  73. targetID := arg["targetID"].(string)
  74. after := arg["after"].(bool)
  75. srcTreeBox, srcTreePath, err := model.Doc2Heading(srcID, targetID, after)
  76. if nil != err {
  77. ret.Code = -1
  78. ret.Msg = err.Error()
  79. ret.Data = map[string]interface{}{"closeTimeout": 5000}
  80. return
  81. }
  82. ret.Data = map[string]interface{}{
  83. "srcTreeBox": srcTreeBox,
  84. "srcTreePath": srcTreePath,
  85. }
  86. }
  87. func heading2Doc(c *gin.Context) {
  88. ret := gulu.Ret.NewResult()
  89. defer c.JSON(http.StatusOK, ret)
  90. arg, ok := util.JsonArg(c, ret)
  91. if !ok {
  92. return
  93. }
  94. srcHeadingID := arg["srcHeadingID"].(string)
  95. targetNotebook := arg["targetNoteBook"].(string)
  96. targetPath := arg["targetPath"].(string)
  97. srcRootBlockID, targetPath, err := model.Heading2Doc(srcHeadingID, targetNotebook, targetPath)
  98. if nil != err {
  99. ret.Code = -1
  100. ret.Msg = err.Error()
  101. ret.Data = map[string]interface{}{"closeTimeout": 5000}
  102. return
  103. }
  104. model.WaitForWritingFiles()
  105. luteEngine := util.NewLute()
  106. tree, err := filesys.LoadTree(targetNotebook, targetPath, luteEngine)
  107. if nil != err {
  108. ret.Code = -1
  109. ret.Msg = err.Error()
  110. return
  111. }
  112. name := path.Base(targetPath)
  113. box := model.Conf.Box(targetNotebook)
  114. files, _, _ := model.ListDocTree(targetNotebook, path.Dir(targetPath), util.SortModeUnassigned, false, false, model.Conf.FileTree.MaxListCount)
  115. evt := util.NewCmdResult("heading2doc", 0, util.PushModeBroadcast)
  116. evt.Data = map[string]interface{}{
  117. "box": box,
  118. "path": targetPath,
  119. "files": files,
  120. "name": name,
  121. "id": tree.Root.ID,
  122. "srcRootBlockID": srcRootBlockID,
  123. }
  124. evt.Callback = arg["callback"]
  125. util.PushEvent(evt)
  126. }
  127. func li2Doc(c *gin.Context) {
  128. ret := gulu.Ret.NewResult()
  129. defer c.JSON(http.StatusOK, ret)
  130. arg, ok := util.JsonArg(c, ret)
  131. if !ok {
  132. return
  133. }
  134. srcListItemID := arg["srcListItemID"].(string)
  135. targetNotebook := arg["targetNoteBook"].(string)
  136. targetPath := arg["targetPath"].(string)
  137. srcRootBlockID, targetPath, err := model.ListItem2Doc(srcListItemID, targetNotebook, targetPath)
  138. if nil != err {
  139. ret.Code = -1
  140. ret.Msg = err.Error()
  141. ret.Data = map[string]interface{}{"closeTimeout": 5000}
  142. return
  143. }
  144. model.WaitForWritingFiles()
  145. luteEngine := util.NewLute()
  146. tree, err := filesys.LoadTree(targetNotebook, targetPath, luteEngine)
  147. if nil != err {
  148. ret.Code = -1
  149. ret.Msg = err.Error()
  150. return
  151. }
  152. name := path.Base(targetPath)
  153. box := model.Conf.Box(targetNotebook)
  154. files, _, _ := model.ListDocTree(targetNotebook, path.Dir(targetPath), util.SortModeUnassigned, false, false, model.Conf.FileTree.MaxListCount)
  155. evt := util.NewCmdResult("li2doc", 0, util.PushModeBroadcast)
  156. evt.Data = map[string]interface{}{
  157. "box": box,
  158. "path": targetPath,
  159. "files": files,
  160. "name": name,
  161. "id": tree.Root.ID,
  162. "srcRootBlockID": srcRootBlockID,
  163. }
  164. evt.Callback = arg["callback"]
  165. util.PushEvent(evt)
  166. }
  167. func getHPathByPath(c *gin.Context) {
  168. ret := gulu.Ret.NewResult()
  169. defer c.JSON(http.StatusOK, ret)
  170. arg, ok := util.JsonArg(c, ret)
  171. if !ok {
  172. return
  173. }
  174. notebook := arg["notebook"].(string)
  175. if util.InvalidIDPattern(notebook, ret) {
  176. return
  177. }
  178. p := arg["path"].(string)
  179. hPath, err := model.GetHPathByPath(notebook, p)
  180. if nil != err {
  181. ret.Code = -1
  182. ret.Msg = err.Error()
  183. return
  184. }
  185. ret.Data = hPath
  186. }
  187. func getHPathsByPaths(c *gin.Context) {
  188. ret := gulu.Ret.NewResult()
  189. defer c.JSON(http.StatusOK, ret)
  190. arg, ok := util.JsonArg(c, ret)
  191. if !ok {
  192. return
  193. }
  194. pathsArg := arg["paths"].([]interface{})
  195. var paths []string
  196. for _, p := range pathsArg {
  197. paths = append(paths, p.(string))
  198. }
  199. hPath, err := model.GetHPathsByPaths(paths)
  200. if nil != err {
  201. ret.Code = -1
  202. ret.Msg = err.Error()
  203. return
  204. }
  205. ret.Data = hPath
  206. }
  207. func getHPathByID(c *gin.Context) {
  208. ret := gulu.Ret.NewResult()
  209. defer c.JSON(http.StatusOK, ret)
  210. arg, ok := util.JsonArg(c, ret)
  211. if !ok {
  212. return
  213. }
  214. id := arg["id"].(string)
  215. if util.InvalidIDPattern(id, ret) {
  216. return
  217. }
  218. hPath, err := model.GetHPathByID(id)
  219. if nil != err {
  220. ret.Code = -1
  221. ret.Msg = err.Error()
  222. return
  223. }
  224. ret.Data = hPath
  225. }
  226. func getFullHPathByID(c *gin.Context) {
  227. ret := gulu.Ret.NewResult()
  228. defer c.JSON(http.StatusOK, ret)
  229. arg, ok := util.JsonArg(c, ret)
  230. if !ok {
  231. return
  232. }
  233. if nil == arg["id"] {
  234. return
  235. }
  236. id := arg["id"].(string)
  237. hPath, err := model.GetFullHPathByID(id)
  238. if nil != err {
  239. ret.Code = -1
  240. ret.Msg = err.Error()
  241. return
  242. }
  243. ret.Data = hPath
  244. }
  245. func getIDsByHPath(c *gin.Context) {
  246. ret := gulu.Ret.NewResult()
  247. defer c.JSON(http.StatusOK, ret)
  248. arg, ok := util.JsonArg(c, ret)
  249. if !ok {
  250. return
  251. }
  252. if nil == arg["path"] {
  253. return
  254. }
  255. if nil == arg["notebook"] {
  256. return
  257. }
  258. notebook := arg["notebook"].(string)
  259. if util.InvalidIDPattern(notebook, ret) {
  260. return
  261. }
  262. p := arg["path"].(string)
  263. ids, err := model.GetIDsByHPath(p, notebook)
  264. if nil != err {
  265. ret.Code = -1
  266. ret.Msg = err.Error()
  267. return
  268. }
  269. ret.Data = ids
  270. }
  271. func moveDocs(c *gin.Context) {
  272. ret := gulu.Ret.NewResult()
  273. defer c.JSON(http.StatusOK, ret)
  274. arg, ok := util.JsonArg(c, ret)
  275. if !ok {
  276. return
  277. }
  278. var fromPaths []string
  279. fromPathsArg := arg["fromPaths"].([]interface{})
  280. for _, fromPath := range fromPathsArg {
  281. fromPaths = append(fromPaths, fromPath.(string))
  282. }
  283. toPath := arg["toPath"].(string)
  284. toNotebook := arg["toNotebook"].(string)
  285. if util.InvalidIDPattern(toNotebook, ret) {
  286. return
  287. }
  288. callback := arg["callback"]
  289. err := model.MoveDocs(fromPaths, toNotebook, toPath, callback)
  290. if nil != err {
  291. ret.Code = -1
  292. ret.Msg = err.Error()
  293. ret.Data = map[string]interface{}{"closeTimeout": 7000}
  294. return
  295. }
  296. }
  297. func removeDoc(c *gin.Context) {
  298. ret := gulu.Ret.NewResult()
  299. defer c.JSON(http.StatusOK, ret)
  300. arg, ok := util.JsonArg(c, ret)
  301. if !ok {
  302. return
  303. }
  304. notebook := arg["notebook"].(string)
  305. if util.InvalidIDPattern(notebook, ret) {
  306. return
  307. }
  308. p := arg["path"].(string)
  309. model.RemoveDoc(notebook, p)
  310. }
  311. func removeDocs(c *gin.Context) {
  312. ret := gulu.Ret.NewResult()
  313. defer c.JSON(http.StatusOK, ret)
  314. arg, ok := util.JsonArg(c, ret)
  315. if !ok {
  316. return
  317. }
  318. pathsArg := arg["paths"].([]interface{})
  319. var paths []string
  320. for _, path := range pathsArg {
  321. paths = append(paths, path.(string))
  322. }
  323. model.RemoveDocs(paths)
  324. }
  325. func renameDoc(c *gin.Context) {
  326. ret := gulu.Ret.NewResult()
  327. defer c.JSON(http.StatusOK, ret)
  328. arg, ok := util.JsonArg(c, ret)
  329. if !ok {
  330. return
  331. }
  332. notebook := arg["notebook"].(string)
  333. if util.InvalidIDPattern(notebook, ret) {
  334. return
  335. }
  336. p := arg["path"].(string)
  337. title := arg["title"].(string)
  338. err := model.RenameDoc(notebook, p, title)
  339. if nil != err {
  340. ret.Code = -1
  341. ret.Msg = err.Error()
  342. return
  343. }
  344. return
  345. }
  346. func duplicateDoc(c *gin.Context) {
  347. ret := gulu.Ret.NewResult()
  348. defer c.JSON(http.StatusOK, ret)
  349. arg, ok := util.JsonArg(c, ret)
  350. if !ok {
  351. return
  352. }
  353. id := arg["id"].(string)
  354. tree, err := model.LoadTreeByID(id)
  355. if nil != err {
  356. ret.Code = -1
  357. ret.Msg = err.Error()
  358. ret.Data = map[string]interface{}{"closeTimeout": 7000}
  359. return
  360. }
  361. p := tree.Path
  362. notebook := tree.Box
  363. box := model.Conf.Box(notebook)
  364. model.DuplicateDoc(tree)
  365. pushCreate(box, p, tree.Root.ID, arg)
  366. ret.Data = map[string]interface{}{
  367. "id": tree.Root.ID,
  368. "notebook": notebook,
  369. "path": tree.Path,
  370. "hPath": tree.HPath,
  371. }
  372. }
  373. func createDoc(c *gin.Context) {
  374. ret := gulu.Ret.NewResult()
  375. defer c.JSON(http.StatusOK, ret)
  376. arg, ok := util.JsonArg(c, ret)
  377. if !ok {
  378. return
  379. }
  380. notebook := arg["notebook"].(string)
  381. p := arg["path"].(string)
  382. title := arg["title"].(string)
  383. md := arg["md"].(string)
  384. sortsArg := arg["sorts"]
  385. var sorts []string
  386. if nil != sortsArg {
  387. for _, sort := range sortsArg.([]interface{}) {
  388. sorts = append(sorts, sort.(string))
  389. }
  390. }
  391. tree, err := model.CreateDocByMd(notebook, p, title, md, sorts)
  392. if nil != err {
  393. ret.Code = -1
  394. ret.Msg = err.Error()
  395. ret.Data = map[string]interface{}{"closeTimeout": 7000}
  396. return
  397. }
  398. model.WaitForWritingFiles()
  399. box := model.Conf.Box(notebook)
  400. pushCreate(box, p, tree.Root.ID, arg)
  401. ret.Data = map[string]interface{}{
  402. "id": tree.Root.ID,
  403. }
  404. }
  405. func createDailyNote(c *gin.Context) {
  406. ret := gulu.Ret.NewResult()
  407. defer c.JSON(http.StatusOK, ret)
  408. arg, ok := util.JsonArg(c, ret)
  409. if !ok {
  410. return
  411. }
  412. notebook := arg["notebook"].(string)
  413. p, existed, err := model.CreateDailyNote(notebook)
  414. if nil != err {
  415. if model.ErrBoxNotFound == err {
  416. ret.Code = 1
  417. } else {
  418. ret.Code = -1
  419. }
  420. ret.Msg = err.Error()
  421. return
  422. }
  423. model.WaitForWritingFiles()
  424. box := model.Conf.Box(notebook)
  425. luteEngine := util.NewLute()
  426. tree, err := filesys.LoadTree(box.ID, p, luteEngine)
  427. if nil != err {
  428. ret.Code = -1
  429. ret.Msg = err.Error()
  430. return
  431. }
  432. if !existed {
  433. // 只有创建的情况才推送,已经存在的情况不推送
  434. // Creating a dailynote existed no longer expands the doc tree https://github.com/siyuan-note/siyuan/issues/9959
  435. appArg := arg["app"]
  436. app := ""
  437. if nil != appArg {
  438. app = appArg.(string)
  439. }
  440. evt := util.NewCmdResult("createdailynote", 0, util.PushModeBroadcast)
  441. evt.AppId = app
  442. name := path.Base(p)
  443. files, _, _ := model.ListDocTree(box.ID, path.Dir(p), util.SortModeUnassigned, false, false, model.Conf.FileTree.MaxListCount)
  444. evt.Data = map[string]interface{}{
  445. "box": box,
  446. "path": p,
  447. "files": files,
  448. "name": name,
  449. "id": tree.Root.ID,
  450. }
  451. evt.Callback = arg["callback"]
  452. util.PushEvent(evt)
  453. }
  454. ret.Data = map[string]interface{}{
  455. "id": tree.Root.ID,
  456. }
  457. }
  458. func createDocWithMd(c *gin.Context) {
  459. ret := gulu.Ret.NewResult()
  460. defer c.JSON(http.StatusOK, ret)
  461. arg, ok := util.JsonArg(c, ret)
  462. if !ok {
  463. return
  464. }
  465. notebook := arg["notebook"].(string)
  466. if util.InvalidIDPattern(notebook, ret) {
  467. return
  468. }
  469. var parentID string
  470. parentIDArg := arg["parentID"]
  471. if nil != parentIDArg {
  472. parentID = parentIDArg.(string)
  473. }
  474. id := ast.NewNodeID()
  475. idArg := arg["id"]
  476. if nil != idArg {
  477. id = idArg.(string)
  478. }
  479. hPath := arg["path"].(string)
  480. markdown := arg["markdown"].(string)
  481. baseName := path.Base(hPath)
  482. dir := path.Dir(hPath)
  483. r, _ := regexp.Compile("\r\n|\r|\n|\u2028|\u2029|\t|/")
  484. baseName = r.ReplaceAllString(baseName, "")
  485. if 512 < utf8.RuneCountInString(baseName) {
  486. baseName = gulu.Str.SubStr(baseName, 512)
  487. }
  488. hPath = path.Join(dir, baseName)
  489. if !strings.HasPrefix(hPath, "/") {
  490. hPath = "/" + hPath
  491. }
  492. id, err := model.CreateWithMarkdown(notebook, hPath, markdown, parentID, id)
  493. if nil != err {
  494. ret.Code = -1
  495. ret.Msg = err.Error()
  496. return
  497. }
  498. ret.Data = id
  499. model.WaitForWritingFiles()
  500. box := model.Conf.Box(notebook)
  501. b, _ := model.GetBlock(id, nil)
  502. p := b.Path
  503. pushCreate(box, p, id, arg)
  504. }
  505. func getDocCreateSavePath(c *gin.Context) {
  506. ret := gulu.Ret.NewResult()
  507. defer c.JSON(http.StatusOK, ret)
  508. arg, ok := util.JsonArg(c, ret)
  509. if !ok {
  510. return
  511. }
  512. notebook := arg["notebook"].(string)
  513. box := model.Conf.Box(notebook)
  514. docCreateSavePathTpl := model.Conf.FileTree.DocCreateSavePath
  515. if nil != box {
  516. docCreateSavePathTpl = box.GetConf().DocCreateSavePath
  517. }
  518. if "" == docCreateSavePathTpl {
  519. docCreateSavePathTpl = model.Conf.FileTree.DocCreateSavePath
  520. }
  521. docCreateSavePathTpl = strings.TrimSpace(docCreateSavePathTpl)
  522. if "../" == docCreateSavePathTpl {
  523. docCreateSavePathTpl = "../Untitled"
  524. }
  525. p, err := model.RenderGoTemplate(docCreateSavePathTpl)
  526. if nil != err {
  527. ret.Code = -1
  528. ret.Msg = err.Error()
  529. return
  530. }
  531. ret.Data = map[string]interface{}{
  532. "path": p,
  533. }
  534. }
  535. func getRefCreateSavePath(c *gin.Context) {
  536. ret := gulu.Ret.NewResult()
  537. defer c.JSON(http.StatusOK, ret)
  538. arg, ok := util.JsonArg(c, ret)
  539. if !ok {
  540. return
  541. }
  542. notebook := arg["notebook"].(string)
  543. box := model.Conf.Box(notebook)
  544. refCreateSavePath := model.Conf.FileTree.RefCreateSavePath
  545. if nil != box {
  546. refCreateSavePath = box.GetConf().RefCreateSavePath
  547. }
  548. if "" == refCreateSavePath {
  549. refCreateSavePath = model.Conf.FileTree.RefCreateSavePath
  550. }
  551. p, err := model.RenderGoTemplate(refCreateSavePath)
  552. if nil != err {
  553. ret.Code = -1
  554. ret.Msg = err.Error()
  555. return
  556. }
  557. ret.Data = map[string]interface{}{
  558. "path": p,
  559. }
  560. }
  561. func changeSort(c *gin.Context) {
  562. ret := gulu.Ret.NewResult()
  563. defer c.JSON(http.StatusOK, ret)
  564. arg, ok := util.JsonArg(c, ret)
  565. if !ok {
  566. return
  567. }
  568. notebook := arg["notebook"].(string)
  569. pathsArg := arg["paths"].([]interface{})
  570. var paths []string
  571. for _, p := range pathsArg {
  572. paths = append(paths, p.(string))
  573. }
  574. model.ChangeFileTreeSort(notebook, paths)
  575. }
  576. func searchDocs(c *gin.Context) {
  577. ret := gulu.Ret.NewResult()
  578. defer c.JSON(http.StatusOK, ret)
  579. arg, ok := util.JsonArg(c, ret)
  580. if !ok {
  581. return
  582. }
  583. flashcard := false
  584. if arg["flashcard"] != nil {
  585. flashcard = arg["flashcard"].(bool)
  586. }
  587. k := arg["k"].(string)
  588. ret.Data = model.SearchDocsByKeyword(k, flashcard)
  589. }
  590. func listDocsByPath(c *gin.Context) {
  591. ret := gulu.Ret.NewResult()
  592. defer c.JSON(http.StatusOK, ret)
  593. arg, ok := util.JsonArg(c, ret)
  594. if !ok {
  595. return
  596. }
  597. notebook := arg["notebook"].(string)
  598. p := arg["path"].(string)
  599. sortParam := arg["sort"]
  600. sortMode := util.SortModeUnassigned
  601. if nil != sortParam {
  602. sortMode = int(sortParam.(float64))
  603. }
  604. flashcard := false
  605. if arg["flashcard"] != nil {
  606. flashcard = arg["flashcard"].(bool)
  607. }
  608. maxListCount := model.Conf.FileTree.MaxListCount
  609. if arg["maxListCount"] != nil {
  610. // API `listDocsByPath` add an optional parameter `maxListCount` https://github.com/siyuan-note/siyuan/issues/7993
  611. maxListCount = int(arg["maxListCount"].(float64))
  612. if 0 >= maxListCount {
  613. maxListCount = math.MaxInt
  614. }
  615. }
  616. showHidden := false
  617. if arg["showHidden"] != nil {
  618. showHidden = arg["showHidden"].(bool)
  619. }
  620. files, totals, err := model.ListDocTree(notebook, p, sortMode, flashcard, showHidden, maxListCount)
  621. if nil != err {
  622. ret.Code = -1
  623. ret.Msg = err.Error()
  624. return
  625. }
  626. if maxListCount < totals {
  627. // API `listDocsByPath` add an optional parameter `ignoreMaxListHint` https://github.com/siyuan-note/siyuan/issues/10290
  628. ignoreMaxListHintArg := arg["ignoreMaxListHint"]
  629. if nil == ignoreMaxListHintArg || !ignoreMaxListHintArg.(bool) {
  630. util.PushMsg(fmt.Sprintf(model.Conf.Language(48), len(files)), 7000)
  631. }
  632. }
  633. ret.Data = map[string]interface{}{
  634. "box": notebook,
  635. "path": p,
  636. "files": files,
  637. }
  638. }
  639. func getDoc(c *gin.Context) {
  640. ret := gulu.Ret.NewResult()
  641. defer c.JSON(http.StatusOK, ret)
  642. arg, ok := util.JsonArg(c, ret)
  643. if !ok {
  644. return
  645. }
  646. id := arg["id"].(string)
  647. idx := arg["index"]
  648. index := 0
  649. if nil != idx {
  650. index = int(idx.(float64))
  651. }
  652. var query string
  653. if queryArg := arg["query"]; nil != queryArg {
  654. query = queryArg.(string)
  655. }
  656. var queryMethod int
  657. if queryMethodArg := arg["queryMethod"]; nil != queryMethodArg {
  658. queryMethod = int(queryMethodArg.(float64))
  659. }
  660. var queryTypes map[string]bool
  661. if queryTypesArg := arg["queryTypes"]; nil != queryTypesArg {
  662. typesArg := queryTypesArg.(map[string]interface{})
  663. queryTypes = map[string]bool{}
  664. for t, b := range typesArg {
  665. queryTypes[t] = b.(bool)
  666. }
  667. }
  668. m := arg["mode"] // 0: 仅当前 ID,1:向上 2:向下,3:上下都加载,4:加载末尾
  669. mode := 0
  670. if nil != m {
  671. mode = int(m.(float64))
  672. }
  673. s := arg["size"]
  674. size := 102400 // 默认最大加载块数
  675. if nil != s {
  676. size = int(s.(float64))
  677. }
  678. startID := ""
  679. endID := ""
  680. startIDArg := arg["startID"]
  681. endIDArg := arg["endID"]
  682. if nil != startIDArg && nil != endIDArg {
  683. startID = startIDArg.(string)
  684. endID = endIDArg.(string)
  685. size = model.Conf.Editor.DynamicLoadBlocks
  686. }
  687. isBacklinkArg := arg["isBacklink"]
  688. isBacklink := false
  689. if nil != isBacklinkArg {
  690. isBacklink = isBacklinkArg.(bool)
  691. }
  692. blockCount, content, parentID, parent2ID, rootID, typ, eof, scroll, boxID, docPath, isBacklinkExpand, err := model.GetDoc(startID, endID, id, index, query, queryTypes, queryMethod, mode, size, isBacklink)
  693. if model.ErrBlockNotFound == err {
  694. ret.Code = 3
  695. return
  696. }
  697. if nil != err {
  698. ret.Code = 1
  699. ret.Msg = err.Error()
  700. return
  701. }
  702. // 判断是否正在同步中 https://github.com/siyuan-note/siyuan/issues/6290
  703. isSyncing := model.IsSyncingFile(rootID)
  704. ret.Data = map[string]interface{}{
  705. "id": id,
  706. "mode": mode,
  707. "parentID": parentID,
  708. "parent2ID": parent2ID,
  709. "rootID": rootID,
  710. "type": typ,
  711. "content": content,
  712. "blockCount": blockCount,
  713. "eof": eof,
  714. "scroll": scroll,
  715. "box": boxID,
  716. "path": docPath,
  717. "isSyncing": isSyncing,
  718. "isBacklinkExpand": isBacklinkExpand,
  719. }
  720. }
  721. func pushCreate(box *model.Box, p, treeID string, arg map[string]interface{}) {
  722. evt := util.NewCmdResult("create", 0, util.PushModeBroadcast)
  723. name := path.Base(p)
  724. files, _, _ := model.ListDocTree(box.ID, path.Dir(p), util.SortModeUnassigned, false, false, model.Conf.FileTree.MaxListCount)
  725. evt.Data = map[string]interface{}{
  726. "box": box,
  727. "path": p,
  728. "files": files,
  729. "name": name,
  730. "id": treeID,
  731. }
  732. evt.Callback = arg["callback"]
  733. util.PushEvent(evt)
  734. }