diff --git a/app/src/protyle/wysiwyg/index.ts b/app/src/protyle/wysiwyg/index.ts index 41219f7ae..0f14244f2 100644 --- a/app/src/protyle/wysiwyg/index.ts +++ b/app/src/protyle/wysiwyg/index.ts @@ -1882,7 +1882,8 @@ export class WYSIWYG { } } else if (linkAddress) { /// #if !BROWSER - if (0 > linkAddress.indexOf("://")) { + if (0 > linkAddress.indexOf(":")) { + // 使用 : 判断,不使用 :// 判断 Open external application protocol invalid https://github.com/siyuan-note/siyuan/issues/10075 // Support click to open hyperlinks like `www.foo.com` https://github.com/siyuan-note/siyuan/issues/9986 linkAddress = `https://${linkAddress}`; } diff --git a/kernel/api/export.go b/kernel/api/export.go index 9bfc7f913..539c257fa 100644 --- a/kernel/api/export.go +++ b/kernel/api/export.go @@ -33,6 +33,29 @@ import ( "github.com/siyuan-note/siyuan/kernel/util" ) +func exportAttributeView(c *gin.Context) { + ret := gulu.Ret.NewResult() + defer c.JSON(http.StatusOK, ret) + + arg, ok := util.JsonArg(c, ret) + if !ok { + return + } + + avID := arg["id"].(string) + zipPath, err := model.ExportAv2CSV(avID) + if nil != err { + ret.Code = 1 + ret.Msg = err.Error() + ret.Data = map[string]interface{}{"closeTimeout": 7000} + return + } + + ret.Data = map[string]interface{}{ + "zip": zipPath, + } +} + func exportEPUB(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 011626fea..e8ae70c3d 100644 --- a/kernel/api/router.go +++ b/kernel/api/router.go @@ -283,6 +283,7 @@ func ServeAPI(ginServer *gin.Engine) { ginServer.Handle("POST", "/api/export/exportODT", model.CheckAuth, exportODT) ginServer.Handle("POST", "/api/export/exportRTF", model.CheckAuth, exportRTF) ginServer.Handle("POST", "/api/export/exportEPUB", model.CheckAuth, exportEPUB) + ginServer.Handle("POST", "/api/export/exportAttributeView", model.CheckAuth, exportAttributeView) ginServer.Handle("POST", "/api/import/importStdMd", model.CheckAuth, model.CheckReadonly, importStdMd) ginServer.Handle("POST", "/api/import/importData", model.CheckAuth, model.CheckReadonly, importData) diff --git a/kernel/model/export.go b/kernel/model/export.go index 03654915a..118c37f87 100644 --- a/kernel/model/export.go +++ b/kernel/model/export.go @@ -56,8 +56,8 @@ import ( "github.com/siyuan-note/siyuan/kernel/util" ) -func ExportAv2CSV(avID string) (csvPath string, err error) { - // TODO: Database table view supports export as CSV https://github.com/siyuan-note/siyuan/issues/10072 +func ExportAv2CSV(avID string) (zipPath string, err error) { + // Database block supports export as CSV https://github.com/siyuan-note/siyuan/issues/10072 attrView, err := av.ParseAttributeView(avID) if nil != err { @@ -69,25 +69,26 @@ func ExportAv2CSV(avID string) (csvPath string, err error) { return } + name := util.FilterFileName(attrView.Name) + table, err := renderAttributeViewTable(attrView, view) if nil != err { logging.LogErrorf("render attribute view [%s] table failed: %s", avID, err) return } - tmpExport := filepath.Join(util.TempDir, "export", "csv") - if err = os.MkdirAll(tmpExport, 0755); nil != err { - logging.LogErrorf("mkdir [%s] failed: %s", tmpExport, err) + exportFolder := filepath.Join(util.TempDir, "export", "csv", name) + if err = os.MkdirAll(exportFolder, 0755); nil != err { + logging.LogErrorf("mkdir [%s] failed: %s", exportFolder, err) return } - csvPath = filepath.Join(tmpExport, avID+".csv") + csvPath := filepath.Join(exportFolder, name+".csv") f, err := os.OpenFile(csvPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644) if nil != err { logging.LogErrorf("open [%s] failed: %s", csvPath, err) return } - defer f.Close() writer := csv.NewWriter(f) @@ -97,6 +98,7 @@ func ExportAv2CSV(avID string) (csvPath string, err error) { } if err = writer.Write(header); nil != err { logging.LogErrorf("write csv header [%s] failed: %s", header, err) + f.Close() return } @@ -136,10 +138,38 @@ func ExportAv2CSV(avID string) (csvPath string, err error) { } if err = writer.Write(rowVal); nil != err { logging.LogErrorf("write csv row [%s] failed: %s", rowVal, err) + f.Close() return } } writer.Flush() + + zipPath = exportFolder + ".db.zip" + zip, err := gulu.Zip.Create(zipPath) + if nil != err { + logging.LogErrorf("create export .db.zip [%s] failed: %s", exportFolder, err) + f.Close() + return + } + + if err = zip.AddDirectory("", exportFolder); nil != err { + logging.LogErrorf("create export .db.zip [%s] failed: %s", exportFolder, err) + f.Close() + return + } + + if err = zip.Close(); nil != err { + logging.LogErrorf("close export .db.zip failed: %s", err) + f.Close() + return + } + + f.Close() + removeErr := os.RemoveAll(exportFolder) + if nil != removeErr { + logging.LogErrorf("remove export folder [%s] failed: %s", exportFolder, removeErr) + } + zipPath = "/export/" + url.PathEscape(filepath.Base(zipPath)) return }