diff --git a/app/appearance/langs/en_US.json b/app/appearance/langs/en_US.json index 2762ca038..d064e2db1 100644 --- a/app/appearance/langs/en_US.json +++ b/app/appearance/langs/en_US.json @@ -1,4 +1,7 @@ { + "copyMirror": "Copy mirror", + "duplicateMirror": "Duplicate mirror", + "duplicateCompletely": "Duplicate completely", "isMsStoreVerTip": "The currently used version is the Microsoft Store version, please check for updates in the Microsoft Store", "andSubFile": "Are you sure you want to delete ${x} and its ${y} subdocs?", "confirmDeleteTip": "Are you sure to delete ${x}?", diff --git a/app/appearance/langs/es_ES.json b/app/appearance/langs/es_ES.json index 120891cd4..a3e86f2f9 100644 --- a/app/appearance/langs/es_ES.json +++ b/app/appearance/langs/es_ES.json @@ -1,4 +1,7 @@ { + "copyMirror": "Copiar espejo", + "duplicateMirror": "Espejo duplicado", + "duplicateCompletely": "Duplicar completamente", "isMsStoreVerTip": "La versión utilizada actualmente es la versión de Microsoft Store, verifique si hay actualizaciones en Microsoft Store", "andSubFile": "¿Está seguro de que desea eliminar ${x} y sus subdocumentos ${y}?", "confirmDeleteTip": "¿Está seguro de eliminar ${x}?", diff --git a/app/appearance/langs/fr_FR.json b/app/appearance/langs/fr_FR.json index 06b4f57f2..9b486d120 100644 --- a/app/appearance/langs/fr_FR.json +++ b/app/appearance/langs/fr_FR.json @@ -1,4 +1,7 @@ { + "copyMirror": "Copier le miroir", + "duplicateMirror": "Miroir en double", + "duplicateCompletely": "Dupliquer complètement", "isMsStoreVerTip": "La version actuellement utilisée est la version du Microsoft Store, veuillez vérifier les mises à jour dans le Microsoft Store", "andSubFile": "Êtes-vous sûr de vouloir supprimer ${x} et ses sous-documents ${y} ?", "confirmDeleteTip": "Êtes-vous sûr de supprimer ${x} ?", diff --git a/app/appearance/langs/ja_JP.json b/app/appearance/langs/ja_JP.json index 0118bdb10..8babdf017 100644 --- a/app/appearance/langs/ja_JP.json +++ b/app/appearance/langs/ja_JP.json @@ -1,4 +1,7 @@ { + "copyMirror": "ミラーをコピー", + "duplicateMirror": "ミラーの複製", + "duplicateCompletely": "完全に複製します", "isMsStoreVerTip": "現在使用されているバージョンは Microsoft Store バージョンです。Microsoft Store で更新プログラムを確認してください", "andSubFile": "${x} とそのサブドキュメント ${y} を削除してもよろしいですか?", "confirmDeleteTip": "${x} を削除してもよろしいですか?", diff --git a/app/appearance/langs/zh_CHT.json b/app/appearance/langs/zh_CHT.json index 0b7d8a034..9d7052a4c 100644 --- a/app/appearance/langs/zh_CHT.json +++ b/app/appearance/langs/zh_CHT.json @@ -1,4 +1,7 @@ { + "copyMirror": "複製鏡像", + "duplicateMirror": "複製為鏡像副本", + "duplicateCompletely": "複製為完整副本", "isMsStoreVerTip": "目前使用的版本為 Microsoft Store 版本,請在 Microsoft Store 中檢查更新", "andSubFile": "決定刪除 ${x} 及其 ${y} 個子文件嗎?", "confirmDeleteTip": "確定刪除${x} 嗎?", diff --git a/app/appearance/langs/zh_CN.json b/app/appearance/langs/zh_CN.json index a4931a6c3..31d91376e 100644 --- a/app/appearance/langs/zh_CN.json +++ b/app/appearance/langs/zh_CN.json @@ -1,4 +1,7 @@ { + "copyMirror": "复制镜像", + "duplicateMirror": "复制为镜像副本", + "duplicateCompletely": "复制为完整副本", "isMsStoreVerTip": "当前使用的版本为微软商店版,请在微软商店中检查更新", "andSubFile": "确定删除 ${x} 及其 ${y} 个子文档吗?", "confirmDeleteTip": "确定删除 ${x} 吗?", diff --git a/kernel/api/av.go b/kernel/api/av.go index 88953e6b3..06ded13ee 100644 --- a/kernel/api/av.go +++ b/kernel/api/av.go @@ -27,6 +27,29 @@ import ( "github.com/siyuan-note/siyuan/kernel/util" ) +func duplicateAttributeViewBlock(c *gin.Context) { + ret := gulu.Ret.NewResult() + defer c.JSON(http.StatusOK, ret) + + arg, ok := util.JsonArg(c, ret) + if !ok { + return + } + avID := arg["avID"].(string) + + newAvID, newBlockID, err := model.DuplicateDatabaseBlock(avID) + if nil != err { + ret.Code = -1 + ret.Msg = err.Error() + return + } + + ret.Data = map[string]interface{}{ + "avID": newAvID, + "blockID": newBlockID, + } +} + func getAttributeViewKeysByAvID(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 d4e622fa5..2a53c6934 100644 --- a/kernel/api/router.go +++ b/kernel/api/router.go @@ -425,6 +425,7 @@ func ServeAPI(ginServer *gin.Engine) { ginServer.Handle("POST", "/api/av/setDatabaseBlockView", model.CheckAuth, model.CheckReadonly, setDatabaseBlockView) ginServer.Handle("POST", "/api/av/getMirrorDatabaseBlocks", model.CheckAuth, model.CheckReadonly, getMirrorDatabaseBlocks) ginServer.Handle("POST", "/api/av/getAttributeViewKeysByAvID", model.CheckAuth, model.CheckReadonly, getAttributeViewKeysByAvID) + ginServer.Handle("POST", "/api/av/duplicateAttributeViewBlock", model.CheckAuth, model.CheckReadonly, duplicateAttributeViewBlock) ginServer.Handle("POST", "/api/ai/chatGPT", model.CheckAuth, chatGPT) ginServer.Handle("POST", "/api/ai/chatGPTWithAction", model.CheckAuth, chatGPTWithAction) diff --git a/kernel/model/attribute_view.go b/kernel/model/attribute_view.go index d7c048c8b..9f564200c 100644 --- a/kernel/model/attribute_view.go +++ b/kernel/model/attribute_view.go @@ -41,6 +41,46 @@ import ( "github.com/xrash/smetrics" ) +func DuplicateDatabaseBlock(avID string) (newAvID, newBlockID string, err error) { + storageAvDir := filepath.Join(util.DataDir, "storage", "av") + oldAvPath := filepath.Join(storageAvDir, avID+".json") + newAvID, newBlockID = ast.NewNodeID(), ast.NewNodeID() + + oldAv, err := av.ParseAttributeView(avID) + if nil != err { + return + } + + data, err := filelock.ReadFile(oldAvPath) + if nil != err { + logging.LogErrorf("read attribute view [%s] failed: %s", avID, err) + return + } + + data = bytes.ReplaceAll(data, []byte(avID), []byte(newAvID)) + av.UpsertBlockRel(newAvID, newBlockID) + + newAv := &av.AttributeView{} + if err = gulu.JSON.UnmarshalJSON(data, newAv); nil != err { + logging.LogErrorf("unmarshal attribute view [%s] failed: %s", newAvID, err) + return + } + + newAv.Name = oldAv.Name + " (Duplicated " + time.Now().Format("2006-01-02 15:04:05") + ")" + data, err = gulu.JSON.MarshalJSON(newAv) + if nil != err { + logging.LogErrorf("marshal attribute view [%s] failed: %s", newAvID, err) + return + } + + newAvPath := filepath.Join(storageAvDir, newAvID+".json") + if err = filelock.WriteFile(newAvPath, data); nil != err { + logging.LogErrorf("write attribute view [%s] failed: %s", newAvID, err) + return + } + return +} + func GetAttributeViewKeysByAvID(avID string) (ret []*av.Key) { ret = []*av.Key{}