diff --git a/app/guide/20210808180117-6v0mkxr/20200923234011-ieuun1p/20210808180303-xaduj2o/20200924100744-br924ar.sy b/app/guide/20210808180117-6v0mkxr/20200923234011-ieuun1p/20210808180303-xaduj2o/20200924100744-br924ar.sy index b8fb93db4..f3e7da86f 100644 --- a/app/guide/20210808180117-6v0mkxr/20200923234011-ieuun1p/20210808180303-xaduj2o/20200924100744-br924ar.sy +++ b/app/guide/20210808180117-6v0mkxr/20200923234011-ieuun1p/20210808180303-xaduj2o/20200924100744-br924ar.sy @@ -5,7 +5,7 @@ "id": "20200924100744-br924ar", "title": "Assets", "type": "doc", - "updated": "20220715092808" + "updated": "20220715105117" }, "Children": [ { @@ -625,7 +625,7 @@ "ListData": {}, "Properties": { "id": "20220503122349-o26mvnq", - "updated": "20220715092808" + "updated": "20220715105117" }, "Children": [ { @@ -650,23 +650,7 @@ "Children": [ { "Type": "NodeText", - "Data": "When deleting the notebook, in order to ensure that cross-notebook asset references work properly, the " - }, - { - "Type": "NodeText", - "Data": "assets" - }, - { - "Type": "NodeText", - "Data": " under the notebook will be copied to the global " - }, - { - "Type": "NodeText", - "Data": "assets" - }, - { - "Type": "NodeText", - "Data": " in batches" + "Data": "When deleting the notebook, in order to ensure that cross-notebook asset references work properly, the assets under the notebook will be copied to the global assets in batches" } ] } @@ -694,19 +678,7 @@ "Children": [ { "Type": "NodeText", - "Data": "Does not support viewing notebook-level " - }, - { - "Type": "NodeText", - "Data": "assets" - }, - { - "Type": "NodeText", - "Data": " history in " - }, - { - "Type": "NodeText", - "Data": "data history" + "Data": "Does not support viewing notebook-level assets history in data history" } ] } @@ -721,7 +693,7 @@ }, "Properties": { "id": "20220715092756-f5azf15", - "updated": "20220715092808" + "updated": "20220715105117" }, "Children": [ { @@ -729,7 +701,7 @@ "Type": "NodeParagraph", "Properties": { "id": "20220715092756-4bzibji", - "updated": "20220715092808" + "updated": "20220715105117" }, "Children": [ { @@ -755,6 +727,34 @@ ] } ] + }, + { + "ID": "20220715105056-8d652vx", + "Type": "NodeListItem", + "ListData": { + "BulletChar": 42, + "Marker": "Kg==" + }, + "Properties": { + "id": "20220715105056-8d652vx", + "updated": "20220715105100" + }, + "Children": [ + { + "ID": "20220715105056-a6p3fg7", + "Type": "NodeParagraph", + "Properties": { + "id": "20220715105056-a6p3fg7", + "updated": "20220715105100" + }, + "Children": [ + { + "Type": "NodeText", + "Data": "Dose not support rename" + } + ] + } + ] } ] } diff --git a/app/guide/20210808180117-czj9bvb/20200812220555-lj3enxa/20210808180321-hbvl5c2/20200915214115-42b8zma.sy b/app/guide/20210808180117-czj9bvb/20200812220555-lj3enxa/20210808180321-hbvl5c2/20200915214115-42b8zma.sy index 5d5c9916a..2b98cfa98 100644 --- a/app/guide/20210808180117-czj9bvb/20200812220555-lj3enxa/20210808180321-hbvl5c2/20200915214115-42b8zma.sy +++ b/app/guide/20210808180117-czj9bvb/20200812220555-lj3enxa/20210808180321-hbvl5c2/20200915214115-42b8zma.sy @@ -5,7 +5,7 @@ "id": "20200915214115-42b8zma", "title": "资源文件", "type": "doc", - "updated": "20220715092726" + "updated": "20220715105016" }, "Children": [ { @@ -547,7 +547,7 @@ "ListData": {}, "Properties": { "id": "20220503121213-afjyt05", - "updated": "20220715092726" + "updated": "20220715105016" }, "Children": [ { @@ -572,7 +572,15 @@ "Children": [ { "Type": "NodeText", - "Data": "删除该笔记本时,为保证跨笔记本资源文件引用正常工作,该笔记本下的资源文件会被批量复制到全局 assets 中" + "Data": "删除该笔记本时,为保证跨笔记本资源文件引用正常工作,该笔记本下的资源文件会被批量复制到全局 " + }, + { + "Type": "NodeText", + "Data": "assets" + }, + { + "Type": "NodeText", + "Data": " 中" } ] } @@ -661,6 +669,34 @@ ] } ] + }, + { + "ID": "20220715105014-5jfe0bz", + "Type": "NodeListItem", + "ListData": { + "BulletChar": 42, + "Marker": "Kg==" + }, + "Properties": { + "id": "20220715105014-5jfe0bz", + "updated": "20220715105016" + }, + "Children": [ + { + "ID": "20220715105014-3r1es71", + "Type": "NodeParagraph", + "Properties": { + "id": "20220715105014-3r1es71", + "updated": "20220715105016" + }, + "Children": [ + { + "Type": "NodeText", + "Data": "不支持重命名" + } + ] + } + ] } ] } diff --git a/app/guide/20211226090932-5lcq56f/20211226115423-d5z1joq/20211226121203-rjjngpz/20211226123038-4umgpxy.sy b/app/guide/20211226090932-5lcq56f/20211226115423-d5z1joq/20211226121203-rjjngpz/20211226123038-4umgpxy.sy index 7642567f9..7ad7de34d 100644 --- a/app/guide/20211226090932-5lcq56f/20211226115423-d5z1joq/20211226121203-rjjngpz/20211226123038-4umgpxy.sy +++ b/app/guide/20211226090932-5lcq56f/20211226115423-d5z1joq/20211226121203-rjjngpz/20211226123038-4umgpxy.sy @@ -4,7 +4,7 @@ "Properties": { "id": "20211226123038-4umgpxy", "title": "資料文件", - "updated": "20220715092951" + "updated": "20220715105038" }, "Children": [ { @@ -554,7 +554,7 @@ "ListData": {}, "Properties": { "id": "20220503122338-jmr7c2k", - "updated": "20220715092938" + "updated": "20220715105038" }, "Children": [ { @@ -579,7 +579,15 @@ "Children": [ { "Type": "NodeText", - "Data": "刪除該筆記本時,為保證跨筆記本資料文件引用正常工作,該筆記本下的資料文件會被批量複製到全局 assets 中" + "Data": "刪除該筆記本時,為保證跨筆記本資料文件引用正常工作,該筆記本下的資料文件會被批量複製到全局 " + }, + { + "Type": "NodeText", + "Data": "assets" + }, + { + "Type": "NodeText", + "Data": " 中" } ] } @@ -607,7 +615,15 @@ "Children": [ { "Type": "NodeText", - "Data": "不支持在數據歷史中查看筆記本級資料文件歷史" + "Data": "不支持在" + }, + { + "Type": "NodeText", + "Data": "數據歷史" + }, + { + "Type": "NodeText", + "Data": "中查看筆記本級資料文件歷史" } ] } @@ -660,6 +676,34 @@ ] } ] + }, + { + "ID": "20220715105038-bwu5wmg", + "Type": "NodeListItem", + "ListData": { + "BulletChar": 42, + "Marker": "Kg==" + }, + "Properties": { + "id": "20220715105038-bwu5wmg", + "updated": "20220715105038" + }, + "Children": [ + { + "ID": "20220715105038-mmy9v15", + "Type": "NodeParagraph", + "Properties": { + "id": "20220715105038-mmy9v15", + "updated": "20220715105038" + }, + "Children": [ + { + "Type": "NodeText", + "Data": "不支持重命名" + } + ] + } + ] } ] } diff --git a/kernel/api/asset.go b/kernel/api/asset.go index 0d4d0cdf4..4d3a80d04 100644 --- a/kernel/api/asset.go +++ b/kernel/api/asset.go @@ -28,6 +28,26 @@ import ( "github.com/siyuan-note/siyuan/kernel/util" ) +func renameAsset(c *gin.Context) { + ret := gulu.Ret.NewResult() + defer c.JSON(http.StatusOK, ret) + + arg, ok := util.JsonArg(c, ret) + if !ok { + return + } + + oldPath := arg["oldPath"].(string) + newName := arg["newName"].(string) + err := model.RenameAsset(oldPath, newName) + if nil != err { + ret.Code = -1 + ret.Msg = err.Error() + ret.Data = map[string]interface{}{"closeTimeout": 5000} + return + } +} + func getDocImageAssets(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 8c9a32ef9..370dcf34c 100644 --- a/kernel/api/router.go +++ b/kernel/api/router.go @@ -185,6 +185,7 @@ func ServeAPI(ginServer *gin.Engine) { ginServer.Handle("POST", "/api/asset/removeUnusedAsset", model.CheckAuth, model.CheckReadonly, removeUnusedAsset) ginServer.Handle("POST", "/api/asset/removeUnusedAssets", model.CheckAuth, model.CheckReadonly, removeUnusedAssets) ginServer.Handle("POST", "/api/asset/getDocImageAssets", model.CheckAuth, model.CheckReadonly, getDocImageAssets) + ginServer.Handle("POST", "/api/asset/renameAsset", model.CheckAuth, model.CheckReadonly, renameAsset) ginServer.Handle("POST", "/api/export/batchExportMd", model.CheckAuth, batchExportMd) ginServer.Handle("POST", "/api/export/exportMd", model.CheckAuth, exportMd) diff --git a/kernel/model/assets.go b/kernel/model/assets.go index 05474f7b1..6cc0978ab 100644 --- a/kernel/model/assets.go +++ b/kernel/model/assets.go @@ -34,6 +34,7 @@ import ( "github.com/88250/gulu" "github.com/88250/lute/ast" "github.com/88250/lute/parse" + "github.com/88250/protyle" "github.com/gabriel-vasile/mimetype" "github.com/siyuan-note/filelock" "github.com/siyuan-note/httpclient" @@ -443,6 +444,81 @@ func RemoveUnusedAsset(p string) (ret string) { return } +func RenameAsset(oldPath, newName string) (err error) { + util.PushEndlessProgress(Conf.Language(110)) + defer util.PushClearProgress() + + notebooks, err := ListNotebooks() + if nil != err { + return + } + + newName = strings.TrimSpace(newName) + newName = gulu.Str.RemoveInvisible(newName) + if path.Base(oldPath) == newName { + return + } + if "" == newName { + return + } + + if !gulu.File.IsValidFilename(newName) { + err = errors.New(Conf.Language(151)) + return + } + + newPath := util.AssetName(newName) + if err = gulu.File.Copy(filepath.Join(util.DataDir, oldPath), filepath.Join(util.DataDir, newPath)); nil != err { + util.LogErrorf("copy asset [%s] failed: %s", oldPath, err) + return + } + + luteEngine := NewLute() + for _, notebook := range notebooks { + pages := pagedPaths(filepath.Join(util.DataDir, notebook.ID), 32) + for _, paths := range pages { + for _, treeAbsPath := range paths { + data, readErr := filelock.NoLockFileRead(treeAbsPath) + if nil != readErr { + util.LogErrorf("get data [path=%s] failed: %s", treeAbsPath, readErr) + err = readErr + return + } + + if !bytes.Contains(data, []byte(oldPath)) { + return + } + + data = bytes.Replace(data, []byte(oldPath), []byte(newPath), -1) + if writeErr := filelock.NoLockFileWrite(treeAbsPath, data); nil != writeErr { + util.LogErrorf("write data [path=%s] failed: %s", treeAbsPath, writeErr) + err = writeErr + return + } + + tree, parseErr := protyle.ParseJSONWithoutFix(luteEngine, data) + if nil != parseErr { + util.LogErrorf("parse json to tree [%s] failed: %s", treeAbsPath, parseErr) + err = parseErr + return + } + + treenode.ReindexBlockTree(tree) + sql.UpsertTreeQueue(tree) + + util.PushEndlessProgress(fmt.Sprintf(Conf.Language(111), tree.Root.IALAttr("title"))) + } + } + } + + IncSync() + + util.PushEndlessProgress(Conf.Language(113)) + sql.WaitForWritingDatabase() + util.ReloadUI() + return +} + func UnusedAssets() (ret []string) { ret = []string{}