diff --git a/kernel/api/av.go b/kernel/api/av.go index bd6d723db..9ef04273f 100644 --- a/kernel/api/av.go +++ b/kernel/api/av.go @@ -26,6 +26,46 @@ import ( "github.com/siyuan-note/siyuan/kernel/util" ) +func renderHistoryAttributeView(c *gin.Context) { + ret := gulu.Ret.NewResult() + defer c.JSON(http.StatusOK, ret) + + arg, ok := util.JsonArg(c, ret) + if !ok { + return + } + + id := arg["id"].(string) + created := arg["created"].(string) + view, attrView, err := model.RenderHistoryAttributeView(id, created) + if nil != err { + ret.Code = -1 + ret.Msg = err.Error() + return + } + + var views []map[string]interface{} + for _, v := range attrView.Views { + view := map[string]interface{}{ + "id": v.ID, + "name": v.Name, + "type": v.LayoutType, + } + + views = append(views, view) + } + + ret.Data = map[string]interface{}{ + "name": attrView.Name, + "id": attrView.ID, + "viewType": view.GetType(), + "viewID": view.GetID(), + "views": views, + "view": view, + "isMirror": av.IsMirror(attrView.ID), + } +} + func renderAttributeView(c *gin.Context) { ret := gulu.Ret.NewResult() defer c.JSON(http.StatusOK, ret) diff --git a/kernel/api/router.go b/kernel/api/router.go index ffb9bc341..b686f80f2 100644 --- a/kernel/api/router.go +++ b/kernel/api/router.go @@ -375,6 +375,7 @@ func ServeAPI(ginServer *gin.Engine) { ginServer.Handle("POST", "/api/snippet/removeSnippet", model.CheckAuth, model.CheckReadonly, removeSnippet) ginServer.Handle("POST", "/api/av/renderAttributeView", model.CheckAuth, renderAttributeView) + ginServer.Handle("POST", "/api/av/renderHistoryAttributeView", model.CheckAuth, renderHistoryAttributeView) ginServer.Handle("POST", "/api/av/getAttributeViewKeys", model.CheckAuth, getAttributeViewKeys) ginServer.Handle("POST", "/api/av/setAttributeViewBlockAttr", model.CheckAuth, model.CheckReadonly, setAttributeViewBlockAttr) diff --git a/kernel/model/attribute_view.go b/kernel/model/attribute_view.go index bacd17bdc..658c39572 100644 --- a/kernel/model/attribute_view.go +++ b/kernel/model/attribute_view.go @@ -18,7 +18,10 @@ package model import ( "bytes" + "os" + "path/filepath" "sort" + "strconv" "strings" "text/template" "time" @@ -181,6 +184,46 @@ func GetBlockAttributeViewKeys(blockID string) (ret []*BlockAttributeViewKeys) { return } +func RenderHistoryAttributeView(avID, created string) (viewable av.Viewable, attrView *av.AttributeView, err error) { + createdUnix, parseErr := strconv.ParseInt(created, 10, 64) + if nil != parseErr { + logging.LogErrorf("parse created [%s] failed: %s", created, parseErr) + return + } + + dirPrefix := time.Unix(createdUnix, 0).Format("2006-01-02-150405") + globPath := filepath.Join(util.HistoryDir, dirPrefix+"*") + matches, err := filepath.Glob(globPath) + if nil != err { + logging.LogErrorf("glob [%s] failed: %s", globPath, err) + return + } + if 1 > len(matches) { + return + } + + historyDir := matches[0] + avJSONPath := filepath.Join(historyDir, "storage", "av", avID+".json") + if !gulu.File.IsExist(avJSONPath) { + return + } + + data, readErr := os.ReadFile(avJSONPath) + if nil != readErr { + logging.LogErrorf("read attribute view [%s] failed: %s", avID, readErr) + return + } + + attrView = &av.AttributeView{} + if err = gulu.JSON.UnmarshalJSON(data, attrView); nil != err { + logging.LogErrorf("unmarshal attribute view [%s] failed: %s", avID, err) + return + } + + viewable, err = renderAttributeView(attrView) + return +} + func RenderAttributeView(avID string) (viewable av.Viewable, attrView *av.AttributeView, err error) { waitForSyncingStorages() @@ -198,12 +241,17 @@ func RenderAttributeView(avID string) (viewable av.Viewable, attrView *av.Attrib return } + viewable, err = renderAttributeView(attrView) + return +} + +func renderAttributeView(attrView *av.AttributeView) (viewable av.Viewable, err error) { if 1 > len(attrView.Views) { view := av.NewView() attrView.Views = append(attrView.Views, view) attrView.ViewID = view.ID if err = av.SaveAttributeView(attrView); nil != err { - logging.LogErrorf("save attribute view [%s] failed: %s", avID, err) + logging.LogErrorf("save attribute view [%s] failed: %s", attrView.ID, err) return } } diff --git a/kernel/model/file.go b/kernel/model/file.go index dae860526..84b932749 100644 --- a/kernel/model/file.go +++ b/kernel/model/file.go @@ -1409,6 +1409,16 @@ func removeDoc(box *Box, p string, luteEngine *lute.Lute) { return } + // 关联的属性视图也要复制到历史中 https://github.com/siyuan-note/siyuan/issues/9567 + avNodes := tree.Root.ChildrenByType(ast.NodeAttributeView) + for _, avNode := range avNodes { + srcAvPath := filepath.Join(util.DataDir, "storage", "av", avNode.AttributeViewID+".json") + destAvPath := filepath.Join(historyDir, "storage", "av", avNode.AttributeViewID+".json") + if copyErr := filelock.Copy(srcAvPath, destAvPath); nil != copyErr { + logging.LogErrorf("copy av [%s] failed: %s", srcAvPath, copyErr) + } + } + copyDocAssetsToDataAssets(box.ID, p) removeIDs := treenode.RootChildIDs(tree.ID) diff --git a/kernel/model/history.go b/kernel/model/history.go index 3ea03afb5..281a0b878 100644 --- a/kernel/model/history.go +++ b/kernel/model/history.go @@ -464,6 +464,7 @@ func (box *Box) generateDocHistory0() { return } + luteEngine := util.NewLute() for _, file := range files { historyPath := filepath.Join(historyDir, box.ID, strings.TrimPrefix(file, filepath.Join(util.DataDir, box.ID))) if err = os.MkdirAll(filepath.Dir(historyPath), 0755); nil != err { @@ -481,6 +482,23 @@ func (box *Box) generateDocHistory0() { logging.LogErrorf("generate history failed: %s", err) return } + + if strings.HasSuffix(file, ".sy") { + tree, loadErr := loadTree(file, luteEngine) + if nil != loadErr { + logging.LogErrorf("load tree [%s] failed: %s", file, loadErr) + } else { + // 关联的属性视图也要复制到历史中 https://github.com/siyuan-note/siyuan/issues/9567 + avNodes := tree.Root.ChildrenByType(ast.NodeAttributeView) + for _, avNode := range avNodes { + srcAvPath := filepath.Join(util.DataDir, "storage", "av", avNode.AttributeViewID+".json") + destAvPath := filepath.Join(historyDir, "storage", "av", avNode.AttributeViewID+".json") + if copyErr := filelock.Copy(srcAvPath, destAvPath); nil != copyErr { + logging.LogErrorf("copy av [%s] failed: %s", srcAvPath, copyErr) + } + } + } + } } indexHistoryDir(filepath.Base(historyDir), util.NewLute()) @@ -544,7 +562,7 @@ func (box *Box) recentModifiedDocs() (ret []string) { } if info.ModTime().After(latestHistoryTime) { - ret = append(ret, filepath.Join(path)) + ret = append(ret, path) } return nil })