diff --git a/app/appearance/langs/ar_SA.json b/app/appearance/langs/ar_SA.json index fee748aa4..adfc8563e 100644 --- a/app/appearance/langs/ar_SA.json +++ b/app/appearance/langs/ar_SA.json @@ -1704,6 +1704,7 @@ "271": "اكتملت عملية تحسين فهرس البيانات، تم تحرير [%s] من مساحة القرص", "272": "حقل غير مسمى", "273": "لا تقم بإنشاء مساحة العمل في مسار جذر القسم، يرجى إنشاء مجلد جديد كمساحة عمل", - "274": "يحتوي هذا المجلد على ملفات أخرى، يرجى إنشاء مجلد جديد كمساحة عمل" + "274": "يحتوي هذا المجلد على ملفات أخرى، يرجى إنشاء مجلد جديد كمساحة عمل", + "275": "يتعذر فتح المستند الذي تم إنشاؤه بواسطة إصدار أحدث. يرجى الترقية إلى أحدث إصدار ثم المحاولة مرة أخرى" } } diff --git a/app/appearance/langs/de_DE.json b/app/appearance/langs/de_DE.json index bd7530b6e..fdb88b1db 100644 --- a/app/appearance/langs/de_DE.json +++ b/app/appearance/langs/de_DE.json @@ -1704,6 +1704,7 @@ "271": "Datenindex-Optimierung abgeschlossen, [%s] Speicherplatz freigegeben", "272": "Unbenanntes Feld", "273": "Erstellen Sie den Arbeitsbereich nicht im Stammverzeichnis der Partition, erstellen Sie bitte einen neuen Ordner als Arbeitsbereich", - "274": "Dieser Ordner enthält andere Dateien, erstellen Sie bitte einen neuen Ordner als Arbeitsbereich" + "274": "Dieser Ordner enthält andere Dateien, erstellen Sie bitte einen neuen Ordner als Arbeitsbereich", + "275": "Dokumente, die mit einer neueren Version erstellt wurden, können nicht geöffnet werden. Bitte aktualisieren Sie auf die neueste Version und versuchen Sie es erneut" } } diff --git a/app/appearance/langs/en_US.json b/app/appearance/langs/en_US.json index 8a000c2c0..40999824a 100644 --- a/app/appearance/langs/en_US.json +++ b/app/appearance/langs/en_US.json @@ -1704,6 +1704,7 @@ "271": "Data index optimization completed, [%s] disk space freed", "272": "Unnamed field", "273": "Do not create the workspace in the partition root path, please create a new folder as the workspace", - "274": "This folder contains other files, please create a new folder as the workspace" + "274": "This folder contains other files, please create a new folder as the workspace", + "275": "Cannot open documents created by a newer version. Please upgrade to the latest version and try again" } } diff --git a/app/appearance/langs/es_ES.json b/app/appearance/langs/es_ES.json index 5d7461d87..a9406fe8d 100644 --- a/app/appearance/langs/es_ES.json +++ b/app/appearance/langs/es_ES.json @@ -1704,6 +1704,7 @@ "271": "Optimización del índice de datos completada, se liberaron [%s] de espacio en disco", "272": "Campo sin nombre", "273": "No cree el espacio de trabajo en la ruta raíz de la partición, cree una nueva carpeta como espacio de trabajo", - "274": "Esta carpeta contiene otros archivos, cree una nueva carpeta como espacio de trabajo" + "274": "Esta carpeta contiene otros archivos, cree una nueva carpeta como espacio de trabajo", + "275": "No se puede abrir el documento creado con una versión más reciente. Actualiza a la última versión e inténtalo de nuevo" } } diff --git a/app/appearance/langs/fr_FR.json b/app/appearance/langs/fr_FR.json index 2baff5af1..8825d26dd 100644 --- a/app/appearance/langs/fr_FR.json +++ b/app/appearance/langs/fr_FR.json @@ -1704,6 +1704,7 @@ "271": "Optimisation de l'index des données terminée, [%s] d'espace disque libéré", "272": "Champ sans nom", "273": "Ne créez pas l’espace de travail à la racine de la partition, créez un nouveau dossier comme espace de travail", - "274": "Ce dossier contient d’autres fichiers, créez un nouveau dossier comme espace de travail" + "274": "Ce dossier contient d’autres fichiers, créez un nouveau dossier comme espace de travail", + "275": "Impossible d'ouvrir le document créé par une version plus récente. Veuillez mettre à jour vers la dernière version et réessayer" } } diff --git a/app/appearance/langs/he_IL.json b/app/appearance/langs/he_IL.json index 138b37f4b..db003d83f 100644 --- a/app/appearance/langs/he_IL.json +++ b/app/appearance/langs/he_IL.json @@ -1704,6 +1704,7 @@ "271": "אופטימיזציית אינדקס הנתונים הושלמה, שוחררו [%s] שטח דיסק", "272": "שדה ללא שם", "273": "אל תיצור סביבת עבודה בנתיב השורש של המחיצה, צור תיקיה חדשה כסביבת עבודה", - "274": "התיקיה הזו מכילה קבצים נוספים, צור תיקיה חדשה כסביבת עבודה" + "274": "התיקיה הזו מכילה קבצים נוספים, צור תיקיה חדשה כסביבת עבודה", + "275": "לא ניתן לפתוח את המסמך שנוצר בגרסה חדשה יותר. יש לעדכן לגרסה העדכנית ביותר ולנסות שוב" } } diff --git a/app/appearance/langs/it_IT.json b/app/appearance/langs/it_IT.json index b815e02fd..509c78d54 100644 --- a/app/appearance/langs/it_IT.json +++ b/app/appearance/langs/it_IT.json @@ -1704,6 +1704,7 @@ "271": "Ottimizzazione dell'indice dei dati completata, liberati [%s] di spazio su disco", "272": "Campo senza nome", "273": "Non creare lo spazio di lavoro nella directory radice della partizione, crea una nuova cartella come spazio di lavoro", - "274": "Questa cartella contiene altri file, crea una nuova cartella come spazio di lavoro" + "274": "Questa cartella contiene altri file, crea una nuova cartella come spazio di lavoro", + "275": "Impossibile aprire il documento creato con una versione più recente. Aggiorna all'ultima versione e riprova" } } diff --git a/app/appearance/langs/ja_JP.json b/app/appearance/langs/ja_JP.json index 9dcf762b7..de9b61650 100644 --- a/app/appearance/langs/ja_JP.json +++ b/app/appearance/langs/ja_JP.json @@ -1704,6 +1704,7 @@ "271": "データインデックスの最適化が完了しました。合計 [%s] のディスク容量が解放されました", "272": "未命名フィールド", "273": "パーティションのルートパスにワークスペースを作成しないでください。新しいフォルダーをワークスペースとして作成してください", - "274": "このフォルダーには他のファイルが含まれています。新しいフォルダーをワークスペースとして作成してください" + "274": "このフォルダーには他のファイルが含まれています。新しいフォルダーをワークスペースとして作成してください", + "275": "新しいバージョンで作成された文書を開くことができません。最新バージョンにアップデートしてから再試行してください" } } diff --git a/app/appearance/langs/pl_PL.json b/app/appearance/langs/pl_PL.json index 2b858bdc4..1c00ca39c 100644 --- a/app/appearance/langs/pl_PL.json +++ b/app/appearance/langs/pl_PL.json @@ -1704,6 +1704,7 @@ "271": "Optymalizacja indeksu danych zakończona, zwolniono [%s] miejsca na dysku", "272": "Nienazwane pole", "273": "Nie twórz przestrzeni roboczej w katalogu głównym partycji, utwórz nowy folder jako przestrzeń roboczą", - "274": "Ten folder zawiera inne pliki, utwórz nowy folder jako przestrzeń roboczą" + "274": "Ten folder zawiera inne pliki, utwórz nowy folder jako przestrzeń roboczą", + "275": "Nie można otworzyć dokumentu utworzonego w nowszej wersji. Zaktualizuj program do najnowszej wersji i spróbuj ponownie" } } diff --git a/app/appearance/langs/pt_BR.json b/app/appearance/langs/pt_BR.json index fc1494d80..4caca191b 100644 --- a/app/appearance/langs/pt_BR.json +++ b/app/appearance/langs/pt_BR.json @@ -1704,6 +1704,7 @@ "271": "Otimização do índice de dados concluída, [%s] de espaço liberado", "272": "Campo sem nome", "273": "Não crie o espaço de trabalho na raiz da partição, crie uma nova pasta para o espaço de trabalho", - "274": "Esta pasta contém outros arquivos, crie uma nova pasta para o espaço de trabalho" + "274": "Esta pasta contém outros arquivos, crie uma nova pasta para o espaço de trabalho", + "275": "Não é possível abrir o documento criado por uma versão mais recente. Atualize para a versão mais recente e tente novamente" } } diff --git a/app/appearance/langs/ru_RU.json b/app/appearance/langs/ru_RU.json index ad214f001..0dcfeb562 100644 --- a/app/appearance/langs/ru_RU.json +++ b/app/appearance/langs/ru_RU.json @@ -1704,6 +1704,7 @@ "271": "Оптимизация индекса данных завершена, освобождено [%s] дискового пространства", "272": "Неименованное поле", "273": "Не создавайте рабочее пространство в корневом каталоге раздела, создайте отдельную папку для рабочего пространства", - "274": "Эта папка содержит другие файлы, создайте отдельную папку для рабочего пространства" + "274": "Эта папка содержит другие файлы, создайте отдельную папку для рабочего пространства", + "275": "Невозможно открыть документ, созданный в более новой версии. Пожалуйста, обновите приложение до последней версии и попробуйте снова" } } diff --git a/app/appearance/langs/zh_CHT.json b/app/appearance/langs/zh_CHT.json index d956444a5..76e37440a 100644 --- a/app/appearance/langs/zh_CHT.json +++ b/app/appearance/langs/zh_CHT.json @@ -1704,6 +1704,7 @@ "271": "資料索引優化完畢,共釋放 [%s] 磁碟空間", "272": "未命名欄位", "273": "請勿在分區根路徑上建立工作空間,請新建一個資料夾作為工作空間", - "274": "該資料夾包含其他檔案,請新建一個資料夾作為工作空間" + "274": "該資料夾包含其他檔案,請新建一個資料夾作為工作空間", + "275": "無法打開由新版本建立的檔案,請升級到最新版本後再試" } } diff --git a/app/appearance/langs/zh_CN.json b/app/appearance/langs/zh_CN.json index 35ab6d804..02fb9a8c9 100644 --- a/app/appearance/langs/zh_CN.json +++ b/app/appearance/langs/zh_CN.json @@ -1704,6 +1704,7 @@ "271": "数据索引优化完毕,共释放 [%s] 磁盘空间", "272": "未命名字段", "273": "请勿在分区根路径上创建工作空间,请新建一个文件夹作为工作空间", - "274": "该文件夹包含了其他文件,请新建一个文件夹作为工作空间" + "274": "该文件夹包含了其他文件,请新建一个文件夹作为工作空间", + "275": "无法打开新版本创建的文档,请升级到最新版本后再试" } } diff --git a/kernel/api/block.go b/kernel/api/block.go index 662e60e99..187cc2d36 100644 --- a/kernel/api/block.go +++ b/kernel/api/block.go @@ -28,6 +28,7 @@ import ( "github.com/siyuan-note/logging" "github.com/siyuan-note/siyuan/kernel/filesys" "github.com/siyuan-note/siyuan/kernel/model" + "github.com/siyuan-note/siyuan/kernel/treenode" "github.com/siyuan-note/siyuan/kernel/util" ) @@ -660,6 +661,10 @@ func getBlockInfo(c *gin.Context) { ret.Code = 3 ret.Msg = model.Conf.Language(56) return + } else if errors.Is(err, treenode.ErrSpecTooNew) { + ret.Code = -1 + ret.Msg = model.Conf.Language(275) + return } block, _ := model.GetBlock(id, tree) diff --git a/kernel/filesys/tree.go b/kernel/filesys/tree.go index 8ee1204eb..7c3f5ca38 100644 --- a/kernel/filesys/tree.go +++ b/kernel/filesys/tree.go @@ -125,10 +125,9 @@ func LoadTree(boxID, p string, luteEngine *lute.Lute) (ret *parse.Tree, err erro } func LoadTreeByData(data []byte, boxID, p string, luteEngine *lute.Lute) (ret *parse.Tree, err error) { - ret = parseJSON2Tree(boxID, p, data, luteEngine) - if nil == ret { - logging.LogErrorf("parse tree [%s] failed", p) - err = errors.New("parse tree failed") + ret, err = parseJSON2Tree(boxID, p, data, luteEngine) + if nil != err { + logging.LogErrorf("parse tree [%s] failed: %s", p, err) return } ret.Path = p @@ -245,12 +244,9 @@ func prepareWriteTree(tree *parse.Tree) (data []byte, filePath string, err error treenode.UpsertBlockTree(tree) } + treenode.UpgradeSpec(tree) + filePath = filepath.Join(util.DataDir, tree.Box, tree.Path) - if oldSpec := tree.Root.Spec; "" == oldSpec { - parse.NestedInlines2FlattedSpans(tree, false) - tree.Root.Spec = "1" - logging.LogInfof("migrated tree [%s] from spec [%s] to [%s]", filePath, oldSpec, tree.Root.Spec) - } tree.Root.SetIALAttr("type", "doc") renderer := render.NewJSONRenderer(tree, luteEngine.RenderOptions) data = renderer.Render() @@ -277,8 +273,7 @@ func afterWriteTree(tree *parse.Tree) { cache.PutDocIAL(tree.Path, docIAL) } -func parseJSON2Tree(boxID, p string, jsonData []byte, luteEngine *lute.Lute) (ret *parse.Tree) { - var err error +func parseJSON2Tree(boxID, p string, jsonData []byte, luteEngine *lute.Lute) (ret *parse.Tree, err error) { var needFix bool ret, needFix, err = dataparser.ParseJSON(jsonData, luteEngine.ParseOptions) if err != nil { @@ -289,12 +284,12 @@ func parseJSON2Tree(boxID, p string, jsonData []byte, luteEngine *lute.Lute) (re ret.Box = boxID ret.Path = p - filePath := filepath.Join(util.DataDir, ret.Box, ret.Path) - if oldSpec := ret.Root.Spec; "" == oldSpec { - parse.NestedInlines2FlattedSpans(ret, false) - ret.Root.Spec = "1" + if err = treenode.CheckSpec(ret); errors.Is(err, treenode.ErrSpecTooNew) { + return + } + + if treenode.UpgradeSpec(ret) { needFix = true - logging.LogInfof("migrated tree [%s] from spec [%s] to [%s]", filePath, oldSpec, ret.Root.Spec) } if pathID := util.GetTreeID(p); pathID != ret.Root.ID { @@ -318,6 +313,7 @@ func parseJSON2Tree(boxID, p string, jsonData []byte, luteEngine *lute.Lute) (re data = buf.Bytes() } + filePath := filepath.Join(util.DataDir, ret.Box, ret.Path) if err = os.MkdirAll(filepath.Dir(filePath), 0755); err != nil { return } diff --git a/kernel/model/file.go b/kernel/model/file.go index 30954e2f8..66d682646 100644 --- a/kernel/model/file.go +++ b/kernel/model/file.go @@ -1748,7 +1748,7 @@ func createDoc(boxID, p, title, dom string) (tree *parse.Tree, err error) { tree.HPath = hPath tree.ID = id tree.Root.ID = id - tree.Root.Spec = "1" + tree.Root.Spec = treenode.CurrentSpec updated := util.TimeFromID(id) tree.Root.KramdownIAL = [][]string{{"id", id}, {"title", html.EscapeAttrVal(title)}, {"updated", updated}} if nil == tree.Root.FirstChild { diff --git a/kernel/model/format.go b/kernel/model/format.go index a28bd9e8d..06ca6eeee 100644 --- a/kernel/model/format.go +++ b/kernel/model/format.go @@ -23,6 +23,7 @@ import ( "github.com/88250/lute/editor" "github.com/88250/lute/render" "github.com/siyuan-note/logging" + "github.com/siyuan-note/siyuan/kernel/treenode" "github.com/siyuan-note/siyuan/kernel/util" ) @@ -63,13 +64,13 @@ func AutoSpace(rootID string) (err error) { formatRenderer := render.NewFormatRenderer(tree, luteEngine.RenderOptions) md := formatRenderer.Render() newTree := parseKTree(md) - newTree.Root.Spec = "1" + newTree.Root.Spec = treenode.CurrentSpec // 第二次格式化启用自动空格 luteEngine.SetAutoSpace(true) formatRenderer = render.NewFormatRenderer(newTree, luteEngine.RenderOptions) md = formatRenderer.Render() newTree = parseKTree(md) - newTree.Root.Spec = "1" + newTree.Root.Spec = treenode.CurrentSpec newTree.Root.ID = tree.ID newTree.Root.KramdownIAL = rootIAL newTree.ID = tree.ID diff --git a/kernel/model/heading.go b/kernel/model/heading.go index a12eb5284..f2bc8d987 100644 --- a/kernel/model/heading.go +++ b/kernel/model/heading.go @@ -427,7 +427,7 @@ func Heading2Doc(srcHeadingID, targetBoxID, targetPath, previousPath string) (sr newTree.Box, newTree.Path = targetBoxID, newTargetPath newTree.Root.SetIALAttr("updated", util.CurrentTimeSecondsStr()) - newTree.Root.Spec = "1" + newTree.Root.Spec = treenode.CurrentSpec if "" != previousPath { box.addSort(previousPath, newTree.ID) } else { diff --git a/kernel/model/import.go b/kernel/model/import.go index 99d5b8724..f12763b2f 100644 --- a/kernel/model/import.go +++ b/kernel/model/import.go @@ -418,10 +418,7 @@ func ImportSY(zipPath, boxID, toPath string) (err error) { for _, tree := range trees { util.PushEndlessProgress(Conf.language(73) + " " + fmt.Sprintf(Conf.language(70), tree.Root.IALAttr("title"))) syPath := filepath.Join(unzipRootPath, tree.Path) - if "" == tree.Root.Spec { - parse.NestedInlines2FlattedSpans(tree, false) - tree.Root.Spec = "1" - } + treenode.UpgradeSpec(tree) renderer := render.NewJSONRenderer(tree, luteEngine.RenderOptions) data := renderer.Render() @@ -943,7 +940,7 @@ func ImportFromLocalPath(boxID, localPath string, toPath string) (err error) { tree.Path = targetPath targetPaths[curRelPath] = targetPath tree.HPath = hPath - tree.Root.Spec = "1" + tree.Root.Spec = treenode.CurrentSpec docDirLocalPath := filepath.Dir(filepath.Join(boxLocalPath, targetPath)) assetDirPath := getAssetsDir(boxLocalPath, docDirLocalPath) @@ -1075,7 +1072,7 @@ func ImportFromLocalPath(boxID, localPath string, toPath string) (err error) { tree.Box = boxID tree.Path = targetPath tree.HPath = path.Join(baseHPath, title) - tree.Root.Spec = "1" + tree.Root.Spec = treenode.CurrentSpec docDirLocalPath := filepath.Dir(filepath.Join(boxLocalPath, targetPath)) assetDirPath := getAssetsDir(boxLocalPath, docDirLocalPath) diff --git a/kernel/model/listitem.go b/kernel/model/listitem.go index 202b00001..b8864f088 100644 --- a/kernel/model/listitem.go +++ b/kernel/model/listitem.go @@ -128,7 +128,7 @@ func ListItem2Doc(srcListItemID, targetBoxID, targetPath, previousPath string) ( newTree.Box, newTree.Path = targetBoxID, newTargetPath newTree.Root.SetIALAttr("updated", util.CurrentTimeSecondsStr()) - newTree.Root.Spec = "1" + newTree.Root.Spec = treenode.CurrentSpec if "" != previousPath { box.addSort(previousPath, newTree.ID) } else { diff --git a/kernel/treenode/tree.go b/kernel/treenode/tree.go index 871a3d31e..779d4edff 100644 --- a/kernel/treenode/tree.go +++ b/kernel/treenode/tree.go @@ -22,6 +22,7 @@ import ( "io/fs" "path/filepath" "sort" + "strconv" "strings" "github.com/88250/gulu" @@ -29,6 +30,7 @@ import ( "github.com/88250/lute/ast" "github.com/88250/lute/parse" "github.com/siyuan-note/filelock" + "github.com/siyuan-note/logging" "github.com/siyuan-note/siyuan/kernel/util" ) @@ -71,7 +73,7 @@ func NewTree(boxID, p, hp, title string) *parse.Tree { root.SetIALAttr("id", id) root.SetIALAttr("updated", util.TimeFromID(id)) ret := &parse.Tree{Root: root, ID: id, Box: boxID, Path: p, HPath: hp} - ret.Root.Spec = "1" + ret.Root.Spec = CurrentSpec newPara := &ast.Node{Type: ast.NodeParagraph, ID: ast.NewNodeID(), Box: boxID, Path: p} newPara.SetIALAttr("id", newPara.ID) newPara.SetIALAttr("updated", util.TimeFromID(newPara.ID)) @@ -129,3 +131,62 @@ func NewSpanAnchor(id string) (ret *ast.Node) { func ContainOnlyDefaultIAL(tree *parse.Tree) bool { return 5 > len(tree.Root.KramdownIAL) } + +var CurrentSpec = "2" + +var ErrSpecTooNew = fmt.Errorf("the document spec is too new") + +func CheckSpec(tree *parse.Tree) (err error) { + if CurrentSpec == tree.Root.Spec || "" == tree.Root.Spec { + return + } + + spec, err := strconv.Atoi(tree.Root.Spec) + if nil != err { + logging.LogErrorf("parse spec [%s] failed: %s", tree.Root.Spec, err) + return + } + + currentSpec, _ := strconv.Atoi(CurrentSpec) + if spec > currentSpec { + logging.LogErrorf("tree spec [%s] is newer than current spec [%s]", tree.Root.Spec, CurrentSpec) + return ErrSpecTooNew + } + return +} + +func UpgradeSpec(tree *parse.Tree) (upgraded bool) { + if CurrentSpec == tree.Root.Spec { + return + } + + upgradeSpec1(tree) + upgradeSpec2(tree) + return true +} + +func upgradeSpec2(tree *parse.Tree) { + oldSpec, err := strconv.Atoi(tree.Root.Spec) + if nil != err { + logging.LogErrorf("parse spec [%s] failed: %s", tree.Root.Spec, err) + return + } + + if 2 <= oldSpec { + return + } + + // 增加了 Callout + + tree.Root.Spec = "2" +} + +func upgradeSpec1(tree *parse.Tree) { + if "" != tree.Root.Spec { + return + } + + parse.NestedInlines2FlattedSpans(tree, false) + tree.Root.Spec = "1" + return +}