diff --git a/app/appearance/langs/en_US.json b/app/appearance/langs/en_US.json index 402b7f18f..6c40b8e5b 100644 --- a/app/appearance/langs/en_US.json +++ b/app/appearance/langs/en_US.json @@ -1200,7 +1200,7 @@ "38": "Too many keywords mentioned [%d], adjust if necessary [Settings - Search - Backlink Mentions - Keyword Limit]", "39": "Deleting index %s", "40": "Inserting index %s", - "41": "Upload completed", + "41": "Upload completed [%d]", "42": "The setting is complete, the application will be closed automatically, please restart later...", "43": "The maximum storage capacity of cloud space [%s] has been exceeded, and data upload cannot continue", "44": "Parse template failed: %s", diff --git a/app/appearance/langs/es_ES.json b/app/appearance/langs/es_ES.json index 8f1e92d4e..136e9e485 100644 --- a/app/appearance/langs/es_ES.json +++ b/app/appearance/langs/es_ES.json @@ -1200,7 +1200,7 @@ "38": "Demasiadas palabras clave mencionadas [%d], ajústelas si es necesario [Configuración - Búsqueda - Menciones de backlinks - Límite de palabras clave]", "39": "Eliminando índice %s", "40": "Insertando índice %s", - "41": "Carga completada", + "41": "Carga completada [%d]", "42": "La configuración se ha completado, la aplicación se cerrará automáticamente, por favor reinicie más tarde...", "43": "Se ha superado la capacidad máxima de almacenamiento del espacio en la nube [%s] y la carga de datos no puede continuar", "44": "El proceso de análisis de la plantilla ha fallado: %s", diff --git a/app/appearance/langs/fr_FR.json b/app/appearance/langs/fr_FR.json index 7defe0f66..4f7010f2d 100644 --- a/app/appearance/langs/fr_FR.json +++ b/app/appearance/langs/fr_FR.json @@ -1200,7 +1200,7 @@ "38": "Trop de mots-clés mentionnés [%d], ajustez si nécessaire [Paramètres - Recherche - Mentions de backlink - Limite de mots-clés]", "39": "Suppression de l'index %s", "40": "Insertion de l'index %s", - "41": "Transfert complété", + "41": "Transfert complété [%d]", "42": "Le paramétrage est terminé, l'application se fermera automatiquement, merci de redémarrer plus tard...", "43": "La capacité de stockage maximale de l'espace cloud [%s] a été dépassée et le téléchargement des données ne peut pas continuer", "44": "L'analyse du template a échoué : %s", diff --git a/app/appearance/langs/zh_CHT.json b/app/appearance/langs/zh_CHT.json index 171084d0e..f0a2de14f 100644 --- a/app/appearance/langs/zh_CHT.json +++ b/app/appearance/langs/zh_CHT.json @@ -1200,7 +1200,7 @@ "38": "提及關鍵字數量 [%d] 過多,如有需要可以調整 [設置 - 搜索 - 反連提及 - 關鍵字數量限制]", "39": "正在刪除索引 %s", "40": "正在插入索引 %s", - "41": "上傳完畢", + "41": "上傳完畢 [%d]", "42": "設置完成,即將自動關閉應用,請稍後重新啟動...", "43": "已超過雲端空間最大存儲容量 [%s],無法繼續上傳資料", "44": "範本解析失敗:%s", diff --git a/app/appearance/langs/zh_CN.json b/app/appearance/langs/zh_CN.json index 1a66783ad..cce910915 100644 --- a/app/appearance/langs/zh_CN.json +++ b/app/appearance/langs/zh_CN.json @@ -1200,7 +1200,7 @@ "38": "提及关键字数量 [%d] 过多,如有需要可以调整 [设置 - 搜索 - 反链提及 - 关键字数量限制]", "39": "正在删除索引 %s", "40": "正在插入索引 %s", - "41": "上传完毕", + "41": "上传完毕 [%d]", "42": "设置完成,即将自动关闭应用,请稍后重新启动...", "43": "已超过云端空间最大存储容量 [%s],无法继续上传数据", "44": "模板解析失败:%s", diff --git a/app/changelogs/v2.12.0/v2.12.0.md b/app/changelogs/v2.12.0/v2.12.0.md index 769c78d05..fa104d3c2 100644 --- a/app/changelogs/v2.12.0/v2.12.0.md +++ b/app/changelogs/v2.12.0/v2.12.0.md @@ -35,6 +35,8 @@ Below are the detailed changes in this version. * [Search and replace fails in some cases](https://github.com/siyuan-note/siyuan/issues/10016) * [Reference jump is not located in read-only mode](https://github.com/siyuan-note/siyuan/issues/10028) * [Converting PDF annotation ref to text fails after setting the appearance](https://github.com/siyuan-note/siyuan/issues/10029) +* [Pressing the scoring shortcut key immediately after `Alt+F` is invalid](https://github.com/siyuan-note/siyuan/issues/10020) +* [The images in the embed blocks are not uploaded to the community hosting](https://github.com/siyuan-note/siyuan/issues/10042) ### Development diff --git a/app/changelogs/v2.12.0/v2.12.0_zh_CHT.md b/app/changelogs/v2.12.0/v2.12.0_zh_CHT.md index 6551351e1..e2be68fff 100644 --- a/app/changelogs/v2.12.0/v2.12.0_zh_CHT.md +++ b/app/changelogs/v2.12.0/v2.12.0_zh_CHT.md @@ -35,6 +35,8 @@ * [某些情況下搜尋替換失效](https://github.com/siyuan-note/siyuan/issues/10016) * [唯讀模式下引用跳轉後未定位瀏覽位置](https://github.com/siyuan-note/siyuan/issues/10028) * [無法轉換外觀樣式的 PDF 註解引用為文字](https://github.com/siyuan-note/siyuan/issues/10029) +* [`Alt+F` 後快速按下評分快捷鍵失效](https://github.com/siyuan-note/siyuan/issues/10020) +* [在嵌入區塊中的圖片未能上傳社群](https://github.com/siyuan-note/siyuan/issues/10042) ### 開發者 diff --git a/app/changelogs/v2.12.0/v2.12.0_zh_CN.md b/app/changelogs/v2.12.0/v2.12.0_zh_CN.md index 5f820fb8b..da9159b30 100644 --- a/app/changelogs/v2.12.0/v2.12.0_zh_CN.md +++ b/app/changelogs/v2.12.0/v2.12.0_zh_CN.md @@ -35,6 +35,8 @@ * [某些情况下搜索替换失效](https://github.com/siyuan-note/siyuan/issues/10016) * [只读模式下引用跳转后未定位浏览位置](https://github.com/siyuan-note/siyuan/issues/10028) * [无法转换带外观样式的 PDF 注释引用为文本](https://github.com/siyuan-note/siyuan/issues/10029) +* [`Alt+F` 后快速按下评分快捷键失效](https://github.com/siyuan-note/siyuan/issues/10020) +* [在嵌入块中的图片未能上传社区](https://github.com/siyuan-note/siyuan/issues/10042) ### 开发者 diff --git a/kernel/api/asset.go b/kernel/api/asset.go index e8514cb4d..0dc9c6133 100644 --- a/kernel/api/asset.go +++ b/kernel/api/asset.go @@ -17,6 +17,7 @@ package api import ( + "fmt" "net/http" "path/filepath" "strings" @@ -257,14 +258,15 @@ func uploadCloud(c *gin.Context) { } rootID := arg["id"].(string) - err := model.UploadAssets2Cloud(rootID) + count, err := model.UploadAssets2Cloud(rootID) if nil != err { ret.Code = -1 ret.Msg = err.Error() ret.Data = map[string]interface{}{"closeTimeout": 3000} - } else { - util.PushMsg(model.Conf.Language(41), 3000) + return } + + util.PushMsg(fmt.Sprintf(model.Conf.Language(41), count), 3000) } func insertLocalAssets(c *gin.Context) { diff --git a/kernel/av/value.go b/kernel/av/value.go index 29c3bb966..0e70548cc 100644 --- a/kernel/av/value.go +++ b/kernel/av/value.go @@ -475,7 +475,7 @@ type ValueRollup struct { Contents []*Value `json:"contents"` } -func (r *ValueRollup) RenderContents(calc *RollupCalc) { +func (r *ValueRollup) RenderContents(calc *RollupCalc, destKey *Key) { if nil == calc { return } @@ -531,103 +531,105 @@ func (r *ValueRollup) RenderContents(calc *RollupCalc) { case CalcOperatorSum: sum := 0.0 for _, v := range r.Contents { - if "" != v.String() { - n, _ := strconv.ParseFloat(v.String(), 64) - sum += n + if nil != v.Number { + sum += v.Number.Content } } - r.Contents = []*Value{{Type: KeyTypeNumber, Number: NewValueNumber(sum)}} + r.Contents = []*Value{{Type: KeyTypeNumber, Number: NewFormattedValueNumber(sum, destKey.NumberFormat)}} case CalcOperatorAverage: sum := 0.0 count := 0 for _, v := range r.Contents { - if "" != v.String() { - n, _ := strconv.ParseFloat(v.String(), 64) - sum += n + if nil != v.Number { + sum += v.Number.Content count++ } } if 0 < count { - r.Contents = []*Value{{Type: KeyTypeNumber, Number: NewValueNumber(sum / float64(count))}} + r.Contents = []*Value{{Type: KeyTypeNumber, Number: NewFormattedValueNumber(sum/float64(count), destKey.NumberFormat)}} } case CalcOperatorMedian: var numbers []float64 for _, v := range r.Contents { - if "" != v.String() { - n, _ := strconv.ParseFloat(v.String(), 64) - numbers = append(numbers, n) + if nil != v.Number { + numbers = append(numbers, v.Number.Content) } } sort.Float64s(numbers) if 0 < len(numbers) { - r.Contents = []*Value{{Type: KeyTypeNumber, Number: NewValueNumber(numbers[len(numbers)/2])}} + r.Contents = []*Value{{Type: KeyTypeNumber, Number: NewFormattedValueNumber(numbers[len(numbers)/2], destKey.NumberFormat)}} } case CalcOperatorMin: min := math.MaxFloat64 for _, v := range r.Contents { - if "" != v.String() { - n, _ := strconv.ParseFloat(v.String(), 64) - if n < min { - min = n + if nil != v.Number { + if v.Number.Content < min { + min = v.Number.Content } } } - r.Contents = []*Value{{Type: KeyTypeNumber, Number: NewValueNumber(min)}} + r.Contents = []*Value{{Type: KeyTypeNumber, Number: NewFormattedValueNumber(min, destKey.NumberFormat)}} case CalcOperatorMax: max := -math.MaxFloat64 for _, v := range r.Contents { - if "" != v.String() { - n, _ := strconv.ParseFloat(v.String(), 64) - if n > max { - max = n + if nil != v.Number { + if v.Number.Content > max { + max = v.Number.Content } } } - r.Contents = []*Value{{Type: KeyTypeNumber, Number: NewValueNumber(max)}} + r.Contents = []*Value{{Type: KeyTypeNumber, Number: NewFormattedValueNumber(max, destKey.NumberFormat)}} case CalcOperatorRange: min := math.MaxFloat64 max := -math.MaxFloat64 for _, v := range r.Contents { - if "" != v.String() { - n, _ := strconv.ParseFloat(v.String(), 64) - if n < min { - min = n + if nil != v.Number { + if v.Number.Content < min { + min = v.Number.Content } - if n > max { - max = n + if v.Number.Content > max { + max = v.Number.Content } } } - r.Contents = []*Value{{Type: KeyTypeNumber, Number: NewValueNumber(max - min)}} + r.Contents = []*Value{{Type: KeyTypeNumber, Number: NewFormattedValueNumber(max-min, destKey.NumberFormat)}} case CalcOperatorChecked: countChecked := 0 for _, v := range r.Contents { - if "√" == v.String() { - countChecked++ + if nil != v.Checkbox { + if v.Checkbox.Checked { + countChecked++ + } } } r.Contents = []*Value{{Type: KeyTypeNumber, Number: NewValueNumber(float64(countChecked))}} case CalcOperatorUnchecked: countUnchecked := 0 for _, v := range r.Contents { - if "√" != v.String() { - countUnchecked++ + if nil != v.Checkbox { + if !v.Checkbox.Checked { + countUnchecked++ + } } } r.Contents = []*Value{{Type: KeyTypeNumber, Number: NewValueNumber(float64(countUnchecked))}} case CalcOperatorPercentChecked: countChecked := 0 for _, v := range r.Contents { - if "√" == v.String() { - countChecked++ + if nil != v.Checkbox { + if v.Checkbox.Checked { + countChecked++ + } } } r.Contents = []*Value{{Type: KeyTypeNumber, Number: NewValueNumber(float64(countChecked * 100 / len(r.Contents)))}} case CalcOperatorPercentUnchecked: countUnchecked := 0 for _, v := range r.Contents { - if "√" != v.String() { - countUnchecked++ + if nil != v.Checkbox { + if !v.Checkbox.Checked { + countUnchecked++ + } } } r.Contents = []*Value{{Type: KeyTypeNumber, Number: NewValueNumber(float64(countUnchecked * 100 / len(r.Contents)))}} diff --git a/kernel/model/assets.go b/kernel/model/assets.go index 855c877d4..90ec91908 100644 --- a/kernel/model/assets.go +++ b/kernel/model/assets.go @@ -20,6 +20,7 @@ import ( "bytes" "errors" "fmt" + "github.com/88250/lute/editor" "io" "io/fs" "mime" @@ -509,13 +510,25 @@ func GetAssetAbsPath(relativePath string) (ret string, err error) { return "", errors.New(fmt.Sprintf(Conf.Language(12), relativePath)) } -func UploadAssets2Cloud(rootID string) (err error) { +func UploadAssets2Cloud(rootID string) (count int, err error) { if !IsSubscriber() { return } - sqlAssets := sql.QueryRootBlockAssets(rootID) - err = uploadAssets2Cloud(sqlAssets, bizTypeUploadAssets) + tree, err := loadTreeByBlockID(rootID) + if nil != err { + return + } + + assets := assetsLinkDestsInTree(tree) + embedAssets := assetsLinkDestsInQueryEmbedNodes(tree) + assets = append(assets, embedAssets...) + assets = gulu.Str.RemoveDuplicatedElem(assets) + err = uploadAssets2Cloud(assets, bizTypeUploadAssets) + if nil != err { + return + } + count = len(assets) return } @@ -525,17 +538,17 @@ const ( ) // uploadAssets2Cloud 将资源文件上传到云端图床。 -func uploadAssets2Cloud(sqlAssets []*sql.Asset, bizType string) (err error) { +func uploadAssets2Cloud(assetPaths []string, bizType string) (err error) { var uploadAbsAssets []string - for _, asset := range sqlAssets { + for _, assetPath := range assetPaths { var absPath string - absPath, err = GetAssetAbsPath(asset.Path) + absPath, err = GetAssetAbsPath(assetPath) if nil != err { - logging.LogWarnf("get asset [%s] abs path failed: %s", asset, err) + logging.LogWarnf("get asset [%s] abs path failed: %s", assetPath, err) return } if "" == absPath { - logging.LogErrorf("not found asset [%s]", asset) + logging.LogErrorf("not found asset [%s]", assetPath) continue } @@ -1044,9 +1057,45 @@ func emojisInTree(tree *parse.Tree) (ret []string) { return } -func assetsLinkDestsInTree(tree *parse.Tree) (ret []string) { +func assetsLinkDestsInQueryEmbedNodes(tree *parse.Tree) (ret []string) { + // The images in the embed blocks are not uploaded to the community hosting https://github.com/siyuan-note/siyuan/issues/10042 + ret = []string{} ast.Walk(tree.Root, func(n *ast.Node, entering bool) ast.WalkStatus { + if !entering || ast.NodeBlockQueryEmbedScript != n.Type { + return ast.WalkContinue + } + + stmt := n.TokensStr() + stmt = html.UnescapeString(stmt) + stmt = strings.ReplaceAll(stmt, editor.IALValEscNewLine, "\n") + sqlBlocks := sql.SelectBlocksRawStmt(stmt, 1, Conf.Search.Limit) + for _, sqlBlock := range sqlBlocks { + subtree, _ := loadTreeByBlockID(sqlBlock.ID) + if nil == subtree { + continue + } + embedNode := treenode.GetNodeInTree(subtree, sqlBlock.ID) + if nil == embedNode { + continue + } + + ret = append(ret, assetsLinkDestsInNode(embedNode)...) + } + return ast.WalkContinue + }) + ret = gulu.Str.RemoveDuplicatedElem(ret) + return +} + +func assetsLinkDestsInTree(tree *parse.Tree) (ret []string) { + ret = assetsLinkDestsInNode(tree.Root) + return +} + +func assetsLinkDestsInNode(node *ast.Node) (ret []string) { + ret = []string{} + ast.Walk(node, func(n *ast.Node, entering bool) ast.WalkStatus { // 修改以下代码时需要同时修改 database 构造行级元素实现,增加必要的类型 if !entering || (ast.NodeLinkDest != n.Type && ast.NodeHTMLBlock != n.Type && ast.NodeInlineHTML != n.Type && ast.NodeIFrame != n.Type && ast.NodeWidget != n.Type && ast.NodeAudio != n.Type && ast.NodeVideo != n.Type && @@ -1103,7 +1152,7 @@ func assetsLinkDestsInTree(tree *parse.Tree) (ret []string) { dest := strings.TrimSpace(string(src)) ret = append(ret, dest) } else { - logging.LogWarnf("src is missing the closing double quote in tree [%s] ", tree.Box+tree.Path) + logging.LogWarnf("src is missing the closing double quote in tree [%s] ", node.Box+node.Path) } } } diff --git a/kernel/model/attribute_view.go b/kernel/model/attribute_view.go index 805025b71..fc86013e4 100644 --- a/kernel/model/attribute_view.go +++ b/kernel/model/attribute_view.go @@ -243,26 +243,22 @@ func GetBlockAttributeViewKeys(blockID string) (ret []*BlockAttributeViewKeys) { relVal := attrView.GetValue(kv.Key.Rollup.RelationKeyID, kv.Values[0].BlockID) if nil != relVal && nil != relVal.Relation { destAv, _ := av.ParseAttributeView(relKey.Relation.AvID) - if nil != destAv { + destKey, _ := destAv.GetKey(kv.Key.Rollup.KeyID) + if nil != destAv && nil != destKey { for _, bID := range relVal.Relation.BlockIDs { destVal := destAv.GetValue(kv.Key.Rollup.KeyID, bID) if nil == destVal { - destKey, _ := destAv.GetKey(kv.Key.Rollup.KeyID) - if nil == destKey { - continue - } - destVal = treenode.GetAttributeViewDefaultValue(ast.NewNodeID(), kv.Key.Rollup.KeyID, blockID, destKey.Type) } - if av.KeyTypeNumber == destVal.Type { - destVal.Number.Format = kv.Key.NumberFormat + if av.KeyTypeNumber == destKey.Type { + destVal.Number.Format = destKey.NumberFormat destVal.Number.FormatNumber() } kv.Values[0].Rollup.Contents = append(kv.Values[0].Rollup.Contents, destVal.Clone()) - kv.Values[0].Rollup.RenderContents(kv.Key.Rollup.Calc) } + kv.Values[0].Rollup.RenderContents(kv.Key.Rollup.Calc, destKey) } } case av.KeyTypeRelation: @@ -817,25 +813,25 @@ func renderAttributeViewTable(attrView *av.AttributeView, view *av.View) (ret *a break } + destKey, _ := destAv.GetKey(rollupKey.Rollup.KeyID) + if nil == destKey { + continue + } + for _, blockID := range relVal.Relation.BlockIDs { destVal := destAv.GetValue(rollupKey.Rollup.KeyID, blockID) if nil == destVal { - destKey, _ := destAv.GetKey(rollupKey.Rollup.KeyID) - if nil == destKey { - continue - } - destVal = treenode.GetAttributeViewDefaultValue(ast.NewNodeID(), rollupKey.Rollup.KeyID, blockID, destKey.Type) } - if av.KeyTypeNumber == destVal.Type { - destVal.Number.Format = rollupKey.NumberFormat + if av.KeyTypeNumber == destKey.Type { + destVal.Number.Format = destKey.NumberFormat destVal.Number.FormatNumber() } cell.Value.Rollup.Contents = append(cell.Value.Rollup.Contents, destVal.Clone()) } - cell.Value.Rollup.RenderContents(rollupKey.Rollup.Calc) + cell.Value.Rollup.RenderContents(rollupKey.Rollup.Calc, destKey) case av.KeyTypeRelation: // 渲染关联列 relKey, _ := attrView.GetKey(cell.Value.KeyID) if nil != relKey && nil != relKey.Relation { diff --git a/kernel/model/conf.go b/kernel/model/conf.go index 72bbe3bef..84f2ba1ff 100644 --- a/kernel/model/conf.go +++ b/kernel/model/conf.go @@ -780,12 +780,17 @@ func IsSubscriber() bool { } func IsPaidUser() bool { + // S3/WebDAV data sync and backup are available for a fee https://github.com/siyuan-note/siyuan/issues/8780 + if IsSubscriber() { return true } - return nil != Conf.GetUser() // Sign in to use S3/WebDAV data sync https://github.com/siyuan-note/siyuan/issues/8779 - // TODO S3/WebDAV data sync and backup are available for a fee https://github.com/siyuan-note/siyuan/issues/8780 - // return nil != Conf.User && 1 == Conf.User.UserSiYuanOneTimePayStatus + + u := Conf.GetUser() + if nil == u { + return false + } + return 1 == u.UserSiYuanOneTimePayStatus } const ( diff --git a/kernel/model/export.go b/kernel/model/export.go index 367b95b6d..62d6f02d6 100644 --- a/kernel/model/export.go +++ b/kernel/model/export.go @@ -67,8 +67,11 @@ func Export2Liandi(id string) (err error) { return errors.New(Conf.Language(204)) } - sqlAssets := sql.QueryRootBlockAssets(id) - err = uploadAssets2Cloud(sqlAssets, bizTypeExport2Liandi) + assets := assetsLinkDestsInTree(tree) + embedAssets := assetsLinkDestsInQueryEmbedNodes(tree) + assets = append(assets, embedAssets...) + assets = gulu.Str.RemoveDuplicatedElem(assets) + err = uploadAssets2Cloud(assets, bizTypeExport2Liandi) if nil != err { return } diff --git a/kernel/sql/asset.go b/kernel/sql/asset.go index 332dfd850..66d619d55 100644 --- a/kernel/sql/asset.go +++ b/kernel/sql/asset.go @@ -115,21 +115,6 @@ func QueryAssetByHash(hash string) (ret *Asset) { return } -func QueryRootBlockAssets(rootID string) (ret []*Asset) { - sqlStmt := "SELECT * FROM assets WHERE root_id = ?" - rows, err := query(sqlStmt, rootID) - if nil != err { - logging.LogErrorf("sql query [%s] failed: %s", sqlStmt, err) - return - } - defer rows.Close() - for rows.Next() { - asset := scanAssetRows(rows) - ret = append(ret, asset) - } - return -} - func scanAssetRows(rows *sql.Rows) (ret *Asset) { var asset Asset if err := rows.Scan(&asset.ID, &asset.BlockID, &asset.RootID, &asset.Box, &asset.DocPath, &asset.Path, &asset.Name, &asset.Title, &asset.Hash); nil != err { diff --git a/kernel/treenode/node.go b/kernel/treenode/node.go index 6f335c9f7..0853c513b 100644 --- a/kernel/treenode/node.go +++ b/kernel/treenode/node.go @@ -774,25 +774,25 @@ func renderAttributeViewTable(attrView *av.AttributeView, view *av.View) (ret *a break } + destKey, _ := destAv.GetKey(rollupKey.Rollup.KeyID) + if nil == destKey { + continue + } + for _, blockID := range relVal.Relation.BlockIDs { destVal := destAv.GetValue(rollupKey.Rollup.KeyID, blockID) if nil == destVal { - destKey, _ := destAv.GetKey(rollupKey.Rollup.KeyID) - if nil == destKey { - continue - } - destVal = GetAttributeViewDefaultValue(ast.NewNodeID(), rollupKey.Rollup.KeyID, blockID, destKey.Type) } - if av.KeyTypeNumber == destVal.Type { - destVal.Number.Format = rollupKey.NumberFormat + if av.KeyTypeNumber == destKey.Type { + destVal.Number.Format = destKey.NumberFormat destVal.Number.FormatNumber() } cell.Value.Rollup.Contents = append(cell.Value.Rollup.Contents, destVal.Clone()) } - cell.Value.Rollup.RenderContents(rollupKey.Rollup.Calc) + cell.Value.Rollup.RenderContents(rollupKey.Rollup.Calc, destKey) case av.KeyTypeRelation: // 渲染关联列 relKey, _ := attrView.GetKey(cell.Value.KeyID) if nil != relKey && nil != relKey.Relation {