block.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630
  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. "errors"
  19. "fmt"
  20. "net/http"
  21. "strings"
  22. "github.com/88250/gulu"
  23. "github.com/88250/lute/html"
  24. "github.com/gin-gonic/gin"
  25. "github.com/siyuan-note/logging"
  26. "github.com/siyuan-note/siyuan/kernel/model"
  27. "github.com/siyuan-note/siyuan/kernel/util"
  28. )
  29. func getBlockTreeInfos(c *gin.Context) {
  30. ret := gulu.Ret.NewResult()
  31. defer c.JSON(http.StatusOK, ret)
  32. arg, ok := util.JsonArg(c, ret)
  33. if !ok {
  34. return
  35. }
  36. var ids []string
  37. idsArg := arg["ids"].([]interface{})
  38. for _, id := range idsArg {
  39. ids = append(ids, id.(string))
  40. }
  41. ret.Data = model.GetBlockTreeInfos(ids)
  42. }
  43. func getBlockSiblingID(c *gin.Context) {
  44. ret := gulu.Ret.NewResult()
  45. defer c.JSON(http.StatusOK, ret)
  46. arg, ok := util.JsonArg(c, ret)
  47. if !ok {
  48. return
  49. }
  50. id := arg["id"].(string)
  51. parent, previous, next := model.GetBlockSiblingID(id)
  52. ret.Data = map[string]string{
  53. "parent": parent,
  54. "next": next,
  55. "previous": previous,
  56. }
  57. }
  58. func transferBlockRef(c *gin.Context) {
  59. ret := gulu.Ret.NewResult()
  60. defer c.JSON(http.StatusOK, ret)
  61. arg, ok := util.JsonArg(c, ret)
  62. if !ok {
  63. return
  64. }
  65. fromID := arg["fromID"].(string)
  66. if util.InvalidIDPattern(fromID, ret) {
  67. return
  68. }
  69. toID := arg["toID"].(string)
  70. if util.InvalidIDPattern(toID, ret) {
  71. return
  72. }
  73. reloadUI := true
  74. if nil != arg["reloadUI"] {
  75. reloadUI = arg["reloadUI"].(bool)
  76. }
  77. var refIDs []string
  78. if nil != arg["refIDs"] {
  79. for _, refID := range arg["refIDs"].([]interface{}) {
  80. refIDs = append(refIDs, refID.(string))
  81. }
  82. }
  83. err := model.TransferBlockRef(fromID, toID, refIDs)
  84. if nil != err {
  85. ret.Code = -1
  86. ret.Msg = err.Error()
  87. ret.Data = map[string]interface{}{"closeTimeout": 7000}
  88. return
  89. }
  90. if reloadUI {
  91. util.ReloadUI()
  92. }
  93. }
  94. func swapBlockRef(c *gin.Context) {
  95. ret := gulu.Ret.NewResult()
  96. defer c.JSON(http.StatusOK, ret)
  97. arg, ok := util.JsonArg(c, ret)
  98. if !ok {
  99. return
  100. }
  101. refID := arg["refID"].(string)
  102. defID := arg["defID"].(string)
  103. includeChildren := arg["includeChildren"].(bool)
  104. err := model.SwapBlockRef(refID, defID, includeChildren)
  105. if nil != err {
  106. ret.Code = -1
  107. ret.Msg = err.Error()
  108. ret.Data = map[string]interface{}{"closeTimeout": 7000}
  109. return
  110. }
  111. }
  112. func getHeadingChildrenIDs(c *gin.Context) {
  113. ret := gulu.Ret.NewResult()
  114. defer c.JSON(http.StatusOK, ret)
  115. arg, ok := util.JsonArg(c, ret)
  116. if !ok {
  117. return
  118. }
  119. id := arg["id"].(string)
  120. ids := model.GetHeadingChildrenIDs(id)
  121. ret.Data = ids
  122. }
  123. func getHeadingChildrenDOM(c *gin.Context) {
  124. ret := gulu.Ret.NewResult()
  125. defer c.JSON(http.StatusOK, ret)
  126. arg, ok := util.JsonArg(c, ret)
  127. if !ok {
  128. return
  129. }
  130. id := arg["id"].(string)
  131. dom := model.GetHeadingChildrenDOM(id)
  132. ret.Data = dom
  133. }
  134. func getHeadingDeleteTransaction(c *gin.Context) {
  135. ret := gulu.Ret.NewResult()
  136. defer c.JSON(http.StatusOK, ret)
  137. arg, ok := util.JsonArg(c, ret)
  138. if !ok {
  139. return
  140. }
  141. id := arg["id"].(string)
  142. transaction, err := model.GetHeadingDeleteTransaction(id)
  143. if nil != err {
  144. ret.Code = -1
  145. ret.Msg = err.Error()
  146. ret.Data = map[string]interface{}{"closeTimeout": 7000}
  147. return
  148. }
  149. ret.Data = transaction
  150. }
  151. func getHeadingLevelTransaction(c *gin.Context) {
  152. ret := gulu.Ret.NewResult()
  153. defer c.JSON(http.StatusOK, ret)
  154. arg, ok := util.JsonArg(c, ret)
  155. if !ok {
  156. return
  157. }
  158. id := arg["id"].(string)
  159. level := int(arg["level"].(float64))
  160. transaction, err := model.GetHeadingLevelTransaction(id, level)
  161. if nil != err {
  162. ret.Code = -1
  163. ret.Msg = err.Error()
  164. ret.Data = map[string]interface{}{"closeTimeout": 7000}
  165. return
  166. }
  167. ret.Data = transaction
  168. }
  169. func setBlockReminder(c *gin.Context) {
  170. ret := gulu.Ret.NewResult()
  171. defer c.JSON(http.StatusOK, ret)
  172. arg, ok := util.JsonArg(c, ret)
  173. if !ok {
  174. return
  175. }
  176. id := arg["id"].(string)
  177. timed := arg["timed"].(string) // yyyyMMddHHmmss
  178. err := model.SetBlockReminder(id, timed)
  179. if nil != err {
  180. ret.Code = -1
  181. ret.Msg = err.Error()
  182. ret.Data = map[string]interface{}{"closeTimeout": 7000}
  183. return
  184. }
  185. }
  186. func checkBlockFold(c *gin.Context) {
  187. ret := gulu.Ret.NewResult()
  188. defer c.JSON(http.StatusOK, ret)
  189. arg, ok := util.JsonArg(c, ret)
  190. if !ok {
  191. return
  192. }
  193. id := arg["id"].(string)
  194. isFolded, isRoot := model.IsBlockFolded(id)
  195. ret.Data = map[string]interface{}{
  196. "isFolded": isFolded,
  197. "isRoot": isRoot,
  198. }
  199. }
  200. func checkBlockExist(c *gin.Context) {
  201. ret := gulu.Ret.NewResult()
  202. defer c.JSON(http.StatusOK, ret)
  203. arg, ok := util.JsonArg(c, ret)
  204. if !ok {
  205. return
  206. }
  207. id := arg["id"].(string)
  208. b, err := model.GetBlock(id, nil)
  209. if errors.Is(err, model.ErrIndexing) {
  210. ret.Code = 0
  211. ret.Data = false
  212. return
  213. }
  214. ret.Data = nil != b
  215. }
  216. func getDocInfo(c *gin.Context) {
  217. ret := gulu.Ret.NewResult()
  218. defer c.JSON(http.StatusOK, ret)
  219. arg, ok := util.JsonArg(c, ret)
  220. if !ok {
  221. return
  222. }
  223. id := arg["id"].(string)
  224. info := model.GetDocInfo(id)
  225. if nil == info {
  226. ret.Code = -1
  227. ret.Msg = fmt.Sprintf(model.Conf.Language(15), id)
  228. return
  229. }
  230. ret.Data = info
  231. }
  232. func getRecentUpdatedBlocks(c *gin.Context) {
  233. ret := gulu.Ret.NewResult()
  234. defer c.JSON(http.StatusOK, ret)
  235. blocks := model.RecentUpdatedBlocks()
  236. ret.Data = blocks
  237. }
  238. func getContentWordCount(c *gin.Context) {
  239. ret := gulu.Ret.NewResult()
  240. defer c.JSON(http.StatusOK, ret)
  241. arg, ok := util.JsonArg(c, ret)
  242. if !ok {
  243. return
  244. }
  245. content := arg["content"].(string)
  246. ret.Data = model.ContentStat(content)
  247. }
  248. func getBlocksWordCount(c *gin.Context) {
  249. ret := gulu.Ret.NewResult()
  250. defer c.JSON(http.StatusOK, ret)
  251. arg, ok := util.JsonArg(c, ret)
  252. if !ok {
  253. return
  254. }
  255. idsArg := arg["ids"].([]interface{})
  256. var ids []string
  257. for _, id := range idsArg {
  258. ids = append(ids, id.(string))
  259. }
  260. ret.Data = model.BlocksWordCount(ids)
  261. }
  262. func getTreeStat(c *gin.Context) {
  263. ret := gulu.Ret.NewResult()
  264. defer c.JSON(http.StatusOK, ret)
  265. arg, ok := util.JsonArg(c, ret)
  266. if !ok {
  267. return
  268. }
  269. id := arg["id"].(string)
  270. ret.Data = model.StatTree(id)
  271. }
  272. func getDOMText(c *gin.Context) {
  273. ret := gulu.Ret.NewResult()
  274. defer c.JSON(http.StatusOK, ret)
  275. arg, ok := util.JsonArg(c, ret)
  276. if !ok {
  277. return
  278. }
  279. dom := arg["dom"].(string)
  280. ret.Data = model.GetDOMText(dom)
  281. }
  282. func getRefText(c *gin.Context) {
  283. ret := gulu.Ret.NewResult()
  284. defer c.JSON(http.StatusOK, ret)
  285. arg, ok := util.JsonArg(c, ret)
  286. if !ok {
  287. return
  288. }
  289. id := arg["id"].(string)
  290. model.WaitForWritingFiles()
  291. refText := model.GetBlockRefText(id)
  292. if "" == refText {
  293. // 空块返回 id https://github.com/siyuan-note/siyuan/issues/10259
  294. refText = id
  295. ret.Data = refText
  296. return
  297. }
  298. if strings.Count(refText, "\\") == len(refText) {
  299. // 全部都是 \ 的话使用实体 https://github.com/siyuan-note/siyuan/issues/11473
  300. refText = strings.ReplaceAll(refText, "\\", "&#92;")
  301. ret.Data = refText
  302. return
  303. }
  304. ret.Data = refText
  305. }
  306. func getRefIDs(c *gin.Context) {
  307. ret := gulu.Ret.NewResult()
  308. defer c.JSON(http.StatusOK, ret)
  309. arg, ok := util.JsonArg(c, ret)
  310. if !ok {
  311. return
  312. }
  313. if nil == arg["id"] {
  314. arg["id"] = ""
  315. }
  316. id := arg["id"].(string)
  317. refIDs, refTexts, defIDs := model.GetBlockRefIDs(id)
  318. ret.Data = map[string][]string{
  319. "refIDs": refIDs,
  320. "refTexts": refTexts,
  321. "defIDs": defIDs,
  322. }
  323. }
  324. func getRefIDsByFileAnnotationID(c *gin.Context) {
  325. ret := gulu.Ret.NewResult()
  326. defer c.JSON(http.StatusOK, ret)
  327. arg, ok := util.JsonArg(c, ret)
  328. if !ok {
  329. return
  330. }
  331. id := arg["id"].(string)
  332. refIDs, refTexts := model.GetBlockRefIDsByFileAnnotationID(id)
  333. ret.Data = map[string][]string{
  334. "refIDs": refIDs,
  335. "refTexts": refTexts,
  336. }
  337. }
  338. func getBlockDefIDsByRefText(c *gin.Context) {
  339. ret := gulu.Ret.NewResult()
  340. defer c.JSON(http.StatusOK, ret)
  341. arg, ok := util.JsonArg(c, ret)
  342. if !ok {
  343. return
  344. }
  345. anchor := arg["anchor"].(string)
  346. excludeIDsArg := arg["excludeIDs"].([]interface{})
  347. var excludeIDs []string
  348. for _, excludeID := range excludeIDsArg {
  349. excludeIDs = append(excludeIDs, excludeID.(string))
  350. }
  351. excludeIDs = nil // 不限制虚拟引用搜索自己 https://ld246.com/article/1633243424177
  352. ids := model.GetBlockDefIDsByRefText(anchor, excludeIDs)
  353. ret.Data = ids
  354. }
  355. func getBlockBreadcrumb(c *gin.Context) {
  356. ret := gulu.Ret.NewResult()
  357. defer c.JSON(http.StatusOK, ret)
  358. arg, ok := util.JsonArg(c, ret)
  359. if !ok {
  360. return
  361. }
  362. id := arg["id"].(string)
  363. excludeTypesArg := arg["excludeTypes"]
  364. var excludeTypes []string
  365. if nil != excludeTypesArg {
  366. for _, excludeType := range excludeTypesArg.([]interface{}) {
  367. excludeTypes = append(excludeTypes, excludeType.(string))
  368. }
  369. }
  370. blockPath, err := model.BuildBlockBreadcrumb(id, excludeTypes)
  371. if nil != err {
  372. ret.Code = -1
  373. ret.Msg = err.Error()
  374. return
  375. }
  376. ret.Data = blockPath
  377. }
  378. func getBlockIndex(c *gin.Context) {
  379. ret := gulu.Ret.NewResult()
  380. defer c.JSON(http.StatusOK, ret)
  381. arg, ok := util.JsonArg(c, ret)
  382. if !ok {
  383. return
  384. }
  385. id := arg["id"].(string)
  386. index := model.GetBlockIndex(id)
  387. ret.Data = index
  388. }
  389. func getBlocksIndexes(c *gin.Context) {
  390. ret := gulu.Ret.NewResult()
  391. defer c.JSON(http.StatusOK, ret)
  392. arg, ok := util.JsonArg(c, ret)
  393. if !ok {
  394. return
  395. }
  396. idsArg := arg["ids"].([]interface{})
  397. var ids []string
  398. for _, id := range idsArg {
  399. ids = append(ids, id.(string))
  400. }
  401. index := model.GetBlocksIndexes(ids)
  402. ret.Data = index
  403. }
  404. func getBlockInfo(c *gin.Context) {
  405. ret := gulu.Ret.NewResult()
  406. defer c.JSON(http.StatusOK, ret)
  407. arg, ok := util.JsonArg(c, ret)
  408. if !ok {
  409. return
  410. }
  411. id := arg["id"].(string)
  412. // 仅在此处使用带重建索引的加载函数,其他地方不要使用
  413. tree, err := model.LoadTreeByBlockIDWithReindex(id)
  414. if errors.Is(err, model.ErrIndexing) {
  415. ret.Code = 3
  416. ret.Msg = model.Conf.Language(56)
  417. return
  418. }
  419. block, _ := model.GetBlock(id, tree)
  420. if nil == block {
  421. ret.Code = -1
  422. ret.Msg = fmt.Sprintf(model.Conf.Language(15), id)
  423. return
  424. }
  425. var rootChildID string
  426. b := block
  427. for i := 0; i < 128; i++ {
  428. parentID := b.ParentID
  429. if "" == parentID {
  430. rootChildID = b.ID
  431. break
  432. }
  433. if b, _ = model.GetBlock(parentID, tree); nil == b {
  434. logging.LogErrorf("not found parent")
  435. break
  436. }
  437. }
  438. root, err := model.GetBlock(block.RootID, tree)
  439. if errors.Is(err, model.ErrIndexing) {
  440. ret.Code = 3
  441. ret.Data = model.Conf.Language(56)
  442. return
  443. }
  444. rootTitle := root.IAL["title"]
  445. rootTitle = html.UnescapeString(rootTitle)
  446. ret.Data = map[string]string{
  447. "box": block.Box,
  448. "path": block.Path,
  449. "rootID": block.RootID,
  450. "rootTitle": rootTitle,
  451. "rootChildID": rootChildID,
  452. "rootIcon": root.IAL["icon"],
  453. }
  454. }
  455. func getBlockDOM(c *gin.Context) {
  456. ret := gulu.Ret.NewResult()
  457. defer c.JSON(http.StatusOK, ret)
  458. arg, ok := util.JsonArg(c, ret)
  459. if !ok {
  460. return
  461. }
  462. id := arg["id"].(string)
  463. dom := model.GetBlockDOM(id)
  464. ret.Data = map[string]string{
  465. "id": id,
  466. "dom": dom,
  467. }
  468. }
  469. func getBlockKramdown(c *gin.Context) {
  470. ret := gulu.Ret.NewResult()
  471. defer c.JSON(http.StatusOK, ret)
  472. arg, ok := util.JsonArg(c, ret)
  473. if !ok {
  474. return
  475. }
  476. id := arg["id"].(string)
  477. if util.InvalidIDPattern(id, ret) {
  478. return
  479. }
  480. kramdown := model.GetBlockKramdown(id)
  481. ret.Data = map[string]string{
  482. "id": id,
  483. "kramdown": kramdown,
  484. }
  485. }
  486. func getChildBlocks(c *gin.Context) {
  487. ret := gulu.Ret.NewResult()
  488. defer c.JSON(http.StatusOK, ret)
  489. arg, ok := util.JsonArg(c, ret)
  490. if !ok {
  491. return
  492. }
  493. id := arg["id"].(string)
  494. if util.InvalidIDPattern(id, ret) {
  495. return
  496. }
  497. ret.Data = model.GetChildBlocks(id)
  498. }
  499. func getTailChildBlocks(c *gin.Context) {
  500. ret := gulu.Ret.NewResult()
  501. defer c.JSON(http.StatusOK, ret)
  502. arg, ok := util.JsonArg(c, ret)
  503. if !ok {
  504. return
  505. }
  506. id := arg["id"].(string)
  507. if util.InvalidIDPattern(id, ret) {
  508. return
  509. }
  510. var n int
  511. nArg := arg["n"]
  512. if nil != nArg {
  513. n = int(nArg.(float64))
  514. }
  515. if 1 > n {
  516. n = 7
  517. }
  518. ret.Data = model.GetTailChildBlocks(id, n)
  519. }