// SiYuan - Refactor your thinking // Copyright (c) 2020-present, b3log.org // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . package api import ( "net/http" "strings" "time" "github.com/88250/gulu" "github.com/gin-gonic/gin" "github.com/siyuan-note/siyuan/kernel/model" "github.com/siyuan-note/siyuan/kernel/treenode" "github.com/siyuan-note/siyuan/kernel/util" ) func getNotebookInfo(c *gin.Context) { ret := gulu.Ret.NewResult() defer c.JSON(http.StatusOK, ret) arg, ok := util.JsonArg(c, ret) if !ok { return } boxID := arg["notebook"].(string) if util.InvalidIDPattern(boxID, ret) { return } box := model.Conf.Box(boxID) if nil == box { ret.Code = -1 ret.Msg = "notebook [" + boxID + "] not found" return } boxInfo := box.GetInfo() ret.Data = map[string]interface{}{ "boxInfo": boxInfo, } } func setNotebookIcon(c *gin.Context) { ret := gulu.Ret.NewResult() defer c.JSON(http.StatusOK, ret) arg, ok := util.JsonArg(c, ret) if !ok { return } boxID := arg["notebook"].(string) icon := arg["icon"].(string) model.SetBoxIcon(boxID, icon) } func changeSortNotebook(c *gin.Context) { ret := gulu.Ret.NewResult() defer c.JSON(http.StatusOK, ret) arg, ok := util.JsonArg(c, ret) if !ok { return } idsArg := arg["notebooks"].([]interface{}) var ids []string for _, p := range idsArg { ids = append(ids, p.(string)) } model.ChangeBoxSort(ids) } func renameNotebook(c *gin.Context) { ret := gulu.Ret.NewResult() defer c.JSON(http.StatusOK, ret) arg, ok := util.JsonArg(c, ret) if !ok { return } notebook := arg["notebook"].(string) if util.InvalidIDPattern(notebook, ret) { return } name := arg["name"].(string) err := model.RenameBox(notebook, name) if err != nil { ret.Code = -1 ret.Msg = err.Error() ret.Data = map[string]interface{}{"closeTimeout": 5000} return } evt := util.NewCmdResult("renamenotebook", 0, util.PushModeBroadcast) evt.Data = map[string]interface{}{ "box": notebook, "name": name, } util.PushEvent(evt) } func removeNotebook(c *gin.Context) { ret := gulu.Ret.NewResult() defer c.JSON(http.StatusOK, ret) arg, ok := util.JsonArg(c, ret) if !ok { return } notebook := arg["notebook"].(string) if util.InvalidIDPattern(notebook, ret) { return } if util.ReadOnly && !model.IsUserGuide(notebook) { ret.Code = -1 ret.Msg = model.Conf.Language(34) ret.Data = map[string]interface{}{"closeTimeout": 5000} return } err := model.RemoveBox(notebook) if err != nil { ret.Code = -1 ret.Msg = err.Error() return } evt := util.NewCmdResult("unmount", 0, util.PushModeBroadcast) evt.Data = map[string]interface{}{ "box": notebook, } evt.Callback = arg["callback"] util.PushEvent(evt) } func createNotebook(c *gin.Context) { ret := gulu.Ret.NewResult() defer c.JSON(http.StatusOK, ret) arg, ok := util.JsonArg(c, ret) if !ok { return } name := arg["name"].(string) id, err := model.CreateBox(name) if err != nil { ret.Code = -1 ret.Msg = err.Error() return } existed, err := model.Mount(id) if err != nil { ret.Code = -1 ret.Msg = err.Error() return } box := model.Conf.Box(id) if nil == box { ret.Code = -1 ret.Msg = "opened notebook [" + id + "] not found" return } ret.Data = map[string]interface{}{ "notebook": box, } evt := util.NewCmdResult("createnotebook", 0, util.PushModeBroadcast) evt.Data = map[string]interface{}{ "box": box, "existed": existed, } util.PushEvent(evt) } func openNotebook(c *gin.Context) { ret := gulu.Ret.NewResult() defer c.JSON(http.StatusOK, ret) arg, ok := util.JsonArg(c, ret) if !ok { return } notebook := arg["notebook"].(string) if util.InvalidIDPattern(notebook, ret) { return } isUserGuide := model.IsUserGuide(notebook) if util.ReadOnly && !isUserGuide { ret.Code = -1 ret.Msg = model.Conf.Language(34) ret.Data = map[string]interface{}{"closeTimeout": 5000} return } if isUserGuide && util.ContainerIOS == util.Container { // iOS 端不再支持打开用户指南,请参考桌面端用户指南 // 用户指南中包含了付费相关内容,无法通过商店上架审核 // Opening the user guide is no longer supported on iOS https://github.com/siyuan-note/siyuan/issues/11492 ret.Code = -1 ret.Msg = model.Conf.Language(215) ret.Data = map[string]interface{}{"closeTimeout": 7000} return } msgId := util.PushMsg(model.Conf.Language(45), 1000*60*15) defer util.PushClearMsg(msgId) existed, err := model.Mount(notebook) if err != nil { ret.Code = -1 ret.Msg = err.Error() return } box := model.Conf.Box(notebook) if nil == box { ret.Code = -1 ret.Msg = "opened notebook [" + notebook + "] not found" return } evt := util.NewCmdResult("mount", 0, util.PushModeBroadcast) evt.Data = map[string]interface{}{ "box": box, "existed": existed, } evt.Callback = arg["callback"] util.PushEvent(evt) if isUserGuide { appArg := arg["app"] app := "" if nil != appArg { app = appArg.(string) } go func() { var startID string i := 0 for ; i < 70; i++ { time.Sleep(100 * time.Millisecond) guideStartID := map[string]string{ "20210808180117-czj9bvb": "20200812220555-lj3enxa", "20211226090932-5lcq56f": "20211226115423-d5z1joq", "20210808180117-6v0mkxr": "20200923234011-ieuun1p", "20240530133126-axarxgx": "20240530101000-4qitucx", } startID = guideStartID[notebook] if treenode.ExistBlockTree(startID) { util.BroadcastByTypeAndApp("main", app, "openFileById", 0, "", map[string]interface{}{ "id": startID, }) break } } }() } } func closeNotebook(c *gin.Context) { ret := gulu.Ret.NewResult() defer c.JSON(http.StatusOK, ret) arg, ok := util.JsonArg(c, ret) if !ok { return } notebook := arg["notebook"].(string) if util.InvalidIDPattern(notebook, ret) { return } model.Unmount(notebook) } func getNotebookConf(c *gin.Context) { ret := gulu.Ret.NewResult() defer c.JSON(http.StatusOK, ret) arg, ok := util.JsonArg(c, ret) if !ok { return } notebook := arg["notebook"].(string) if util.InvalidIDPattern(notebook, ret) { return } box := model.Conf.GetBox(notebook) if nil == box { ret.Code = -1 ret.Msg = "notebook [" + notebook + "] not found" return } ret.Data = map[string]interface{}{ "box": box.ID, "name": box.Name, "conf": box.GetConf(), } } func setNotebookConf(c *gin.Context) { ret := gulu.Ret.NewResult() defer c.JSON(http.StatusOK, ret) arg, ok := util.JsonArg(c, ret) if !ok { return } notebook := arg["notebook"].(string) if util.InvalidIDPattern(notebook, ret) { return } box := model.Conf.GetBox(notebook) if nil == box { ret.Code = -1 ret.Msg = "notebook [" + notebook + "] not found" return } param, err := gulu.JSON.MarshalJSON(arg["conf"]) if err != nil { ret.Code = -1 ret.Msg = err.Error() return } boxConf := box.GetConf() if err = gulu.JSON.UnmarshalJSON(param, boxConf); err != nil { ret.Code = -1 ret.Msg = err.Error() return } boxConf.RefCreateSavePath = strings.TrimSpace(boxConf.RefCreateSavePath) if "" != boxConf.RefCreateSavePath { if !strings.HasSuffix(boxConf.RefCreateSavePath, "/") { boxConf.RefCreateSavePath += "/" } } boxConf.DailyNoteSavePath = strings.TrimSpace(boxConf.DailyNoteSavePath) if "" != boxConf.DailyNoteSavePath { if !strings.HasPrefix(boxConf.DailyNoteSavePath, "/") { boxConf.DailyNoteSavePath = "/" + boxConf.DailyNoteSavePath } } if "/" == boxConf.DailyNoteSavePath { ret.Code = -1 ret.Msg = model.Conf.Language(49) return } boxConf.DailyNoteTemplatePath = strings.TrimSpace(boxConf.DailyNoteTemplatePath) if "" != boxConf.DailyNoteTemplatePath { if !strings.HasSuffix(boxConf.DailyNoteTemplatePath, ".md") { boxConf.DailyNoteTemplatePath += ".md" } if !strings.HasPrefix(boxConf.DailyNoteTemplatePath, "/") { boxConf.DailyNoteTemplatePath = "/" + boxConf.DailyNoteTemplatePath } } boxConf.DocCreateSavePath = strings.TrimSpace(boxConf.DocCreateSavePath) box.SaveConf(boxConf) ret.Data = boxConf } func lsNotebooks(c *gin.Context) { ret := gulu.Ret.NewResult() defer c.JSON(http.StatusOK, ret) flashcard := false // 兼容旧版接口,不能直接使用 util.JsonArg() arg := map[string]interface{}{} if err := c.ShouldBindJSON(&arg); err == nil { if arg["flashcard"] != nil { flashcard = arg["flashcard"].(bool) } } var notebooks []*model.Box if flashcard { notebooks = model.GetFlashcardNotebooks() } else { var err error notebooks, err = model.ListNotebooks() if err != nil { return } } ret.Data = map[string]interface{}{ "notebooks": notebooks, } }