block.go 11 KB

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