2023-06-24 20:39:55 +08:00
// SiYuan - Refactor your thinking
2023-03-02 11:32:39 +08:00
// Copyright (c) 2020-present, b3log.org
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package model
import (
2023-10-01 10:58:46 +08:00
"bytes"
2023-11-23 11:22:35 +08:00
"os"
"path/filepath"
2023-07-13 09:37:52 +08:00
"sort"
2023-11-23 11:22:35 +08:00
"strconv"
2023-06-30 20:23:31 +08:00
"strings"
2023-10-01 10:58:46 +08:00
"text/template"
2023-10-08 10:44:23 +08:00
"time"
2023-06-30 20:23:31 +08:00
"github.com/88250/gulu"
2023-03-03 10:49:45 +08:00
"github.com/88250/lute/ast"
2023-03-02 14:47:57 +08:00
"github.com/88250/lute/parse"
2023-11-23 22:10:01 +08:00
"github.com/siyuan-note/dejavu/entity"
2023-11-06 22:13:04 +08:00
"github.com/siyuan-note/filelock"
2023-03-02 15:03:33 +08:00
"github.com/siyuan-note/logging"
2023-03-02 11:32:39 +08:00
"github.com/siyuan-note/siyuan/kernel/av"
2024-03-04 14:14:10 +08:00
"github.com/siyuan-note/siyuan/kernel/cache"
2023-03-02 11:32:39 +08:00
"github.com/siyuan-note/siyuan/kernel/treenode"
2023-07-03 15:29:54 +08:00
"github.com/siyuan-note/siyuan/kernel/util"
2024-04-09 17:51:39 +08:00
"github.com/xrash/smetrics"
2023-03-02 11:32:39 +08:00
)
2024-03-04 11:51:03 +08:00
func SetDatabaseBlockView ( blockID , viewID string ) ( err error ) {
node , tree , err := getNodeByBlockID ( nil , blockID )
if nil != err {
return
}
attrs := parse . IAL2Map ( node . KramdownIAL )
attrs [ av . NodeAttrView ] = viewID
err = setNodeAttrs ( node , tree , attrs )
if nil != err {
logging . LogWarnf ( "set node [%s] attrs failed: %s" , blockID , err )
return
}
return
}
2024-04-07 09:54:55 +08:00
func GetAttributeViewPrimaryKeyValues ( avID , keyword string , page , pageSize int ) ( attributeViewName string , databaseBlockIDs [ ] string , keyValues * av . KeyValues , err error ) {
2024-02-23 22:22:54 +08:00
waitForSyncingStorages ( )
attrView , err := av . ParseAttributeView ( avID )
if nil != err {
logging . LogErrorf ( "parse attribute view [%s] failed: %s" , avID , err )
return
}
2024-02-23 22:55:35 +08:00
attributeViewName = attrView . Name
2024-04-07 11:43:58 +08:00
if "" == attributeViewName {
attributeViewName = Conf . language ( 105 )
}
2024-02-23 22:55:35 +08:00
2024-04-07 09:54:55 +08:00
databaseBlockIDs = treenode . GetMirrorAttrViewBlockIDs ( avID )
2024-02-23 22:55:35 +08:00
keyValues = attrView . GetBlockKeyValues ( )
2024-02-23 23:32:23 +08:00
// 过滤掉不在视图中的值
2024-02-23 22:55:35 +08:00
tmp := map [ string ] * av . Value { }
for _ , kv := range keyValues . Values {
for _ , view := range attrView . Views {
switch view . LayoutType {
case av . LayoutTypeTable :
2024-02-23 23:32:23 +08:00
if ! kv . IsDetached {
if nil == treenode . GetBlockTree ( kv . BlockID ) {
break
}
}
2024-04-10 17:27:17 +08:00
tmp [ kv . Block . ID ] = kv
2024-02-23 22:55:35 +08:00
}
}
}
keyValues . Values = [ ] * av . Value { }
for _ , v := range tmp {
2024-04-14 12:34:06 +08:00
if strings . Contains ( strings . ToLower ( v . String ( true ) ) , strings . ToLower ( keyword ) ) {
2024-03-06 09:40:04 +08:00
keyValues . Values = append ( keyValues . Values , v )
}
2024-02-23 22:55:35 +08:00
}
2024-02-23 22:22:54 +08:00
if 1 > pageSize {
2024-04-10 17:28:30 +08:00
pageSize = 16
2024-02-23 22:22:54 +08:00
}
start := ( page - 1 ) * pageSize
end := start + pageSize
2024-02-23 22:55:35 +08:00
if len ( keyValues . Values ) < end {
end = len ( keyValues . Values )
2024-02-23 22:22:54 +08:00
}
2024-02-23 22:55:35 +08:00
keyValues . Values = keyValues . Values [ start : end ]
2024-02-23 22:22:54 +08:00
return
}
2024-03-06 00:00:43 +08:00
func GetAttributeViewFilterSort ( avID , blockID string ) ( filters [ ] * av . ViewFilter , sorts [ ] * av . ViewSort ) {
2024-01-07 22:53:05 +08:00
waitForSyncingStorages ( )
2024-03-06 00:00:43 +08:00
attrView , err := av . ParseAttributeView ( avID )
2024-01-07 22:53:05 +08:00
if nil != err {
2024-03-06 00:00:43 +08:00
logging . LogErrorf ( "parse attribute view [%s] failed: %s" , avID , err )
2024-01-07 22:53:05 +08:00
return
}
2024-03-06 00:00:43 +08:00
view , err := getAttrViewViewByBlockID ( attrView , blockID )
if nil == view {
view , err = attrView . GetCurrentView ( attrView . ViewID )
if nil != err {
logging . LogErrorf ( "get current view failed: %s" , err )
return
}
}
2024-01-07 22:53:05 +08:00
filters = [ ] * av . ViewFilter { }
sorts = [ ] * av . ViewSort { }
2024-03-06 00:00:43 +08:00
switch view . LayoutType {
case av . LayoutTypeTable :
filters = view . Table . Filters
sorts = view . Table . Sorts
2024-01-07 22:53:05 +08:00
}
return
}
2023-12-29 09:54:54 +08:00
func SearchAttributeViewNonRelationKey ( avID , keyword string ) ( ret [ ] * av . Key ) {
waitForSyncingStorages ( )
ret = [ ] * av . Key { }
attrView , err := av . ParseAttributeView ( avID )
if nil != err {
logging . LogErrorf ( "parse attribute view [%s] failed: %s" , avID , err )
return
}
for _ , keyValues := range attrView . KeyValues {
2024-01-01 17:52:37 +08:00
if av . KeyTypeRelation != keyValues . Key . Type && av . KeyTypeRollup != keyValues . Key . Type && av . KeyTypeTemplate != keyValues . Key . Type && av . KeyTypeCreated != keyValues . Key . Type && av . KeyTypeUpdated != keyValues . Key . Type {
2023-12-29 09:54:54 +08:00
if strings . Contains ( strings . ToLower ( keyValues . Key . Name ) , strings . ToLower ( keyword ) ) {
ret = append ( ret , keyValues . Key )
}
}
}
return
}
func SearchAttributeViewRelationKey ( avID , keyword string ) ( ret [ ] * av . Key ) {
waitForSyncingStorages ( )
ret = [ ] * av . Key { }
attrView , err := av . ParseAttributeView ( avID )
if nil != err {
logging . LogErrorf ( "parse attribute view [%s] failed: %s" , avID , err )
return
}
for _ , keyValues := range attrView . KeyValues {
if av . KeyTypeRelation == keyValues . Key . Type && nil != keyValues . Key . Relation {
if strings . Contains ( strings . ToLower ( keyValues . Key . Name ) , strings . ToLower ( keyword ) ) {
ret = append ( ret , keyValues . Key )
}
}
}
return
}
2023-12-23 17:17:47 +08:00
func GetAttributeView ( avID string ) ( ret * av . AttributeView ) {
waitForSyncingStorages ( )
ret , _ = av . ParseAttributeView ( avID )
return
}
2023-12-23 12:04:54 +08:00
type SearchAttributeViewResult struct {
AvID string ` json:"avID" `
AvName string ` json:"avName" `
BlockID string ` json:"blockID" `
2023-12-23 21:02:31 +08:00
HPath string ` json:"hPath" `
2023-12-23 12:04:54 +08:00
}
2024-04-08 21:58:57 +08:00
func SearchAttributeView ( keyword string ) ( ret [ ] * SearchAttributeViewResult ) {
2023-12-23 12:04:54 +08:00
waitForSyncingStorages ( )
ret = [ ] * SearchAttributeViewResult { }
2023-12-23 20:51:10 +08:00
keyword = strings . TrimSpace ( keyword )
2024-04-08 21:58:57 +08:00
2024-04-09 17:51:39 +08:00
type result struct {
2024-04-09 21:26:41 +08:00
AvID string
AvName string
AvUpdated int64
Score float64
2024-04-09 17:51:39 +08:00
}
var avs [ ] * result
2024-04-08 21:58:57 +08:00
avDir := filepath . Join ( util . DataDir , "storage" , "av" )
entries , err := os . ReadDir ( avDir )
if nil != err {
logging . LogErrorf ( "read directory [%s] failed: %s" , avDir , err )
return
}
2024-04-09 22:26:37 +08:00
avBlockRels := av . GetBlockRels ( )
2024-04-08 21:58:57 +08:00
for _ , entry := range entries {
if entry . IsDir ( ) {
continue
}
id := strings . TrimSuffix ( entry . Name ( ) , ".json" )
if ! ast . IsNodeIDPattern ( id ) {
continue
}
2024-04-09 22:26:37 +08:00
if nil == avBlockRels [ id ] {
continue
}
2024-04-08 21:58:57 +08:00
name , _ := av . GetAttributeViewNameByPath ( filepath . Join ( avDir , entry . Name ( ) ) )
2024-04-09 21:26:41 +08:00
info , _ := entry . Info ( )
if "" != keyword {
if strings . Contains ( strings . ToLower ( name ) , strings . ToLower ( keyword ) ) {
score := smetrics . JaroWinkler ( name , keyword , 0.7 , 4 )
a := & result { AvID : id , AvName : name , Score : score }
if nil != info && ! info . ModTime ( ) . IsZero ( ) {
a . AvUpdated = info . ModTime ( ) . UnixMilli ( )
}
avs = append ( avs , a )
}
} else {
a := & result { AvID : id , AvName : name }
if nil != info && ! info . ModTime ( ) . IsZero ( ) {
a . AvUpdated = info . ModTime ( ) . UnixMilli ( )
2024-04-08 21:58:57 +08:00
}
2024-04-09 21:26:41 +08:00
avs = append ( avs , a )
2024-04-08 21:58:57 +08:00
}
2024-04-09 17:51:39 +08:00
}
2024-04-09 21:26:41 +08:00
if "" == keyword {
sort . Slice ( avs , func ( i , j int ) bool { return avs [ i ] . AvUpdated > avs [ j ] . AvUpdated } )
} else {
2024-04-09 22:26:37 +08:00
sort . SliceStable ( avs , func ( i , j int ) bool {
if avs [ i ] . Score == avs [ j ] . Score {
return avs [ i ] . AvUpdated > avs [ j ] . AvUpdated
}
return avs [ i ] . Score > avs [ j ] . Score
} )
2024-04-09 21:26:41 +08:00
}
2024-04-09 22:26:37 +08:00
if 12 <= len ( avs ) {
avs = avs [ : 12 ]
2024-04-09 21:26:41 +08:00
}
2024-04-08 21:58:57 +08:00
var avIDs [ ] string
2024-04-09 21:26:41 +08:00
for _ , a := range avs {
avIDs = append ( avIDs , a . AvID )
2024-04-08 21:58:57 +08:00
}
2024-04-09 21:26:41 +08:00
2024-04-09 22:26:37 +08:00
avBlocks := treenode . BatchGetMirrorAttrViewBlocks ( avIDs )
var blockIDs [ ] string
for _ , avBlock := range avBlocks {
blockIDs = append ( blockIDs , avBlock . BlockIDs ... )
}
blockIDs = gulu . Str . RemoveDuplicatedElem ( blockIDs )
2023-12-23 12:04:54 +08:00
trees := map [ string ] * parse . Tree { }
2024-04-08 21:58:57 +08:00
for _ , blockID := range blockIDs {
bt := treenode . GetBlockTree ( blockID )
if nil == bt {
continue
}
tree := trees [ bt . RootID ]
2023-12-23 12:04:54 +08:00
if nil == tree {
2024-04-08 21:58:57 +08:00
tree , _ = LoadTreeByBlockID ( blockID )
2023-12-23 12:04:54 +08:00
if nil != tree {
2024-04-08 21:58:57 +08:00
trees [ bt . RootID ] = tree
2023-12-23 12:04:54 +08:00
}
}
if nil == tree {
continue
}
2024-04-08 21:58:57 +08:00
node := treenode . GetNodeInTree ( tree , blockID )
2023-12-23 12:04:54 +08:00
if nil == node {
continue
}
if "" == node . AttributeViewID {
continue
}
avID := node . AttributeViewID
2024-04-09 17:51:39 +08:00
var existAv * result
for _ , av := range avs {
if av . AvID == avID {
existAv = av
break
}
}
if nil == existAv {
2023-12-23 12:04:54 +08:00
continue
}
exist := false
for _ , result := range ret {
if result . AvID == avID {
exist = true
break
}
}
2023-12-23 21:02:31 +08:00
var hPath string
baseBlock := treenode . GetBlockTreeRootByPath ( node . Box , node . Path )
if nil != baseBlock {
hPath = baseBlock . HPath
}
2023-12-23 21:05:52 +08:00
box := Conf . Box ( node . Box )
if nil != box {
hPath = box . Name + hPath
}
2023-12-23 21:02:31 +08:00
2023-12-23 12:04:54 +08:00
if ! exist {
ret = append ( ret , & SearchAttributeViewResult {
AvID : avID ,
2024-04-09 17:51:39 +08:00
AvName : existAv . AvName ,
2024-04-08 21:58:57 +08:00
BlockID : blockID ,
2023-12-23 21:02:31 +08:00
HPath : hPath ,
2023-12-23 12:04:54 +08:00
} )
}
}
return
}
2023-07-30 20:35:03 +08:00
type BlockAttributeViewKeys struct {
AvID string ` json:"avID" `
AvName string ` json:"avName" `
2023-11-02 16:24:50 +08:00
BlockIDs [ ] string ` json:"blockIDs" `
2023-07-30 20:35:03 +08:00
KeyValues [ ] * av . KeyValues ` json:"keyValues" `
}
func GetBlockAttributeViewKeys ( blockID string ) ( ret [ ] * BlockAttributeViewKeys ) {
waitForSyncingStorages ( )
ret = [ ] * BlockAttributeViewKeys { }
2023-11-22 16:48:20 +08:00
attrs := GetBlockAttrsWithoutWaitWriting ( blockID )
2023-10-05 12:02:17 +08:00
avs := attrs [ av . NodeAttrNameAvs ]
2023-07-30 20:35:03 +08:00
if "" == avs {
return
}
avIDs := strings . Split ( avs , "," )
for _ , avID := range avIDs {
attrView , err := av . ParseAttributeView ( avID )
if nil != err {
logging . LogErrorf ( "parse attribute view [%s] failed: %s" , avID , err )
2024-04-08 17:31:10 +08:00
unbindBlockAv ( nil , avID , blockID )
2023-07-30 20:35:03 +08:00
return
}
if 1 > len ( attrView . Views ) {
err = av . ErrViewNotFound
2024-04-08 17:31:10 +08:00
unbindBlockAv ( nil , avID , blockID )
2023-07-30 20:35:03 +08:00
return
}
2024-04-03 21:17:54 +08:00
if ! attrView . ExistBlock ( blockID ) {
// 比如剪切后粘贴,块 ID 会变,但是属性还在块上,这里做一次数据订正
// Auto verify the database name when clicking the block superscript icon https://github.com/siyuan-note/siyuan/issues/10861
unbindBlockAv ( nil , avID , blockID )
return
}
2023-07-30 20:35:03 +08:00
var keyValues [ ] * av . KeyValues
for _ , kv := range attrView . KeyValues {
kValues := & av . KeyValues { Key : kv . Key }
for _ , v := range kv . Values {
if v . BlockID == blockID {
kValues . Values = append ( kValues . Values , v )
}
}
2023-10-09 12:14:11 +08:00
switch kValues . Key . Type {
2023-12-30 23:34:53 +08:00
case av . KeyTypeRollup :
2024-01-01 15:14:52 +08:00
kValues . Values = append ( kValues . Values , & av . Value { ID : ast . NewNodeID ( ) , KeyID : kValues . Key . ID , BlockID : blockID , Type : av . KeyTypeRollup , Rollup : & av . ValueRollup { Contents : [ ] * av . Value { } } } )
2023-10-09 12:14:11 +08:00
case av . KeyTypeTemplate :
2023-10-03 11:46:25 +08:00
kValues . Values = append ( kValues . Values , & av . Value { ID : ast . NewNodeID ( ) , KeyID : kValues . Key . ID , BlockID : blockID , Type : av . KeyTypeTemplate , Template : & av . ValueTemplate { Content : "" } } )
2023-10-09 12:14:11 +08:00
case av . KeyTypeCreated :
kValues . Values = append ( kValues . Values , & av . Value { ID : ast . NewNodeID ( ) , KeyID : kValues . Key . ID , BlockID : blockID , Type : av . KeyTypeCreated } )
case av . KeyTypeUpdated :
kValues . Values = append ( kValues . Values , & av . Value { ID : ast . NewNodeID ( ) , KeyID : kValues . Key . ID , BlockID : blockID , Type : av . KeyTypeUpdated } )
2023-10-01 18:37:39 +08:00
}
2023-07-30 20:35:03 +08:00
if 0 < len ( kValues . Values ) {
keyValues = append ( keyValues , kValues )
2024-01-01 21:48:23 +08:00
} else {
// 如果没有值,那么就补一个默认值
kValues . Values = append ( kValues . Values , treenode . GetAttributeViewDefaultValue ( ast . NewNodeID ( ) , kv . Key . ID , blockID , kv . Key . Type ) )
keyValues = append ( keyValues , kValues )
2023-07-30 20:35:03 +08:00
}
}
2023-12-23 20:51:10 +08:00
// 渲染自动生成的列值,比如模板列、关联列、汇总列、创建时间列和更新时间列
2023-12-24 22:27:38 +08:00
// 先处理关联列、汇总列、创建时间列和更新时间列
2023-10-03 11:46:25 +08:00
for _ , kv := range keyValues {
2023-10-09 12:14:11 +08:00
switch kv . Key . Type {
2023-12-24 22:27:38 +08:00
case av . KeyTypeRollup :
if nil == kv . Key . Rollup {
break
}
relKey , _ := attrView . GetKey ( kv . Key . Rollup . RelationKeyID )
if nil == relKey {
break
}
relVal := attrView . GetValue ( kv . Key . Rollup . RelationKeyID , kv . Values [ 0 ] . BlockID )
if nil != relVal && nil != relVal . Relation {
2023-12-30 23:34:53 +08:00
destAv , _ := av . ParseAttributeView ( relKey . Relation . AvID )
2024-01-01 16:27:01 +08:00
destKey , _ := destAv . GetKey ( kv . Key . Rollup . KeyID )
if nil != destAv && nil != destKey {
2023-12-30 23:34:53 +08:00
for _ , bID := range relVal . Relation . BlockIDs {
destVal := destAv . GetValue ( kv . Key . Rollup . KeyID , bID )
2024-01-01 14:32:32 +08:00
if nil == destVal {
2024-04-03 09:37:42 +08:00
if destAv . ExistBlock ( bID ) { // 数据库中存在行但是列值不存在是数据未初始化,这里补一个默认值
destVal = treenode . GetAttributeViewDefaultValue ( ast . NewNodeID ( ) , kv . Key . Rollup . KeyID , bID , destKey . Type )
}
if nil == destVal {
continue
}
2023-12-30 23:34:53 +08:00
}
2024-01-01 16:08:46 +08:00
if av . KeyTypeNumber == destKey . Type {
destVal . Number . Format = destKey . NumberFormat
2024-01-01 14:32:32 +08:00
destVal . Number . FormatNumber ( )
}
2024-01-01 15:14:52 +08:00
kv . Values [ 0 ] . Rollup . Contents = append ( kv . Values [ 0 ] . Rollup . Contents , destVal . Clone ( ) )
2023-12-30 23:34:53 +08:00
}
2024-01-01 16:27:01 +08:00
kv . Values [ 0 ] . Rollup . RenderContents ( kv . Key . Rollup . Calc , destKey )
2023-12-23 21:47:01 +08:00
}
}
2023-12-24 22:27:38 +08:00
case av . KeyTypeRelation :
if nil == kv . Key . Relation {
break
}
destAv , _ := av . ParseAttributeView ( kv . Key . Relation . AvID )
if nil == destAv {
break
}
2024-03-07 16:11:58 +08:00
blocks := map [ string ] * av . Value { }
2023-12-24 22:27:38 +08:00
for _ , blockValue := range destAv . GetBlockKeyValues ( ) . Values {
2024-03-07 16:11:58 +08:00
blocks [ blockValue . BlockID ] = blockValue
2023-12-24 22:27:38 +08:00
}
2024-03-20 22:30:21 +08:00
kv . Values [ 0 ] . Relation . Contents = nil // 先清空 https://github.com/siyuan-note/siyuan/issues/10670
2023-12-24 22:27:38 +08:00
for _ , bID := range kv . Values [ 0 ] . Relation . BlockIDs {
kv . Values [ 0 ] . Relation . Contents = append ( kv . Values [ 0 ] . Relation . Contents , blocks [ bID ] )
}
2023-10-09 12:14:11 +08:00
case av . KeyTypeCreated :
createdStr := blockID [ : len ( "20060102150405" ) ]
2023-10-09 16:10:15 +08:00
created , parseErr := time . ParseInLocation ( "20060102150405" , createdStr , time . Local )
2023-10-09 12:14:11 +08:00
if nil == parseErr {
kv . Values [ 0 ] . Created = av . NewFormattedValueCreated ( created . UnixMilli ( ) , 0 , av . CreatedFormatNone )
2023-10-10 20:05:48 +08:00
kv . Values [ 0 ] . Created . IsNotEmpty = true
2023-10-09 12:14:11 +08:00
} else {
logging . LogWarnf ( "parse created [%s] failed: %s" , createdStr , parseErr )
kv . Values [ 0 ] . Created = av . NewFormattedValueCreated ( time . Now ( ) . UnixMilli ( ) , 0 , av . CreatedFormatNone )
}
case av . KeyTypeUpdated :
2023-11-22 16:48:20 +08:00
ial := GetBlockAttrsWithoutWaitWriting ( blockID )
2023-10-09 12:14:11 +08:00
updatedStr := ial [ "updated" ]
2023-10-09 16:10:15 +08:00
updated , parseErr := time . ParseInLocation ( "20060102150405" , updatedStr , time . Local )
2023-10-09 12:14:11 +08:00
if nil == parseErr {
kv . Values [ 0 ] . Updated = av . NewFormattedValueUpdated ( updated . UnixMilli ( ) , 0 , av . UpdatedFormatNone )
2023-10-10 20:05:48 +08:00
kv . Values [ 0 ] . Updated . IsNotEmpty = true
2023-10-09 12:14:11 +08:00
} else {
logging . LogWarnf ( "parse updated [%s] failed: %s" , updatedStr , parseErr )
kv . Values [ 0 ] . Updated = av . NewFormattedValueUpdated ( time . Now ( ) . UnixMilli ( ) , 0 , av . UpdatedFormatNone )
}
2023-10-03 11:46:25 +08:00
}
}
2024-03-09 10:58:14 +08:00
2023-10-10 21:55:43 +08:00
// 再处理模板列
2024-03-09 10:58:14 +08:00
// 获取闪卡信息
// TODO 目前看来使用场景不多,暂时不实现了 https://github.com/siyuan-note/siyuan/issues/10502#issuecomment-1986703280
var flashcard * Flashcard
//deck := Decks[builtinDeckID]
//if nil != deck {
// blockIDs := []string{blockID}
// cards := deck.GetCardsByBlockIDs(blockIDs)
// now := time.Now()
// if 0 < len(cards) {
// flashcard = newFlashcard(cards[0], builtinDeckID, now)
// }
//}
// 渲染模板
2023-10-10 21:55:43 +08:00
for _ , kv := range keyValues {
switch kv . Key . Type {
case av . KeyTypeTemplate :
if 0 < len ( kv . Values ) {
2023-10-12 19:55:57 +08:00
ial := map [ string ] string { }
block := getRowBlockValue ( keyValues )
2024-01-01 23:34:15 +08:00
if nil != block && ! block . IsDetached {
2024-03-31 23:58:46 +08:00
ial = GetBlockAttrsWithoutWaitWriting ( block . BlockID )
2023-10-12 19:55:57 +08:00
}
2024-03-09 10:58:14 +08:00
kv . Values [ 0 ] . Template . Content = renderTemplateCol ( ial , flashcard , keyValues , kv . Key . Template )
2023-10-10 21:55:43 +08:00
}
}
}
2023-10-03 11:46:25 +08:00
2023-09-30 11:57:37 +08:00
// Attribute Panel - Database sort attributes by view column order https://github.com/siyuan-note/siyuan/issues/9319
2024-03-04 15:57:35 +08:00
viewID := attrs [ av . NodeAttrView ]
view , _ := attrView . GetCurrentView ( viewID )
2023-09-30 11:57:37 +08:00
if nil != view {
sorts := map [ string ] int { }
for i , col := range view . Table . Columns {
sorts [ col . ID ] = i
}
sort . Slice ( keyValues , func ( i , j int ) bool {
return sorts [ keyValues [ i ] . Key . ID ] < sorts [ keyValues [ j ] . Key . ID ]
} )
}
2024-03-10 23:27:13 +08:00
blockIDs := treenode . GetMirrorAttrViewBlockIDs ( avID )
2023-11-03 11:15:33 +08:00
if 1 > len ( blockIDs ) {
// 老数据兼容处理
avBts := treenode . GetBlockTreesByType ( "av" )
for _ , avBt := range avBts {
if nil == avBt {
continue
}
2024-03-10 23:27:13 +08:00
tree , _ := LoadTreeByBlockID ( avBt . ID )
2023-11-03 11:15:33 +08:00
if nil == tree {
continue
}
node := treenode . GetNodeInTree ( tree , avBt . ID )
if nil == node {
continue
}
if avID == node . AttributeViewID {
blockIDs = append ( blockIDs , avBt . ID )
}
}
if 1 > len ( blockIDs ) {
2024-03-10 23:27:13 +08:00
tree , _ := LoadTreeByBlockID ( blockID )
2024-02-16 16:02:22 +08:00
if nil != tree {
node := treenode . GetNodeInTree ( tree , blockID )
if nil != node {
if removeErr := removeNodeAvID ( node , avID , nil , tree ) ; nil != removeErr {
logging . LogErrorf ( "remove node avID [%s] failed: %s" , avID , removeErr )
}
}
}
2023-11-03 11:15:33 +08:00
continue
}
blockIDs = gulu . Str . RemoveDuplicatedElem ( blockIDs )
for _ , blockID := range blockIDs {
av . UpsertBlockRel ( avID , blockID )
}
}
2023-11-02 16:24:50 +08:00
2023-07-30 20:35:03 +08:00
ret = append ( ret , & BlockAttributeViewKeys {
AvID : avID ,
AvName : attrView . Name ,
2023-11-02 16:24:50 +08:00
BlockIDs : blockIDs ,
2023-07-30 20:35:03 +08:00
KeyValues : keyValues ,
} )
}
return
}
2023-11-23 22:10:01 +08:00
func RenderRepoSnapshotAttributeView ( indexID , avID string ) ( viewable av . Viewable , attrView * av . AttributeView , err error ) {
repo , err := newRepository ( )
if nil != err {
return
}
index , err := repo . GetIndex ( indexID )
if nil != err {
return
}
files , err := repo . GetFiles ( index )
if nil != err {
return
}
var avFile * entity . File
for _ , f := range files {
if "/storage/av/" + avID + ".json" == f . Path {
avFile = f
break
}
}
if nil == avFile {
2023-11-24 00:07:58 +08:00
attrView = av . NewAttributeView ( avID )
} else {
data , readErr := repo . OpenFile ( avFile )
if nil != readErr {
logging . LogErrorf ( "read attribute view [%s] failed: %s" , avID , readErr )
return
}
2023-11-23 22:10:01 +08:00
2023-11-24 00:07:58 +08:00
attrView = & av . AttributeView { }
if err = gulu . JSON . UnmarshalJSON ( data , attrView ) ; nil != err {
logging . LogErrorf ( "unmarshal attribute view [%s] failed: %s" , avID , err )
return
}
2023-11-23 22:10:01 +08:00
}
2024-03-09 17:31:06 +08:00
viewable , err = renderAttributeView ( attrView , "" , "" , 1 , - 1 )
2023-11-23 22:10:01 +08:00
return
}
2023-11-23 11:22:35 +08:00
func RenderHistoryAttributeView ( avID , created string ) ( viewable av . Viewable , attrView * av . AttributeView , err error ) {
createdUnix , parseErr := strconv . ParseInt ( created , 10 , 64 )
if nil != parseErr {
logging . LogErrorf ( "parse created [%s] failed: %s" , created , parseErr )
return
}
dirPrefix := time . Unix ( createdUnix , 0 ) . Format ( "2006-01-02-150405" )
globPath := filepath . Join ( util . HistoryDir , dirPrefix + "*" )
matches , err := filepath . Glob ( globPath )
if nil != err {
logging . LogErrorf ( "glob [%s] failed: %s" , globPath , err )
return
}
if 1 > len ( matches ) {
return
}
historyDir := matches [ 0 ]
avJSONPath := filepath . Join ( historyDir , "storage" , "av" , avID + ".json" )
if ! gulu . File . IsExist ( avJSONPath ) {
2023-11-24 00:07:58 +08:00
avJSONPath = filepath . Join ( util . DataDir , "storage" , "av" , avID + ".json" )
2023-11-23 11:22:35 +08:00
}
2023-11-24 00:07:58 +08:00
if ! gulu . File . IsExist ( avJSONPath ) {
attrView = av . NewAttributeView ( avID )
} else {
data , readErr := os . ReadFile ( avJSONPath )
if nil != readErr {
logging . LogErrorf ( "read attribute view [%s] failed: %s" , avID , readErr )
return
}
2023-11-23 11:22:35 +08:00
2023-11-24 00:07:58 +08:00
attrView = & av . AttributeView { }
if err = gulu . JSON . UnmarshalJSON ( data , attrView ) ; nil != err {
logging . LogErrorf ( "unmarshal attribute view [%s] failed: %s" , avID , err )
return
}
2023-11-23 11:22:35 +08:00
}
2024-03-09 17:31:06 +08:00
viewable , err = renderAttributeView ( attrView , "" , "" , 1 , - 1 )
2023-11-23 11:22:35 +08:00
return
}
2024-03-08 23:49:27 +08:00
func RenderAttributeView ( avID , viewID , query string , page , pageSize int ) ( viewable av . Viewable , attrView * av . AttributeView , err error ) {
2023-04-02 11:42:04 +08:00
waitForSyncingStorages ( )
2023-11-06 22:13:04 +08:00
if avJSONPath := av . GetAttributeViewDataPath ( avID ) ; ! filelock . IsExist ( avJSONPath ) {
2023-09-26 09:38:50 +08:00
attrView = av . NewAttributeView ( avID )
2023-07-31 11:20:58 +08:00
if err = av . SaveAttributeView ( attrView ) ; nil != err {
logging . LogErrorf ( "save attribute view [%s] failed: %s" , avID , err )
return
}
}
2023-07-11 19:45:27 +08:00
attrView , err = av . ParseAttributeView ( avID )
2023-03-03 15:01:42 +08:00
if nil != err {
logging . LogErrorf ( "parse attribute view [%s] failed: %s" , avID , err )
return
}
2023-07-03 18:45:41 +08:00
2024-03-09 17:31:06 +08:00
viewable , err = renderAttributeView ( attrView , viewID , query , page , pageSize )
2023-11-23 11:22:35 +08:00
return
}
2024-03-09 17:31:06 +08:00
func renderAttributeView ( attrView * av . AttributeView , viewID , query string , page , pageSize int ) ( viewable av . Viewable , err error ) {
2023-07-11 19:45:27 +08:00
if 1 > len ( attrView . Views ) {
2024-03-24 21:26:01 +08:00
view , _ , _ := av . NewTableViewWithBlockKey ( ast . NewNodeID ( ) )
2023-10-23 11:38:27 +08:00
attrView . Views = append ( attrView . Views , view )
attrView . ViewID = view . ID
if err = av . SaveAttributeView ( attrView ) ; nil != err {
2023-11-23 11:22:35 +08:00
logging . LogErrorf ( "save attribute view [%s] failed: %s" , attrView . ID , err )
2023-10-23 11:38:27 +08:00
return
}
2023-07-11 19:45:27 +08:00
}
var view * av . View
2023-11-30 20:04:51 +08:00
if "" != viewID {
2024-03-04 15:57:35 +08:00
view , _ = attrView . GetCurrentView ( viewID )
2024-03-04 11:51:03 +08:00
if nil != view && view . ID != attrView . ViewID {
attrView . ViewID = view . ID
2023-11-30 20:04:51 +08:00
if err = av . SaveAttributeView ( attrView ) ; nil != err {
logging . LogErrorf ( "save attribute view [%s] failed: %s" , attrView . ID , err )
return
2023-07-11 19:45:27 +08:00
}
}
} else {
2024-03-04 15:57:35 +08:00
view = attrView . GetView ( attrView . ViewID )
2023-11-30 20:04:51 +08:00
}
if nil == view {
2023-07-11 19:45:27 +08:00
view = attrView . Views [ 0 ]
}
2023-10-16 23:57:09 +08:00
// 做一些数据兼容和订正处理,保存的时候也会做 av.SaveAttributeView()
2023-10-12 19:38:37 +08:00
currentTimeMillis := util . CurrentTimeMillis ( )
2023-10-10 21:55:43 +08:00
for _ , kv := range attrView . KeyValues {
switch kv . Key . Type {
case av . KeyTypeBlock : // 补全 block 的创建时间和更新时间
for _ , v := range kv . Values {
if 0 == v . Block . Created {
2023-10-13 13:21:53 +08:00
if "" == v . Block . ID {
2023-10-13 14:03:41 +08:00
v . Block . ID = v . BlockID
if "" == v . Block . ID {
v . Block . ID = ast . NewNodeID ( )
v . BlockID = v . Block . ID
}
2023-10-13 13:21:53 +08:00
}
2023-10-10 21:55:43 +08:00
createdStr := v . Block . ID [ : len ( "20060102150405" ) ]
created , parseErr := time . ParseInLocation ( "20060102150405" , createdStr , time . Local )
if nil == parseErr {
v . Block . Created = created . UnixMilli ( )
} else {
2023-10-12 19:38:37 +08:00
v . Block . Created = currentTimeMillis
2023-10-10 21:55:43 +08:00
}
}
if 0 == v . Block . Updated {
2024-03-03 16:12:44 +08:00
v . Block . Updated = v . Block . Created
2023-10-10 21:55:43 +08:00
}
}
}
2024-03-03 16:12:44 +08:00
for _ , v := range kv . Values {
2024-03-07 09:37:49 +08:00
// 校验日期 IsNotEmpty
if av . KeyTypeDate == kv . Key . Type {
2024-03-07 10:05:55 +08:00
if nil != v . Date && 0 != v . Date . Content && ! v . Date . IsNotEmpty {
2024-03-07 09:37:49 +08:00
v . Date . IsNotEmpty = true
}
}
2024-03-07 10:05:55 +08:00
// 校验数字 IsNotEmpty
if av . KeyTypeNumber == kv . Key . Type {
if nil != v . Number && 0 != v . Number . Content && ! v . Number . IsNotEmpty {
v . Number . IsNotEmpty = true
}
}
2024-03-07 09:37:49 +08:00
// 补全值的创建时间和更新时间
2024-03-03 16:21:31 +08:00
if "" == v . ID {
v . ID = ast . NewNodeID ( )
}
2024-03-07 09:37:49 +08:00
if 0 == v . CreatedAt {
createdStr := v . ID [ : len ( "20060102150405" ) ]
created , parseErr := time . ParseInLocation ( "20060102150405" , createdStr , time . Local )
if nil == parseErr {
v . CreatedAt = created . UnixMilli ( )
} else {
v . CreatedAt = currentTimeMillis
}
2024-03-03 16:12:44 +08:00
}
if 0 == v . UpdatedAt {
2024-04-09 23:37:00 +08:00
v . UpdatedAt = v . CreatedAt
2024-03-03 16:12:44 +08:00
}
}
2023-10-10 21:55:43 +08:00
}
2024-03-06 23:12:01 +08:00
// 补全过滤器 Value
if nil != view . Table {
for _ , f := range view . Table . Filters {
if nil != f . Value {
continue
}
if k , _ := attrView . GetKey ( f . Column ) ; nil != k {
f . Value = & av . Value { Type : k . Type }
}
}
}
2023-07-12 21:39:55 +08:00
switch view . LayoutType {
2023-07-12 10:35:17 +08:00
case av . LayoutTypeTable :
2023-10-09 17:17:40 +08:00
// 列删除以后需要删除设置的过滤和排序
tmpFilters := [ ] * av . ViewFilter { }
for _ , f := range view . Table . Filters {
if k , _ := attrView . GetKey ( f . Column ) ; nil != k {
tmpFilters = append ( tmpFilters , f )
}
}
view . Table . Filters = tmpFilters
tmpSorts := [ ] * av . ViewSort { }
for _ , s := range view . Table . Sorts {
if k , _ := attrView . GetKey ( s . Column ) ; nil != k {
tmpSorts = append ( tmpSorts , s )
}
}
view . Table . Sorts = tmpSorts
2024-03-09 17:31:06 +08:00
viewable , err = renderAttributeViewTable ( attrView , view , query )
2023-07-11 19:45:27 +08:00
}
2023-12-30 23:01:21 +08:00
viewable . FilterRows ( attrView )
2024-03-21 10:03:08 +08:00
viewable . SortRows ( attrView )
2023-07-11 19:45:27 +08:00
viewable . CalcCols ( )
2023-12-08 22:17:50 +08:00
// 分页
switch viewable . GetType ( ) {
case av . LayoutTypeTable :
table := viewable . ( * av . Table )
table . RowCount = len ( table . Rows )
if 1 > view . Table . PageSize {
view . Table . PageSize = 50
}
table . PageSize = view . Table . PageSize
if 1 > pageSize {
pageSize = table . PageSize
}
start := ( page - 1 ) * pageSize
end := start + pageSize
if len ( table . Rows ) < end {
end = len ( table . Rows )
}
table . Rows = table . Rows [ start : end ]
}
2023-07-11 19:45:27 +08:00
return
}
2024-03-09 10:58:14 +08:00
func renderTemplateCol ( ial map [ string ] string , flashcard * Flashcard , rowValues [ ] * av . KeyValues , tplContent string ) string {
2023-10-13 10:44:29 +08:00
if "" == ial [ "id" ] {
block := getRowBlockValue ( rowValues )
2024-01-01 23:34:15 +08:00
if nil != block && nil != block . Block {
ial [ "id" ] = block . Block . ID
}
2023-10-13 10:44:29 +08:00
}
if "" == ial [ "updated" ] {
block := getRowBlockValue ( rowValues )
2024-01-01 23:35:20 +08:00
if nil != block && nil != block . Block {
ial [ "updated" ] = time . UnixMilli ( block . Block . Updated ) . Format ( "20060102150405" )
}
2023-10-13 10:44:29 +08:00
}
goTpl := template . New ( "" ) . Delims ( ".action{" , "}" )
2024-01-04 22:31:42 +08:00
tplFuncMap := util . BuiltInTemplateFuncs ( )
SQLTemplateFuncs ( & tplFuncMap )
goTpl = goTpl . Funcs ( tplFuncMap )
2023-12-18 12:02:55 +08:00
tpl , tplErr := goTpl . Parse ( tplContent )
2023-10-13 10:44:29 +08:00
if nil != tplErr {
logging . LogWarnf ( "parse template [%s] failed: %s" , tplContent , tplErr )
return ""
}
buf := & bytes . Buffer { }
dataModel := map [ string ] interface { } { } // 复制一份 IAL 以避免修改原始数据
for k , v := range ial {
dataModel [ k ] = v
// Database template column supports `created` and `updated` built-in variables https://github.com/siyuan-note/siyuan/issues/9364
createdStr := ial [ "id" ]
if "" != createdStr {
createdStr = createdStr [ : len ( "20060102150405" ) ]
}
created , parseErr := time . ParseInLocation ( "20060102150405" , createdStr , time . Local )
if nil == parseErr {
dataModel [ "created" ] = created
} else {
logging . LogWarnf ( "parse created [%s] failed: %s" , createdStr , parseErr )
dataModel [ "created" ] = time . Now ( )
}
updatedStr := ial [ "updated" ]
updated , parseErr := time . ParseInLocation ( "20060102150405" , updatedStr , time . Local )
if nil == parseErr {
dataModel [ "updated" ] = updated
} else {
dataModel [ "updated" ] = time . Now ( )
}
}
2024-03-09 10:58:14 +08:00
if nil != flashcard {
dataModel [ "flashcard" ] = flashcard
}
2023-10-13 10:44:29 +08:00
for _ , rowValue := range rowValues {
if 0 < len ( rowValue . Values ) {
v := rowValue . Values [ 0 ]
if av . KeyTypeNumber == v . Type {
2024-04-02 20:24:10 +08:00
if nil != v . Number && v . Number . IsNotEmpty {
dataModel [ rowValue . Key . Name ] = v . Number . Content
}
2023-12-15 09:53:34 +08:00
} else if av . KeyTypeDate == v . Type {
2024-04-02 20:24:10 +08:00
if nil != v . Date && v . Date . IsNotEmpty {
dataModel [ rowValue . Key . Name ] = time . UnixMilli ( v . Date . Content )
}
2024-03-31 23:32:00 +08:00
} else if av . KeyTypeRollup == v . Type {
2024-04-14 13:02:06 +08:00
if 0 < len ( v . Rollup . Contents ) {
var numbers [ ] float64
var contents [ ] string
for _ , content := range v . Rollup . Contents {
if av . KeyTypeNumber == content . Type {
numbers = append ( numbers , content . Number . Content )
} else {
contents = append ( contents , content . String ( true ) )
}
}
if 0 < len ( numbers ) {
dataModel [ rowValue . Key . Name ] = numbers
} else {
dataModel [ rowValue . Key . Name ] = contents
}
}
} else if av . KeyTypeRelation == v . Type {
if 0 < len ( v . Relation . Contents ) {
var contents [ ] string
for _ , content := range v . Relation . Contents {
contents = append ( contents , content . String ( true ) )
}
dataModel [ rowValue . Key . Name ] = contents
2024-03-31 23:32:00 +08:00
}
2023-10-13 10:44:29 +08:00
} else {
2024-04-14 12:34:06 +08:00
dataModel [ rowValue . Key . Name ] = v . String ( true )
2023-10-13 10:44:29 +08:00
}
}
}
2024-03-09 10:58:14 +08:00
2023-10-13 10:44:29 +08:00
if err := tpl . Execute ( buf , dataModel ) ; nil != err {
logging . LogWarnf ( "execute template [%s] failed: %s" , tplContent , err )
}
return buf . String ( )
}
2024-03-09 17:31:06 +08:00
func renderAttributeViewTable ( attrView * av . AttributeView , view * av . View , query string ) ( ret * av . Table , err error ) {
2023-07-12 10:35:17 +08:00
ret = & av . Table {
2024-03-01 22:40:56 +08:00
ID : view . ID ,
Icon : view . Icon ,
Name : view . Name ,
HideAttrViewName : view . HideAttrViewName ,
Columns : [ ] * av . TableColumn { } ,
Rows : [ ] * av . TableRow { } ,
Filters : view . Table . Filters ,
Sorts : view . Table . Sorts ,
2023-07-12 10:35:17 +08:00
}
2023-08-03 22:47:37 +08:00
// 组装列
2023-07-12 19:10:05 +08:00
for _ , col := range view . Table . Columns {
key , getErr := attrView . GetKey ( col . ID )
if nil != getErr {
err = getErr
return
}
ret . Columns = append ( ret . Columns , & av . TableColumn {
2023-08-03 23:21:11 +08:00
ID : key . ID ,
Name : key . Name ,
Type : key . Type ,
Icon : key . Icon ,
Options : key . Options ,
NumberFormat : key . NumberFormat ,
2023-10-01 18:02:31 +08:00
Template : key . Template ,
2023-12-23 17:45:46 +08:00
Relation : key . Relation ,
Rollup : key . Rollup ,
2024-04-05 21:46:41 +08:00
Date : key . Date ,
2023-08-03 23:21:11 +08:00
Wrap : col . Wrap ,
Hidden : col . Hidden ,
Width : col . Width ,
2023-11-10 10:22:19 +08:00
Pin : col . Pin ,
2023-08-03 23:21:11 +08:00
Calc : col . Calc ,
2023-07-12 19:10:05 +08:00
} )
}
2023-08-03 22:47:37 +08:00
// 生成行
2023-10-03 11:46:25 +08:00
rows := map [ string ] [ ] * av . KeyValues { }
2023-07-12 19:10:05 +08:00
for _ , keyValues := range attrView . KeyValues {
for _ , val := range keyValues . Values {
2023-10-03 11:46:25 +08:00
values := rows [ val . BlockID ]
if nil == values {
2023-10-03 13:01:11 +08:00
values = [ ] * av . KeyValues { { Key : keyValues . Key , Values : [ ] * av . Value { val } } }
2023-10-03 11:46:25 +08:00
} else {
values = append ( values , & av . KeyValues { Key : keyValues . Key , Values : [ ] * av . Value { val } } )
}
rows [ val . BlockID ] = values
2023-07-12 19:10:05 +08:00
}
2023-12-31 10:58:40 +08:00
// 数据订正,补全关联
if av . KeyTypeRelation == keyValues . Key . Type && nil != keyValues . Key . Relation {
av . UpsertAvBackRel ( attrView . ID , keyValues . Key . Relation . AvID )
if keyValues . Key . Relation . IsTwoWay {
av . UpsertAvBackRel ( keyValues . Key . Relation . AvID , attrView . ID )
}
}
2023-07-12 19:10:05 +08:00
}
2023-08-03 22:47:37 +08:00
// 过滤掉不存在的行
2023-09-27 23:14:42 +08:00
var notFound [ ] string
2023-10-03 11:46:25 +08:00
for blockID , keyValues := range rows {
blockValue := getRowBlockValue ( keyValues )
2023-10-03 08:52:33 +08:00
if nil == blockValue {
notFound = append ( notFound , blockID )
continue
}
if blockValue . IsDetached {
continue
}
if nil != blockValue . Block && "" == blockValue . Block . ID {
notFound = append ( notFound , blockID )
2023-09-27 23:14:42 +08:00
continue
}
2024-03-28 21:27:31 +08:00
if nil == treenode . GetBlockTree ( blockID ) {
2023-07-15 23:46:52 +08:00
notFound = append ( notFound , blockID )
}
}
for _ , blockID := range notFound {
delete ( rows , blockID )
}
2023-08-03 22:47:37 +08:00
// 生成行单元格
2023-07-13 00:08:10 +08:00
for rowID , row := range rows {
2023-07-12 19:10:05 +08:00
var tableRow av . TableRow
for _ , col := range ret . Columns {
var tableCell * av . TableCell
2023-10-03 11:46:25 +08:00
for _ , keyValues := range row {
if keyValues . Key . ID == col . ID {
2023-07-12 19:10:05 +08:00
tableCell = & av . TableCell {
2023-10-03 11:46:25 +08:00
ID : keyValues . Values [ 0 ] . ID ,
Value : keyValues . Values [ 0 ] ,
2023-10-03 16:07:07 +08:00
ValueType : col . Type ,
2023-07-12 19:10:05 +08:00
}
break
}
}
if nil == tableCell {
tableCell = & av . TableCell {
ID : ast . NewNodeID ( ) ,
ValueType : col . Type ,
}
}
2023-07-13 00:08:10 +08:00
tableRow . ID = rowID
2023-08-03 22:47:37 +08:00
2023-10-08 12:32:37 +08:00
switch tableCell . ValueType {
case av . KeyTypeNumber : // 格式化数字
if nil != tableCell . Value && nil != tableCell . Value . Number && tableCell . Value . Number . IsNotEmpty {
tableCell . Value . Number . Format = col . NumberFormat
tableCell . Value . Number . FormatNumber ( )
}
case av . KeyTypeTemplate : // 渲染模板列
2023-10-03 11:46:25 +08:00
tableCell . Value = & av . Value { ID : tableCell . ID , KeyID : col . ID , BlockID : rowID , Type : av . KeyTypeTemplate , Template : & av . ValueTemplate { Content : col . Template } }
2023-10-08 12:32:37 +08:00
case av . KeyTypeCreated : // 填充创建时间列值,后面再渲染
tableCell . Value = & av . Value { ID : tableCell . ID , KeyID : col . ID , BlockID : rowID , Type : av . KeyTypeCreated }
case av . KeyTypeUpdated : // 填充更新时间列值,后面再渲染
tableCell . Value = & av . Value { ID : tableCell . ID , KeyID : col . ID , BlockID : rowID , Type : av . KeyTypeUpdated }
2023-12-30 12:08:20 +08:00
case av . KeyTypeRelation : // 清空关联列值,后面再渲染 https://ld246.com/article/1703831044435
if nil != tableCell . Value && nil != tableCell . Value . Relation {
tableCell . Value . Relation . Contents = nil
}
2023-10-01 10:58:46 +08:00
}
2023-10-27 22:28:56 +08:00
treenode . FillAttributeViewTableCellNilValue ( tableCell , rowID , col . ID )
2023-07-12 19:10:05 +08:00
tableRow . Cells = append ( tableRow . Cells , tableCell )
}
ret . Rows = append ( ret . Rows , & tableRow )
2023-07-11 22:11:15 +08:00
}
2023-07-13 09:37:52 +08:00
2024-02-26 09:18:44 +08:00
// 渲染自动生成的列值,比如关联列、汇总列、创建时间列和更新时间列
2023-10-03 11:46:25 +08:00
for _ , row := range ret . Rows {
for _ , cell := range row . Cells {
2023-10-08 12:32:37 +08:00
switch cell . ValueType {
2023-12-24 22:27:38 +08:00
case av . KeyTypeRollup : // 渲染汇总列
rollupKey , _ := attrView . GetKey ( cell . Value . KeyID )
if nil == rollupKey || nil == rollupKey . Rollup {
break
}
relKey , _ := attrView . GetKey ( rollupKey . Rollup . RelationKeyID )
if nil == relKey || nil == relKey . Relation {
break
}
relVal := attrView . GetValue ( relKey . ID , row . ID )
if nil == relVal || nil == relVal . Relation {
break
}
destAv , _ := av . ParseAttributeView ( relKey . Relation . AvID )
if nil == destAv {
break
}
2024-01-01 16:27:01 +08:00
destKey , _ := destAv . GetKey ( rollupKey . Rollup . KeyID )
if nil == destKey {
continue
}
2024-01-01 16:08:46 +08:00
2024-01-01 16:27:01 +08:00
for _ , blockID := range relVal . Relation . BlockIDs {
2023-12-29 11:26:05 +08:00
destVal := destAv . GetValue ( rollupKey . Rollup . KeyID , blockID )
if nil == destVal {
2024-04-03 09:37:42 +08:00
if destAv . ExistBlock ( blockID ) { // 数据库中存在行但是列值不存在是数据未初始化,这里补一个默认值
destVal = treenode . GetAttributeViewDefaultValue ( ast . NewNodeID ( ) , rollupKey . Rollup . KeyID , blockID , destKey . Type )
}
if nil == destVal {
continue
}
2023-12-29 11:26:05 +08:00
}
2024-01-01 16:08:46 +08:00
if av . KeyTypeNumber == destKey . Type {
destVal . Number . Format = destKey . NumberFormat
2023-12-30 20:49:57 +08:00
destVal . Number . FormatNumber ( )
}
2024-01-01 15:14:52 +08:00
cell . Value . Rollup . Contents = append ( cell . Value . Rollup . Contents , destVal . Clone ( ) )
2023-12-24 22:27:38 +08:00
}
2023-12-30 22:36:23 +08:00
2024-01-01 16:27:01 +08:00
cell . Value . Rollup . RenderContents ( rollupKey . Rollup . Calc , destKey )
2024-02-26 09:18:44 +08:00
// 将汇总列的值保存到 rows 中,后续渲染模板列的时候会用到,下同
// Database table view template columns support reading relation, rollup, created and updated columns https://github.com/siyuan-note/siyuan/issues/10442
keyValues := rows [ row . ID ]
keyValues = append ( keyValues , & av . KeyValues { Key : rollupKey , Values : [ ] * av . Value { { ID : cell . Value . ID , KeyID : rollupKey . ID , BlockID : row . ID , Type : av . KeyTypeRollup , Rollup : cell . Value . Rollup } } } )
rows [ row . ID ] = keyValues
2023-12-23 20:51:10 +08:00
case av . KeyTypeRelation : // 渲染关联列
2023-12-23 21:47:01 +08:00
relKey , _ := attrView . GetKey ( cell . Value . KeyID )
if nil != relKey && nil != relKey . Relation {
destAv , _ := av . ParseAttributeView ( relKey . Relation . AvID )
if nil != destAv {
2024-03-07 16:11:58 +08:00
blocks := map [ string ] * av . Value { }
2023-12-23 21:47:01 +08:00
for _ , blockValue := range destAv . GetBlockKeyValues ( ) . Values {
2024-03-07 16:11:58 +08:00
blocks [ blockValue . BlockID ] = blockValue
2023-12-23 21:47:01 +08:00
}
for _ , blockID := range cell . Value . Relation . BlockIDs {
2024-03-08 10:30:41 +08:00
if val := blocks [ blockID ] ; nil != val {
cell . Value . Relation . Contents = append ( cell . Value . Relation . Contents , val )
}
2023-12-23 21:47:01 +08:00
}
}
}
2024-02-26 09:18:44 +08:00
keyValues := rows [ row . ID ]
keyValues = append ( keyValues , & av . KeyValues { Key : relKey , Values : [ ] * av . Value { { ID : cell . Value . ID , KeyID : relKey . ID , BlockID : row . ID , Type : av . KeyTypeRelation , Relation : cell . Value . Relation } } } )
rows [ row . ID ] = keyValues
2023-10-08 12:32:37 +08:00
case av . KeyTypeCreated : // 渲染创建时间
createdStr := row . ID [ : len ( "20060102150405" ) ]
2023-10-09 16:10:15 +08:00
created , parseErr := time . ParseInLocation ( "20060102150405" , createdStr , time . Local )
2023-10-08 12:32:37 +08:00
if nil == parseErr {
2023-10-09 11:39:13 +08:00
cell . Value . Created = av . NewFormattedValueCreated ( created . UnixMilli ( ) , 0 , av . CreatedFormatNone )
2023-10-10 20:05:48 +08:00
cell . Value . Created . IsNotEmpty = true
2023-10-08 12:32:37 +08:00
} else {
2023-10-09 11:39:13 +08:00
cell . Value . Created = av . NewFormattedValueCreated ( time . Now ( ) . UnixMilli ( ) , 0 , av . CreatedFormatNone )
2023-10-08 12:32:37 +08:00
}
2024-02-26 09:18:44 +08:00
keyValues := rows [ row . ID ]
createdKey , _ := attrView . GetKey ( cell . Value . KeyID )
keyValues = append ( keyValues , & av . KeyValues { Key : createdKey , Values : [ ] * av . Value { { ID : cell . Value . ID , KeyID : createdKey . ID , BlockID : row . ID , Type : av . KeyTypeCreated , Created : cell . Value . Created } } } )
rows [ row . ID ] = keyValues
2023-10-08 12:32:37 +08:00
case av . KeyTypeUpdated : // 渲染更新时间
2023-10-12 17:33:23 +08:00
ial := map [ string ] string { }
block := row . GetBlockValue ( )
2023-12-18 22:29:17 +08:00
if nil != block && ! block . IsDetached {
2023-11-22 16:48:20 +08:00
ial = GetBlockAttrsWithoutWaitWriting ( row . ID )
2023-10-12 17:33:23 +08:00
}
2023-10-08 12:32:37 +08:00
updatedStr := ial [ "updated" ]
2023-12-18 22:29:17 +08:00
if "" == updatedStr && nil != block {
2023-10-10 21:55:43 +08:00
cell . Value . Updated = av . NewFormattedValueUpdated ( block . Block . Updated , 0 , av . UpdatedFormatNone )
2023-10-10 20:05:48 +08:00
cell . Value . Updated . IsNotEmpty = true
2023-10-08 12:32:37 +08:00
} else {
2023-10-10 21:55:43 +08:00
updated , parseErr := time . ParseInLocation ( "20060102150405" , updatedStr , time . Local )
if nil == parseErr {
cell . Value . Updated = av . NewFormattedValueUpdated ( updated . UnixMilli ( ) , 0 , av . UpdatedFormatNone )
cell . Value . Updated . IsNotEmpty = true
} else {
cell . Value . Updated = av . NewFormattedValueUpdated ( time . Now ( ) . UnixMilli ( ) , 0 , av . UpdatedFormatNone )
}
2023-10-08 12:32:37 +08:00
}
2024-02-26 09:18:44 +08:00
keyValues := rows [ row . ID ]
updatedKey , _ := attrView . GetKey ( cell . Value . KeyID )
keyValues = append ( keyValues , & av . KeyValues { Key : updatedKey , Values : [ ] * av . Value { { ID : cell . Value . ID , KeyID : updatedKey . ID , BlockID : row . ID , Type : av . KeyTypeUpdated , Updated : cell . Value . Updated } } } )
rows [ row . ID ] = keyValues
}
}
}
// 最后单独渲染模板列,这样模板列就可以使用汇总、关联、创建时间和更新时间列的值了
// Database table view template columns support reading relation, rollup, created and updated columns https://github.com/siyuan-note/siyuan/issues/10442
2024-03-09 10:58:14 +08:00
// 获取闪卡信息
flashcards := map [ string ] * Flashcard { }
2024-03-09 11:12:09 +08:00
//deck := Decks[builtinDeckID]
//if nil != deck {
// var blockIDs []string
// for _, row := range ret.Rows {
// blockIDs = append(blockIDs, row.ID)
// }
// cards := deck.GetCardsByBlockIDs(blockIDs)
// now := time.Now()
// for _, card := range cards {
// flashcards[card.BlockID()] = newFlashcard(card, builtinDeckID, now)
// }
//}
2024-03-09 10:58:14 +08:00
2024-02-26 09:18:44 +08:00
for _ , row := range ret . Rows {
for _ , cell := range row . Cells {
switch cell . ValueType {
case av . KeyTypeTemplate : // 渲染模板列
keyValues := rows [ row . ID ]
ial := map [ string ] string { }
block := row . GetBlockValue ( )
if nil != block && ! block . IsDetached {
ial = GetBlockAttrsWithoutWaitWriting ( row . ID )
}
2024-03-09 10:58:14 +08:00
content := renderTemplateCol ( ial , flashcards [ row . ID ] , keyValues , cell . Value . Template . Content )
2024-02-26 09:18:44 +08:00
cell . Value . Template . Content = content
2023-10-03 11:46:25 +08:00
}
}
}
2024-03-09 17:31:06 +08:00
// 根据搜索条件过滤
query = strings . TrimSpace ( query )
if "" != query {
keywords := strings . Split ( query , " " )
var hitRows [ ] * av . TableRow
for _ , row := range ret . Rows {
hit := false
for _ , cell := range row . Cells {
for _ , keyword := range keywords {
2024-04-14 12:34:06 +08:00
if strings . Contains ( strings . ToLower ( cell . Value . String ( true ) ) , strings . ToLower ( keyword ) ) {
2024-03-09 17:31:06 +08:00
hit = true
break
}
}
}
if hit {
hitRows = append ( hitRows , row )
}
}
ret . Rows = hitRows
if 1 > len ( ret . Rows ) {
ret . Rows = [ ] * av . TableRow { }
}
}
2023-08-03 22:47:37 +08:00
// 自定义排序
2023-07-13 09:37:52 +08:00
sortRowIDs := map [ string ] int { }
if 0 < len ( view . Table . RowIDs ) {
for i , rowID := range view . Table . RowIDs {
sortRowIDs [ rowID ] = i
}
}
sort . Slice ( ret . Rows , func ( i , j int ) bool {
iv := sortRowIDs [ ret . Rows [ i ] . ID ]
jv := sortRowIDs [ ret . Rows [ j ] . ID ]
if iv == jv {
return ret . Rows [ i ] . ID < ret . Rows [ j ] . ID
}
return iv < jv
} )
2023-07-11 22:11:15 +08:00
return
}
2023-10-03 11:46:25 +08:00
func getRowBlockValue ( keyValues [ ] * av . KeyValues ) ( ret * av . Value ) {
for _ , kv := range keyValues {
2023-10-03 13:01:11 +08:00
if av . KeyTypeBlock == kv . Key . Type && 0 < len ( kv . Values ) {
2023-10-03 11:46:25 +08:00
ret = kv . Values [ 0 ]
2023-10-03 08:52:33 +08:00
break
}
}
return
}
2024-04-04 11:33:51 +08:00
func ( tx * Transaction ) doSetAttrViewColDate ( operation * Operation ) ( ret * TxErr ) {
err := setAttributeViewColDate ( operation )
if nil != err {
return & TxErr { code : TxErrWriteAttributeView , id : operation . AvID , msg : err . Error ( ) }
}
return
}
func setAttributeViewColDate ( operation * Operation ) ( err error ) {
attrView , err := av . ParseAttributeView ( operation . AvID )
if nil != err {
return
}
keyID := operation . ID
key , _ := attrView . GetKey ( keyID )
if nil == key || av . KeyTypeDate != key . Type {
return
}
if nil == key . Date {
key . Date = & av . Date { }
}
key . Date . AutoFillNow = operation . Data . ( bool )
err = av . SaveAttributeView ( attrView )
return
}
2024-03-01 20:38:53 +08:00
func ( tx * Transaction ) doHideAttrViewName ( operation * Operation ) ( ret * TxErr ) {
err := hideAttrViewName ( operation )
if nil != err {
return & TxErr { code : TxErrWriteAttributeView , id : operation . AvID , msg : err . Error ( ) }
}
return
}
func hideAttrViewName ( operation * Operation ) ( err error ) {
attrView , err := av . ParseAttributeView ( operation . AvID )
if nil != err {
logging . LogErrorf ( "parse attribute view [%s] failed: %s" , operation . AvID , err )
return
}
2024-03-04 16:41:41 +08:00
view , err := getAttrViewViewByBlockID ( attrView , operation . BlockID )
2024-03-01 20:38:53 +08:00
if nil == view {
2024-03-04 16:41:41 +08:00
logging . LogErrorf ( "get view [%s] failed: %s" , operation . BlockID , err )
2024-03-01 20:38:53 +08:00
return
}
view . HideAttrViewName = operation . Data . ( bool )
err = av . SaveAttributeView ( attrView )
return
}
2023-12-24 21:47:10 +08:00
func ( tx * Transaction ) doUpdateAttrViewColRollup ( operation * Operation ) ( ret * TxErr ) {
err := updateAttributeViewColRollup ( operation )
if nil != err {
return & TxErr { code : TxErrWriteAttributeView , id : operation . AvID , msg : err . Error ( ) }
}
return
}
func updateAttributeViewColRollup ( operation * Operation ) ( err error ) {
// operation.AvID 汇总列所在 av
// operation.ID 汇总列 ID
// operation.ParentID 汇总列基于的关联列 ID
// operation.KeyID 目标列 ID
// operation.Data 计算方式
attrView , err := av . ParseAttributeView ( operation . AvID )
if nil != err {
return
}
rollUpKey , _ := attrView . GetKey ( operation . ID )
if nil == rollUpKey {
return
}
rollUpKey . Rollup = & av . Rollup {
RelationKeyID : operation . ParentID ,
KeyID : operation . KeyID ,
}
2023-12-30 20:49:57 +08:00
if nil != operation . Data {
2023-12-30 17:15:50 +08:00
data := operation . Data . ( map [ string ] interface { } )
if nil != data [ "calc" ] {
calcData , jsonErr := gulu . JSON . MarshalJSON ( data [ "calc" ] )
if nil != jsonErr {
err = jsonErr
return
}
if jsonErr = gulu . JSON . UnmarshalJSON ( calcData , & rollUpKey . Rollup . Calc ) ; nil != jsonErr {
err = jsonErr
return
}
}
2023-12-24 21:47:10 +08:00
}
err = av . SaveAttributeView ( attrView )
return
}
2023-12-23 11:35:53 +08:00
func ( tx * Transaction ) doUpdateAttrViewColRelation ( operation * Operation ) ( ret * TxErr ) {
err := updateAttributeViewColRelation ( operation )
if nil != err {
return & TxErr { code : TxErrWriteAttributeView , id : operation . AvID , msg : err . Error ( ) }
}
return
}
func updateAttributeViewColRelation ( operation * Operation ) ( err error ) {
2023-12-23 17:45:46 +08:00
// operation.AvID 源 avID
// operation.ID 目标 avID
// operation.KeyID 源 av 关联列 ID
// operation.IsTwoWay 是否双向关联
// operation.BackRelationKeyID 双向关联的目标关联列 ID
// operation.Name 双向关联的目标关联列名称
2023-12-25 12:06:53 +08:00
// operation.Format 源 av 关联列名称
2023-12-23 17:45:46 +08:00
srcAv , err := av . ParseAttributeView ( operation . AvID )
2023-12-23 11:35:53 +08:00
if nil != err {
return
}
2023-12-23 17:45:46 +08:00
destAv , err := av . ParseAttributeView ( operation . ID )
2023-12-23 11:35:53 +08:00
if nil != err {
return
}
2023-12-23 17:45:46 +08:00
isSameAv := srcAv . ID == destAv . ID
2023-12-24 09:40:01 +08:00
if isSameAv {
destAv = srcAv
}
2023-12-23 17:45:46 +08:00
for _ , keyValues := range srcAv . KeyValues {
2023-12-24 11:24:40 +08:00
if keyValues . Key . ID != operation . KeyID {
continue
}
2023-12-23 17:57:45 +08:00
2023-12-24 11:24:40 +08:00
srcRel := keyValues . Key . Relation
// 已经设置过双向关联的话需要先断开双向关联
2023-12-31 10:30:39 +08:00
if nil != srcRel {
if srcRel . IsTwoWay {
oldDestAv , _ := av . ParseAttributeView ( srcRel . AvID )
if nil != oldDestAv {
isOldSameAv := oldDestAv . ID == destAv . ID
if isOldSameAv {
oldDestAv = destAv
}
2023-12-23 17:57:45 +08:00
2023-12-31 10:30:39 +08:00
oldDestKey , _ := oldDestAv . GetKey ( srcRel . BackKeyID )
if nil != oldDestKey && nil != oldDestKey . Relation && oldDestKey . Relation . AvID == srcAv . ID && oldDestKey . Relation . IsTwoWay {
oldDestKey . Relation . IsTwoWay = false
oldDestKey . Relation . BackKeyID = ""
}
2023-12-24 11:24:40 +08:00
2023-12-31 10:30:39 +08:00
if ! isOldSameAv {
err = av . SaveAttributeView ( oldDestAv )
if nil != err {
return
}
2023-12-23 17:57:45 +08:00
}
}
}
2023-12-31 10:30:39 +08:00
av . RemoveAvRel ( srcAv . ID , srcRel . AvID )
2023-12-24 11:24:40 +08:00
}
2023-12-23 17:57:45 +08:00
2023-12-24 11:24:40 +08:00
srcRel = & av . Relation {
AvID : operation . ID ,
IsTwoWay : operation . IsTwoWay ,
2023-12-23 11:35:53 +08:00
}
2023-12-24 11:24:40 +08:00
if operation . IsTwoWay {
srcRel . BackKeyID = operation . BackRelationKeyID
} else {
srcRel . BackKeyID = ""
}
2023-12-24 12:44:51 +08:00
keyValues . Key . Relation = srcRel
2023-12-25 12:06:53 +08:00
keyValues . Key . Name = operation . Format
2023-12-24 11:24:40 +08:00
break
2023-12-23 11:35:53 +08:00
}
2023-12-23 17:45:46 +08:00
destAdded := false
2023-12-23 19:57:50 +08:00
backRelKey , _ := destAv . GetKey ( operation . BackRelationKeyID )
if nil != backRelKey {
backRelKey . Relation = & av . Relation {
AvID : operation . AvID ,
IsTwoWay : operation . IsTwoWay ,
BackKeyID : operation . KeyID ,
2023-12-23 17:45:46 +08:00
}
2023-12-23 19:57:50 +08:00
destAdded = true
2023-12-24 22:36:48 +08:00
if operation . IsTwoWay {
name := strings . TrimSpace ( operation . Name )
if "" == name {
2023-12-25 12:06:53 +08:00
name = srcAv . Name + " " + operation . Format
2023-12-24 22:36:48 +08:00
}
2023-12-25 12:06:53 +08:00
backRelKey . Name = strings . TrimSpace ( name )
2023-12-24 22:36:48 +08:00
}
2023-12-23 17:45:46 +08:00
}
2023-12-23 19:57:50 +08:00
2023-12-23 17:45:46 +08:00
if ! destAdded {
2023-12-24 09:50:42 +08:00
if operation . IsTwoWay {
2023-12-24 14:40:05 +08:00
name := strings . TrimSpace ( operation . Name )
if "" == name {
2023-12-25 12:06:53 +08:00
name = srcAv . Name + " " + operation . Format
2023-12-24 14:40:05 +08:00
}
2023-12-24 09:50:42 +08:00
destAv . KeyValues = append ( destAv . KeyValues , & av . KeyValues {
Key : & av . Key {
ID : operation . BackRelationKeyID ,
2023-12-24 14:40:05 +08:00
Name : name ,
2023-12-24 09:50:42 +08:00
Type : av . KeyTypeRelation ,
Relation : & av . Relation { AvID : operation . AvID , IsTwoWay : operation . IsTwoWay , BackKeyID : operation . KeyID } ,
} ,
} )
2023-12-24 09:40:01 +08:00
2023-12-24 09:50:42 +08:00
for _ , v := range destAv . Views {
switch v . LayoutType {
case av . LayoutTypeTable :
2023-12-24 09:55:12 +08:00
v . Table . Columns = append ( v . Table . Columns , & av . ViewTableColumn { ID : operation . BackRelationKeyID } )
2023-12-24 09:50:42 +08:00
}
2023-12-24 09:40:01 +08:00
}
}
2023-12-23 17:45:46 +08:00
}
err = av . SaveAttributeView ( srcAv )
if nil != err {
return
}
if ! isSameAv {
err = av . SaveAttributeView ( destAv )
2023-12-23 19:57:50 +08:00
util . BroadcastByType ( "protyle" , "refreshAttributeView" , 0 , "" , map [ string ] interface { } { "id" : destAv . ID } )
2023-12-23 17:45:46 +08:00
}
2023-12-31 10:30:39 +08:00
2023-12-31 10:58:40 +08:00
av . UpsertAvBackRel ( srcAv . ID , destAv . ID )
2023-12-23 11:35:53 +08:00
return
}
2023-12-01 15:39:21 +08:00
func ( tx * Transaction ) doSortAttrViewView ( operation * Operation ) ( ret * TxErr ) {
avID := operation . AvID
attrView , err := av . ParseAttributeView ( avID )
if nil != err {
logging . LogErrorf ( "parse attribute view [%s] failed: %s" , operation . AvID , err )
return & TxErr { code : TxErrWriteAttributeView , id : operation . AvID , msg : err . Error ( ) }
}
2024-03-13 21:59:59 +08:00
view := attrView . GetView ( operation . ID )
2024-03-04 16:41:41 +08:00
if nil == view {
logging . LogErrorf ( "get view failed: %s" , operation . BlockID )
return & TxErr { code : TxErrWriteAttributeView , id : operation . AvID , msg : err . Error ( ) }
}
viewID := view . ID
2024-03-13 21:59:59 +08:00
previousViewID := operation . PreviousID
if viewID == previousViewID {
2023-12-01 15:39:21 +08:00
return
}
2023-12-01 20:06:08 +08:00
var index , previousIndex int
for i , v := range attrView . Views {
if v . ID == viewID {
view = v
index = i
break
}
}
2023-12-01 15:39:21 +08:00
if nil == view {
2023-12-01 20:06:08 +08:00
return
2023-12-01 15:39:21 +08:00
}
2023-12-01 20:06:08 +08:00
attrView . Views = append ( attrView . Views [ : index ] , attrView . Views [ index + 1 : ] ... )
for i , v := range attrView . Views {
2024-03-13 21:59:59 +08:00
if v . ID == previousViewID {
2023-12-01 20:06:08 +08:00
previousIndex = i + 1
break
2023-12-01 15:39:21 +08:00
}
}
2023-12-01 20:06:08 +08:00
attrView . Views = util . InsertElem ( attrView . Views , previousIndex , view )
2023-12-01 15:39:21 +08:00
if err = av . SaveAttributeView ( attrView ) ; nil != err {
logging . LogErrorf ( "save attribute view [%s] failed: %s" , avID , err )
return & TxErr { code : TxErrCodeWriteTree , msg : err . Error ( ) , id : avID }
}
return
}
2023-12-01 08:55:32 +08:00
func ( tx * Transaction ) doRemoveAttrViewView ( operation * Operation ) ( ret * TxErr ) {
var err error
avID := operation . AvID
attrView , err := av . ParseAttributeView ( avID )
if nil != err {
logging . LogErrorf ( "parse attribute view [%s] failed: %s" , avID , err )
return & TxErr { code : TxErrCodeBlockNotFound , id : avID }
}
2023-12-01 10:33:38 +08:00
if 1 >= len ( attrView . Views ) {
2024-03-04 16:41:41 +08:00
logging . LogWarnf ( "can't remove last view [%s] of attribute view [%s]" , operation . AvID , avID )
return
}
view , err := getAttrViewViewByBlockID ( attrView , operation . BlockID )
if nil == view {
logging . LogWarnf ( "get view failed: %s" , operation . BlockID )
2023-12-01 10:33:38 +08:00
return
}
2024-03-04 16:41:41 +08:00
viewID := view . ID
2023-12-01 10:44:10 +08:00
var index int
2023-12-01 08:55:32 +08:00
for i , view := range attrView . Views {
if viewID == view . ID {
attrView . Views = append ( attrView . Views [ : i ] , attrView . Views [ i + 1 : ] ... )
2023-12-01 10:44:10 +08:00
index = i - 1
2023-12-01 08:55:32 +08:00
break
}
}
2023-12-01 15:39:21 +08:00
if 0 > index {
index = 0
}
2024-03-04 23:07:32 +08:00
2024-03-04 23:07:01 +08:00
attrView . ViewID = attrView . Views [ index ] . ID
2024-03-04 23:07:32 +08:00
if err = av . SaveAttributeView ( attrView ) ; nil != err {
logging . LogErrorf ( "save attribute view [%s] failed: %s" , avID , err )
return & TxErr { code : TxErrCodeWriteTree , msg : err . Error ( ) , id : avID }
}
2023-12-01 08:55:32 +08:00
2024-03-08 22:21:51 +08:00
trees , nodes := getMirrorBlocksNodes ( avID )
for _ , node := range nodes {
attrs := parse . IAL2Map ( node . KramdownIAL )
blockViewID := attrs [ av . NodeAttrView ]
if blockViewID == viewID {
attrs [ av . NodeAttrView ] = attrView . ViewID
oldAttrs , e := setNodeAttrs0 ( node , attrs )
if nil != e {
logging . LogErrorf ( "set node attrs failed: %s" , e )
continue
}
cache . PutBlockIAL ( node . ID , parse . IAL2Map ( node . KramdownIAL ) )
pushBroadcastAttrTransactions ( oldAttrs , node )
}
}
for _ , tree := range trees {
2024-04-11 21:54:34 +08:00
if err = indexWriteTreeUpsertQueue ( tree ) ; nil != err {
2024-03-08 22:21:51 +08:00
return
}
}
return
}
func getMirrorBlocksNodes ( avID string ) ( trees [ ] * parse . Tree , nodes [ ] * ast . Node ) {
2024-03-10 23:27:13 +08:00
mirrorBlocks := treenode . GetMirrorAttrViewBlockIDs ( avID )
2024-03-04 14:31:44 +08:00
mirrorBlockTree := map [ string ] * parse . Tree { }
2024-03-08 22:21:51 +08:00
treeMap := map [ string ] * parse . Tree { }
2024-03-04 14:14:10 +08:00
for _ , mirrorBlock := range mirrorBlocks {
bt := treenode . GetBlockTree ( mirrorBlock )
if nil == bt {
logging . LogErrorf ( "get block tree by block ID [%s] failed" , mirrorBlock )
continue
}
2024-03-04 14:31:44 +08:00
tree := mirrorBlockTree [ mirrorBlock ]
2024-03-04 14:14:10 +08:00
if nil == tree {
2024-03-10 23:27:13 +08:00
tree , _ = LoadTreeByBlockID ( mirrorBlock )
2024-03-04 14:14:10 +08:00
if nil == tree {
logging . LogErrorf ( "load tree by block ID [%s] failed" , mirrorBlock )
continue
}
2024-03-08 22:21:51 +08:00
treeMap [ tree . ID ] = tree
2024-03-04 14:31:44 +08:00
mirrorBlockTree [ mirrorBlock ] = tree
2024-03-04 14:14:10 +08:00
}
}
for _ , mirrorBlock := range mirrorBlocks {
2024-03-04 14:31:44 +08:00
tree := mirrorBlockTree [ mirrorBlock ]
2024-03-04 14:14:10 +08:00
node := treenode . GetNodeInTree ( tree , mirrorBlock )
if nil == node {
logging . LogErrorf ( "get node in tree by block ID [%s] failed" , mirrorBlock )
continue
}
2024-03-08 22:21:51 +08:00
nodes = append ( nodes , node )
2024-03-04 14:14:10 +08:00
}
2024-03-08 22:21:51 +08:00
for _ , tree := range treeMap {
trees = append ( trees , tree )
2024-03-04 14:14:10 +08:00
}
2023-12-01 08:55:32 +08:00
return
}
2023-12-01 09:48:20 +08:00
func ( tx * Transaction ) doDuplicateAttrViewView ( operation * Operation ) ( ret * TxErr ) {
var err error
avID := operation . AvID
attrView , err := av . ParseAttributeView ( avID )
if nil != err {
logging . LogErrorf ( "parse attribute view [%s] failed: %s" , avID , err )
return & TxErr { code : TxErrWriteAttributeView , id : avID }
}
masterView := attrView . GetView ( operation . PreviousID )
if nil == masterView {
logging . LogErrorf ( "get master view failed: %s" , avID )
return & TxErr { code : TxErrWriteAttributeView , id : avID }
}
2024-03-04 16:41:41 +08:00
node , tree , _ := getNodeByBlockID ( nil , operation . BlockID )
if nil == node {
logging . LogErrorf ( "get node by block ID [%s] failed" , operation . BlockID )
return & TxErr { code : TxErrWriteAttributeView , id : operation . AvID }
}
attrs := parse . IAL2Map ( node . KramdownIAL )
attrs [ av . NodeAttrView ] = operation . ID
err = setNodeAttrs ( node , tree , attrs )
if nil != err {
logging . LogWarnf ( "set node [%s] attrs failed: %s" , operation . BlockID , err )
return
}
2023-12-01 09:48:20 +08:00
view := av . NewTableView ( )
2024-03-04 16:41:41 +08:00
view . ID = operation . ID
2023-12-01 09:48:20 +08:00
attrView . Views = append ( attrView . Views , view )
attrView . ViewID = view . ID
view . Icon = masterView . Icon
2023-12-01 10:57:28 +08:00
view . Name = attrView . GetDuplicateViewName ( masterView . Name )
2023-12-01 09:48:20 +08:00
view . LayoutType = masterView . LayoutType
2024-03-01 22:40:56 +08:00
view . HideAttrViewName = masterView . HideAttrViewName
2023-12-01 09:48:20 +08:00
for _ , col := range masterView . Table . Columns {
view . Table . Columns = append ( view . Table . Columns , & av . ViewTableColumn {
ID : col . ID ,
Wrap : col . Wrap ,
Hidden : col . Hidden ,
Pin : col . Pin ,
Width : col . Width ,
Calc : col . Calc ,
} )
}
for _ , filter := range masterView . Table . Filters {
view . Table . Filters = append ( view . Table . Filters , & av . ViewFilter {
2024-03-01 17:06:12 +08:00
Column : filter . Column ,
Operator : filter . Operator ,
Value : filter . Value ,
RelativeDate : filter . RelativeDate ,
RelativeDate2 : filter . RelativeDate2 ,
2023-12-01 09:48:20 +08:00
} )
}
for _ , s := range masterView . Table . Sorts {
view . Table . Sorts = append ( view . Table . Sorts , & av . ViewSort {
Column : s . Column ,
Order : s . Order ,
} )
}
2023-12-08 22:00:23 +08:00
view . Table . PageSize = masterView . Table . PageSize
2023-12-13 21:38:36 +08:00
view . Table . RowIDs = masterView . Table . RowIDs
2023-12-08 22:00:23 +08:00
2023-12-01 09:48:20 +08:00
if err = av . SaveAttributeView ( attrView ) ; nil != err {
logging . LogErrorf ( "save attribute view [%s] failed: %s" , avID , err )
return & TxErr { code : TxErrWriteAttributeView , msg : err . Error ( ) , id : avID }
}
return
}
2023-12-01 08:55:32 +08:00
func ( tx * Transaction ) doAddAttrViewView ( operation * Operation ) ( ret * TxErr ) {
var err error
avID := operation . AvID
attrView , err := av . ParseAttributeView ( avID )
if nil != err {
logging . LogErrorf ( "parse attribute view [%s] failed: %s" , avID , err )
return & TxErr { code : TxErrWriteAttributeView , id : avID }
}
2024-03-04 16:41:41 +08:00
if 1 > len ( attrView . Views ) {
logging . LogErrorf ( "no view in attribute view [%s]" , avID )
return & TxErr { code : TxErrWriteAttributeView , id : avID }
}
2023-12-01 09:17:44 +08:00
firstView := attrView . Views [ 0 ]
if nil == firstView {
logging . LogErrorf ( "get first view failed: %s" , avID )
2023-12-01 08:55:32 +08:00
return & TxErr { code : TxErrWriteAttributeView , id : avID }
}
2024-03-04 16:41:41 +08:00
node , tree , _ := getNodeByBlockID ( nil , operation . BlockID )
if nil == node {
logging . LogErrorf ( "get node by block ID [%s] failed" , operation . BlockID )
return & TxErr { code : TxErrWriteAttributeView , id : operation . AvID }
}
attrs := parse . IAL2Map ( node . KramdownIAL )
attrs [ av . NodeAttrView ] = operation . ID
err = setNodeAttrs ( node , tree , attrs )
if nil != err {
logging . LogWarnf ( "set node [%s] attrs failed: %s" , operation . BlockID , err )
return
}
2023-12-01 09:17:44 +08:00
view := av . NewTableView ( )
2024-03-04 16:41:41 +08:00
view . ID = operation . ID
2023-12-01 08:55:32 +08:00
attrView . Views = append ( attrView . Views , view )
attrView . ViewID = view . ID
2023-12-01 09:17:44 +08:00
for _ , col := range firstView . Table . Columns {
view . Table . Columns = append ( view . Table . Columns , & av . ViewTableColumn { ID : col . ID } )
}
2023-12-13 21:38:36 +08:00
view . Table . RowIDs = firstView . Table . RowIDs
2023-12-01 08:55:32 +08:00
if err = av . SaveAttributeView ( attrView ) ; nil != err {
logging . LogErrorf ( "save attribute view [%s] failed: %s" , avID , err )
return & TxErr { code : TxErrWriteAttributeView , msg : err . Error ( ) , id : avID }
}
return
}
2023-12-01 09:00:14 +08:00
func ( tx * Transaction ) doSetAttrViewViewName ( operation * Operation ) ( ret * TxErr ) {
var err error
avID := operation . AvID
attrView , err := av . ParseAttributeView ( avID )
if nil != err {
logging . LogErrorf ( "parse attribute view [%s] failed: %s" , avID , err )
return & TxErr { code : TxErrWriteAttributeView , id : avID }
}
2024-03-04 16:41:41 +08:00
viewID := operation . ID
2023-12-01 09:00:14 +08:00
view := attrView . GetView ( viewID )
if nil == view {
logging . LogErrorf ( "get view [%s] failed: %s" , viewID , err )
return & TxErr { code : TxErrWriteAttributeView , id : viewID }
}
2023-12-01 11:43:49 +08:00
view . Name = strings . TrimSpace ( operation . Data . ( string ) )
2023-12-01 09:00:14 +08:00
if err = av . SaveAttributeView ( attrView ) ; nil != err {
logging . LogErrorf ( "save attribute view [%s] failed: %s" , avID , err )
return & TxErr { code : TxErrWriteAttributeView , msg : err . Error ( ) , id : avID }
}
return
}
2023-12-01 09:48:20 +08:00
func ( tx * Transaction ) doSetAttrViewViewIcon ( operation * Operation ) ( ret * TxErr ) {
var err error
avID := operation . AvID
attrView , err := av . ParseAttributeView ( avID )
if nil != err {
logging . LogErrorf ( "parse attribute view [%s] failed: %s" , avID , err )
return & TxErr { code : TxErrWriteAttributeView , id : avID }
}
2024-03-04 16:41:41 +08:00
viewID := operation . ID
2023-12-01 09:48:20 +08:00
view := attrView . GetView ( viewID )
if nil == view {
logging . LogErrorf ( "get view [%s] failed: %s" , viewID , err )
return & TxErr { code : TxErrWriteAttributeView , id : viewID }
}
view . Icon = operation . Data . ( string )
if err = av . SaveAttributeView ( attrView ) ; nil != err {
logging . LogErrorf ( "save attribute view [%s] failed: %s" , avID , err )
return & TxErr { code : TxErrWriteAttributeView , msg : err . Error ( ) , id : avID }
}
return
}
2023-07-11 22:36:02 +08:00
func ( tx * Transaction ) doSetAttrViewName ( operation * Operation ) ( ret * TxErr ) {
err := setAttributeViewName ( operation )
2023-07-11 22:11:15 +08:00
if nil != err {
return & TxErr { code : TxErrWriteAttributeView , id : operation . AvID , msg : err . Error ( ) }
}
return
}
2024-03-08 22:51:28 +08:00
const attrAvNameTpl = ` <span data-av-id="$ { avID}" data-popover-url="/api/av/getMirrorDatabaseBlocks" class="popover__block">$ { avName}</span> `
2023-07-11 22:11:15 +08:00
func setAttributeViewName ( operation * Operation ) ( err error ) {
2024-03-08 22:51:28 +08:00
avID := operation . ID
attrView , err := av . ParseAttributeView ( avID )
2023-07-11 22:11:15 +08:00
if nil != err {
return
}
2023-12-01 11:43:49 +08:00
attrView . Name = strings . TrimSpace ( operation . Data . ( string ) )
2023-07-11 22:11:15 +08:00
err = av . SaveAttributeView ( attrView )
2024-03-08 22:51:28 +08:00
nodes := getAttrViewBoundNodes ( attrView )
for _ , node := range nodes {
2024-03-08 23:12:33 +08:00
avNames := getAvNames ( node . IALAttr ( av . NodeAttrNameAvs ) )
2024-03-08 22:51:28 +08:00
oldAttrs := parse . IAL2Map ( node . KramdownIAL )
2024-03-10 23:00:45 +08:00
node . SetIALAttr ( av . NodeAttrViewNames , avNames )
2024-03-08 22:59:28 +08:00
pushBroadcastAttrTransactions ( oldAttrs , node )
}
return
}
2024-03-08 22:51:28 +08:00
2024-03-08 23:12:33 +08:00
func getAvNames ( avIDs string ) ( ret string ) {
2024-03-08 22:59:28 +08:00
if "" == avIDs {
return
}
avNames := bytes . Buffer { }
nodeAvIDs := strings . Split ( avIDs , "," )
for _ , nodeAvID := range nodeAvIDs {
nodeAvName , getErr := av . GetAttributeViewName ( nodeAvID )
if nil != getErr {
continue
2024-03-08 22:51:28 +08:00
}
2024-03-08 22:59:28 +08:00
if "" == nodeAvName {
2024-03-24 22:15:19 +08:00
nodeAvName = Conf . language ( 105 )
2024-03-08 22:51:28 +08:00
}
2024-03-08 22:59:28 +08:00
tpl := strings . ReplaceAll ( attrAvNameTpl , "${avID}" , nodeAvID )
tpl = strings . ReplaceAll ( tpl , "${avName}" , nodeAvName )
avNames . WriteString ( tpl )
avNames . WriteString ( " " )
}
if 0 < avNames . Len ( ) {
avNames . Truncate ( avNames . Len ( ) - 6 )
ret = avNames . String ( )
2024-03-08 22:51:28 +08:00
}
return
}
func getAttrViewBoundNodes ( attrView * av . AttributeView ) ( ret [ ] * ast . Node ) {
blockKeyValues := attrView . GetBlockKeyValues ( )
treeMap := map [ string ] * parse . Tree { }
for _ , blockKeyValue := range blockKeyValues . Values {
if blockKeyValue . IsDetached {
continue
}
var tree * parse . Tree
tree = treeMap [ blockKeyValue . BlockID ]
if nil == tree {
2024-03-10 23:27:13 +08:00
tree , _ = LoadTreeByBlockID ( blockKeyValue . BlockID )
2024-03-08 22:51:28 +08:00
}
if nil == tree {
continue
}
treeMap [ blockKeyValue . BlockID ] = tree
node := treenode . GetNodeInTree ( tree , blockKeyValue . BlockID )
if nil == node {
continue
}
ret = append ( ret , node )
}
2023-07-11 22:11:15 +08:00
return
}
2023-07-11 22:36:02 +08:00
func ( tx * Transaction ) doSetAttrViewFilters ( operation * Operation ) ( ret * TxErr ) {
err := setAttributeViewFilters ( operation )
if nil != err {
return & TxErr { code : TxErrWriteAttributeView , id : operation . AvID , msg : err . Error ( ) }
}
return
}
2023-07-11 22:11:15 +08:00
func setAttributeViewFilters ( operation * Operation ) ( err error ) {
2023-07-13 09:45:39 +08:00
attrView , err := av . ParseAttributeView ( operation . AvID )
2023-07-11 22:11:15 +08:00
if nil != err {
return
}
2024-03-04 16:41:41 +08:00
view , err := getAttrViewViewByBlockID ( attrView , operation . BlockID )
2023-07-11 23:47:17 +08:00
if nil != err {
2023-07-11 22:11:15 +08:00
return
}
operationData := operation . Data . ( [ ] interface { } )
data , err := gulu . JSON . MarshalJSON ( operationData )
if nil != err {
return
}
2023-07-12 21:39:55 +08:00
switch view . LayoutType {
2023-07-12 19:10:05 +08:00
case av . LayoutTypeTable :
if err = gulu . JSON . UnmarshalJSON ( data , & view . Table . Filters ) ; nil != err {
return
}
2023-07-11 22:11:15 +08:00
}
err = av . SaveAttributeView ( attrView )
return
}
2023-07-11 22:36:02 +08:00
func ( tx * Transaction ) doSetAttrViewSorts ( operation * Operation ) ( ret * TxErr ) {
err := setAttributeViewSorts ( operation )
if nil != err {
return & TxErr { code : TxErrWriteAttributeView , id : operation . AvID , msg : err . Error ( ) }
}
return
}
2023-07-11 22:11:15 +08:00
func setAttributeViewSorts ( operation * Operation ) ( err error ) {
2023-07-13 09:45:39 +08:00
attrView , err := av . ParseAttributeView ( operation . AvID )
2023-07-11 22:11:15 +08:00
if nil != err {
return
}
2024-03-04 16:41:41 +08:00
view , err := getAttrViewViewByBlockID ( attrView , operation . BlockID )
2023-07-11 23:47:17 +08:00
if nil != err {
2023-07-11 22:11:15 +08:00
return
}
operationData := operation . Data . ( [ ] interface { } )
data , err := gulu . JSON . MarshalJSON ( operationData )
if nil != err {
return
}
2023-07-12 21:39:55 +08:00
switch view . LayoutType {
2023-07-12 19:10:05 +08:00
case av . LayoutTypeTable :
if err = gulu . JSON . UnmarshalJSON ( data , & view . Table . Sorts ) ; nil != err {
return
}
2023-07-11 22:11:15 +08:00
}
err = av . SaveAttributeView ( attrView )
return
}
2023-12-08 21:05:21 +08:00
func ( tx * Transaction ) doSetAttrViewPageSize ( operation * Operation ) ( ret * TxErr ) {
err := setAttributeViewPageSize ( operation )
if nil != err {
return & TxErr { code : TxErrWriteAttributeView , id : operation . AvID , msg : err . Error ( ) }
}
return
}
func setAttributeViewPageSize ( operation * Operation ) ( err error ) {
attrView , err := av . ParseAttributeView ( operation . AvID )
if nil != err {
return
}
2024-03-04 21:58:26 +08:00
view , err := getAttrViewViewByBlockID ( attrView , operation . BlockID )
2023-12-08 21:05:21 +08:00
if nil != err {
return
}
switch view . LayoutType {
case av . LayoutTypeTable :
view . Table . PageSize = int ( operation . Data . ( float64 ) )
}
err = av . SaveAttributeView ( attrView )
return
}
2023-07-15 21:36:35 +08:00
func ( tx * Transaction ) doSetAttrViewColCalc ( operation * Operation ) ( ret * TxErr ) {
err := setAttributeViewColumnCalc ( operation )
if nil != err {
return & TxErr { code : TxErrWriteAttributeView , id : operation . AvID , msg : err . Error ( ) }
}
return
}
func setAttributeViewColumnCalc ( operation * Operation ) ( err error ) {
attrView , err := av . ParseAttributeView ( operation . AvID )
if nil != err {
return
}
2024-03-04 16:41:41 +08:00
view , err := getAttrViewViewByBlockID ( attrView , operation . BlockID )
2023-07-15 21:36:35 +08:00
if nil != err {
return
}
2023-07-15 22:27:32 +08:00
operationData := operation . Data . ( interface { } )
2023-07-15 21:36:35 +08:00
data , err := gulu . JSON . MarshalJSON ( operationData )
if nil != err {
return
}
calc := & av . ColumnCalc { }
switch view . LayoutType {
case av . LayoutTypeTable :
if err = gulu . JSON . UnmarshalJSON ( data , calc ) ; nil != err {
return
}
for _ , column := range view . Table . Columns {
if column . ID == operation . ID {
column . Calc = calc
break
}
}
}
err = av . SaveAttributeView ( attrView )
return
}
2023-07-11 22:36:02 +08:00
func ( tx * Transaction ) doInsertAttrViewBlock ( operation * Operation ) ( ret * TxErr ) {
2024-03-14 10:58:49 +08:00
err := AddAttributeViewBlock ( tx , operation . SrcIDs , operation . AvID , operation . BlockID , operation . PreviousID , operation . IsDetached , operation . IgnoreFillFilterVal )
2024-01-18 21:01:36 +08:00
if nil != err {
return & TxErr { code : TxErrWriteAttributeView , id : operation . AvID , msg : err . Error ( ) }
}
return
}
2024-03-14 10:58:49 +08:00
func AddAttributeViewBlock ( tx * Transaction , srcIDs [ ] string , avID , blockID , previousBlockID string , isDetached , ignoreFillFilter bool ) ( err error ) {
2024-01-18 21:01:36 +08:00
for _ , id := range srcIDs {
var tree * parse . Tree
if ! isDetached {
var loadErr error
if nil != tx {
tree , loadErr = tx . loadTree ( id )
} else {
2024-03-10 23:27:13 +08:00
tree , loadErr = LoadTreeByBlockID ( id )
2024-01-18 21:01:36 +08:00
}
if nil != loadErr {
logging . LogErrorf ( "load tree [%s] failed: %s" , id , err )
return loadErr
}
2023-07-31 21:16:25 +08:00
}
2024-03-14 10:58:49 +08:00
if avErr := addAttributeViewBlock ( avID , blockID , previousBlockID , id , isDetached , ignoreFillFilter , tree , tx ) ; nil != avErr {
2024-01-18 21:01:36 +08:00
return avErr
2023-07-11 22:36:02 +08:00
}
}
return
}
2024-03-14 10:58:49 +08:00
func addAttributeViewBlock ( avID , blockID , previousBlockID , addingBlockID string , isDetached , ignoreFillFilter bool , tree * parse . Tree , tx * Transaction ) ( err error ) {
2023-09-27 16:23:46 +08:00
var node * ast . Node
2024-01-18 21:01:36 +08:00
if ! isDetached {
2024-03-04 16:41:41 +08:00
node = treenode . GetNodeInTree ( tree , addingBlockID )
2023-09-27 16:23:46 +08:00
if nil == node {
err = ErrBlockNotFound
return
}
2023-09-27 23:14:42 +08:00
} else {
2024-03-04 16:41:41 +08:00
if "" == addingBlockID {
addingBlockID = ast . NewNodeID ( )
logging . LogWarnf ( "detached block id is empty, generate a new one [%s]" , addingBlockID )
2023-11-10 17:10:28 +08:00
}
2023-07-11 22:36:02 +08:00
}
2024-01-18 21:01:36 +08:00
attrView , err := av . ParseAttributeView ( avID )
2023-07-11 22:36:02 +08:00
if nil != err {
return
}
2024-04-08 09:25:19 +08:00
var content string
if ! isDetached {
content = getNodeRefText ( node )
}
now := time . Now ( ) . UnixMilli ( )
// 检查是否重复添加相同的块
2023-07-12 19:10:05 +08:00
blockValues := attrView . GetBlockKeyValues ( )
for _ , blockValue := range blockValues . Values {
2024-03-04 16:41:41 +08:00
if blockValue . Block . ID == addingBlockID {
2024-03-28 21:27:31 +08:00
if ! isDetached {
2024-04-08 09:25:19 +08:00
// 重复绑定一下,比如剪切数据库块、取消绑定块后再次添加的场景需要
bindBlockAv0 ( tx , avID , node , tree )
blockValue . IsDetached = isDetached
blockValue . Block . Content = content
blockValue . UpdatedAt = now
err = av . SaveAttributeView ( attrView )
2024-03-28 21:27:31 +08:00
}
2023-07-11 22:36:02 +08:00
return
}
}
2024-03-03 16:12:44 +08:00
blockValue := & av . Value {
ID : ast . NewNodeID ( ) ,
KeyID : blockValues . Key . ID ,
2024-03-04 16:41:41 +08:00
BlockID : addingBlockID ,
2024-03-03 16:12:44 +08:00
Type : av . KeyTypeBlock ,
IsDetached : isDetached ,
CreatedAt : now ,
UpdatedAt : now ,
2024-03-04 16:41:41 +08:00
Block : & av . ValueBlock { ID : addingBlockID , Content : content , Created : now , Updated : now } }
2023-12-17 12:11:59 +08:00
blockValues . Values = append ( blockValues . Values , blockValue )
2023-07-11 22:36:02 +08:00
2024-03-03 16:12:44 +08:00
// 如果存在过滤条件,则将过滤条件应用到新添加的块上
2024-03-04 16:41:41 +08:00
view , _ := getAttrViewViewByBlockID ( attrView , blockID )
2024-03-14 10:58:49 +08:00
if nil != view && 0 < len ( view . Table . Filters ) && ! ignoreFillFilter {
2024-03-09 17:31:06 +08:00
viewable , _ := renderAttributeViewTable ( attrView , view , "" )
2023-12-30 23:01:21 +08:00
viewable . FilterRows ( attrView )
2024-03-21 10:03:08 +08:00
viewable . SortRows ( attrView )
2024-01-07 17:30:52 +08:00
2024-03-10 22:06:25 +08:00
var nearRow * av . TableRow
2024-03-06 23:39:49 +08:00
if 0 < len ( viewable . Rows ) {
2024-03-10 22:06:25 +08:00
if "" != previousBlockID {
for _ , row := range viewable . Rows {
if row . ID == previousBlockID {
nearRow = row
break
}
}
} else {
if 0 < len ( viewable . Rows ) {
nearRow = viewable . Rows [ 0 ]
}
}
2024-03-06 23:39:49 +08:00
}
2024-03-06 11:20:20 +08:00
2024-03-06 23:39:49 +08:00
sameKeyFilterSort := false // 是否在同一个字段上同时存在过滤和排序
if 0 < len ( viewable . Sorts ) {
2024-03-06 11:20:20 +08:00
filterKeys , sortKeys := map [ string ] bool { } , map [ string ] bool { }
2024-03-07 09:37:49 +08:00
for _ , f := range view . Table . Filters {
filterKeys [ f . Column ] = true
2024-03-06 11:20:20 +08:00
}
2024-03-07 09:37:49 +08:00
for _ , s := range view . Table . Sorts {
sortKeys [ s . Column ] = true
2024-03-06 11:20:20 +08:00
}
for key := range filterKeys {
if sortKeys [ key ] {
sameKeyFilterSort = true
2024-03-03 16:12:44 +08:00
break
2023-12-30 16:59:41 +08:00
}
2023-12-17 12:00:55 +08:00
}
}
2024-03-06 11:20:20 +08:00
if ! sameKeyFilterSort {
// 如果在同一个字段上仅存在过滤条件,则将过滤条件应用到新添加的块上
for _ , filter := range view . Table . Filters {
for _ , keyValues := range attrView . KeyValues {
if keyValues . Key . ID == filter . Column {
var defaultVal * av . Value
2024-03-10 22:06:25 +08:00
if nil != nearRow {
defaultVal = nearRow . GetValue ( filter . Column )
2024-03-06 11:20:20 +08:00
}
newValue := filter . GetAffectValue ( keyValues . Key , defaultVal )
if nil == newValue {
continue
}
newValue . ID = ast . NewNodeID ( )
newValue . KeyID = keyValues . Key . ID
newValue . BlockID = addingBlockID
newValue . IsDetached = isDetached
keyValues . Values = append ( keyValues . Values , newValue )
break
}
}
}
}
2023-12-17 12:00:55 +08:00
}
2024-04-04 11:33:51 +08:00
// 处理日期字段默认填充当前创建时间
// The database date field supports filling the current time by default https://github.com/siyuan-note/siyuan/issues/10823
for _ , keyValues := range attrView . KeyValues {
if av . KeyTypeDate == keyValues . Key . Type && nil != keyValues . Key . Date && keyValues . Key . Date . AutoFillNow {
dateVal := & av . Value {
ID : ast . NewNodeID ( ) , KeyID : keyValues . Key . ID , BlockID : addingBlockID , Type : av . KeyTypeDate , IsDetached : isDetached , CreatedAt : now , UpdatedAt : now + 1000 ,
Date : & av . ValueDate { Content : now , IsNotEmpty : true } ,
}
keyValues . Values = append ( keyValues . Values , dateVal )
}
}
2024-01-18 21:01:36 +08:00
if ! isDetached {
2024-04-08 09:25:19 +08:00
bindBlockAv0 ( tx , avID , node , tree )
2023-07-11 22:36:02 +08:00
}
2024-04-04 11:33:51 +08:00
for _ , v := range attrView . Views {
switch v . LayoutType {
2023-12-14 11:23:34 +08:00
case av . LayoutTypeTable :
2024-01-18 21:01:36 +08:00
if "" != previousBlockID {
2023-12-14 11:23:34 +08:00
changed := false
2024-04-04 11:33:51 +08:00
for i , id := range v . Table . RowIDs {
2024-01-18 21:01:36 +08:00
if id == previousBlockID {
2024-04-04 11:33:51 +08:00
v . Table . RowIDs = append ( v . Table . RowIDs [ : i + 1 ] , append ( [ ] string { addingBlockID } , v . Table . RowIDs [ i + 1 : ] ... ) ... )
2023-12-14 11:23:34 +08:00
changed = true
break
}
2023-07-12 19:10:05 +08:00
}
2023-12-14 11:23:34 +08:00
if ! changed {
2024-04-04 11:33:51 +08:00
v . Table . RowIDs = append ( v . Table . RowIDs , addingBlockID )
2023-12-14 11:23:34 +08:00
}
} else {
2024-04-04 11:33:51 +08:00
v . Table . RowIDs = append ( [ ] string { addingBlockID } , v . Table . RowIDs ... )
2023-07-11 22:36:02 +08:00
}
}
}
err = av . SaveAttributeView ( attrView )
return
}
func ( tx * Transaction ) doRemoveAttrViewBlock ( operation * Operation ) ( ret * TxErr ) {
2024-01-18 21:01:36 +08:00
err := removeAttributeViewBlock ( operation . SrcIDs , operation . AvID , tx )
2023-08-02 00:02:30 +08:00
if nil != err {
return & TxErr { code : TxErrWriteAttributeView , id : operation . AvID }
2023-07-11 22:37:18 +08:00
}
return
}
2024-01-18 21:01:36 +08:00
func RemoveAttributeViewBlock ( srcIDs [ ] string , avID string ) ( err error ) {
err = removeAttributeViewBlock ( srcIDs , avID , nil )
return
}
func removeAttributeViewBlock ( srcIDs [ ] string , avID string , tx * Transaction ) ( err error ) {
attrView , err := av . ParseAttributeView ( avID )
2023-07-11 22:37:18 +08:00
if nil != err {
return
}
2023-07-11 22:36:02 +08:00
2023-09-07 18:02:47 +08:00
trees := map [ string ] * parse . Tree { }
2023-07-12 19:10:05 +08:00
for _ , keyValues := range attrView . KeyValues {
2023-08-02 00:02:30 +08:00
tmp := keyValues . Values [ : 0 ]
2023-07-12 19:10:05 +08:00
for i , values := range keyValues . Values {
2024-01-18 21:01:36 +08:00
if ! gulu . Str . Contains ( values . BlockID , srcIDs ) {
2023-08-02 00:02:30 +08:00
tmp = append ( tmp , keyValues . Values [ i ] )
2023-09-07 18:02:47 +08:00
} else {
2023-09-07 18:06:10 +08:00
// Remove av block also remove node attr https://github.com/siyuan-note/siyuan/issues/9091#issuecomment-1709824006
2023-09-07 18:02:47 +08:00
if bt := treenode . GetBlockTree ( values . BlockID ) ; nil != bt {
tree := trees [ bt . RootID ]
if nil == tree {
2024-03-10 23:27:13 +08:00
tree , _ = LoadTreeByBlockID ( values . BlockID )
2023-09-07 18:02:47 +08:00
}
if nil != tree {
trees [ bt . RootID ] = tree
if node := treenode . GetNodeInTree ( tree , values . BlockID ) ; nil != node {
2024-02-16 16:02:22 +08:00
if err = removeNodeAvID ( node , avID , tx , tree ) ; nil != err {
return
2023-09-07 18:02:47 +08:00
}
}
}
}
2023-07-12 19:10:05 +08:00
}
2023-07-11 22:37:18 +08:00
}
2023-08-02 00:02:30 +08:00
keyValues . Values = tmp
2023-07-11 19:45:27 +08:00
}
2023-07-11 22:37:18 +08:00
2023-12-14 11:23:34 +08:00
for _ , view := range attrView . Views {
2024-01-18 21:01:36 +08:00
for _ , blockID := range srcIDs {
2023-12-14 11:23:34 +08:00
view . Table . RowIDs = gulu . Str . RemoveElem ( view . Table . RowIDs , blockID )
}
2023-08-02 00:02:30 +08:00
}
2023-07-13 09:37:52 +08:00
2024-04-03 21:33:53 +08:00
relatedAvIDs := av . GetSrcAvIDs ( avID )
for _ , relatedAvID := range relatedAvIDs {
util . BroadcastByType ( "protyle" , "refreshAttributeView" , 0 , "" , map [ string ] interface { } { "id" : relatedAvID } )
}
2023-07-11 22:37:18 +08:00
err = av . SaveAttributeView ( attrView )
2023-07-03 19:06:54 +08:00
return
}
2024-02-16 16:02:22 +08:00
func removeNodeAvID ( node * ast . Node , avID string , tx * Transaction , tree * parse . Tree ) ( err error ) {
attrs := parse . IAL2Map ( node . KramdownIAL )
if ast . NodeDocument == node . Type {
delete ( attrs , "custom-hidden" )
node . RemoveIALAttr ( "custom-hidden" )
}
if avs := attrs [ av . NodeAttrNameAvs ] ; "" != avs {
avIDs := strings . Split ( avs , "," )
avIDs = gulu . Str . RemoveElem ( avIDs , avID )
var existAvIDs [ ] string
for _ , attributeViewID := range avIDs {
if av . IsAttributeViewExist ( attributeViewID ) {
existAvIDs = append ( existAvIDs , attributeViewID )
}
}
avIDs = existAvIDs
if 0 == len ( avIDs ) {
2024-03-10 22:57:21 +08:00
attrs [ av . NodeAttrNameAvs ] = ""
2024-02-16 16:02:22 +08:00
} else {
attrs [ av . NodeAttrNameAvs ] = strings . Join ( avIDs , "," )
node . SetIALAttr ( av . NodeAttrNameAvs , strings . Join ( avIDs , "," ) )
2024-03-10 22:57:21 +08:00
avNames := getAvNames ( node . IALAttr ( av . NodeAttrNameAvs ) )
2024-03-10 23:00:45 +08:00
attrs [ av . NodeAttrViewNames ] = avNames
2024-02-16 16:02:22 +08:00
}
}
if nil != tx {
if err = setNodeAttrsWithTx ( tx , node , tree , attrs ) ; nil != err {
return
}
} else {
if err = setNodeAttrs ( node , tree , attrs ) ; nil != err {
return
}
}
return
}
2023-07-11 22:44:31 +08:00
func ( tx * Transaction ) doSetAttrViewColumnWidth ( operation * Operation ) ( ret * TxErr ) {
err := setAttributeViewColWidth ( operation )
if nil != err {
2023-07-13 00:29:22 +08:00
return & TxErr { code : TxErrWriteAttributeView , id : operation . AvID , msg : err . Error ( ) }
2023-07-11 22:44:31 +08:00
}
return
}
func setAttributeViewColWidth ( operation * Operation ) ( err error ) {
attrView , err := av . ParseAttributeView ( operation . AvID )
if nil != err {
return
}
2024-03-04 16:41:41 +08:00
view , err := getAttrViewViewByBlockID ( attrView , operation . BlockID )
2023-07-11 23:47:17 +08:00
if nil != err {
2023-07-11 23:40:05 +08:00
return
}
2023-07-12 21:39:55 +08:00
switch view . LayoutType {
2023-07-12 19:10:05 +08:00
case av . LayoutTypeTable :
for _ , column := range view . Table . Columns {
if column . ID == operation . ID {
column . Width = operation . Data . ( string )
break
}
2023-07-11 22:44:31 +08:00
}
}
err = av . SaveAttributeView ( attrView )
return
}
func ( tx * Transaction ) doSetAttrViewColumnWrap ( operation * Operation ) ( ret * TxErr ) {
err := setAttributeViewColWrap ( operation )
if nil != err {
2023-07-13 00:29:22 +08:00
return & TxErr { code : TxErrWriteAttributeView , id : operation . AvID , msg : err . Error ( ) }
2023-07-11 22:44:31 +08:00
}
return
}
func setAttributeViewColWrap ( operation * Operation ) ( err error ) {
attrView , err := av . ParseAttributeView ( operation . AvID )
if nil != err {
return
}
2024-03-04 16:41:41 +08:00
view , err := getAttrViewViewByBlockID ( attrView , operation . BlockID )
2023-07-11 23:47:17 +08:00
if nil != err {
2023-07-11 23:40:05 +08:00
return
}
2023-07-12 21:39:55 +08:00
switch view . LayoutType {
2023-07-12 19:10:05 +08:00
case av . LayoutTypeTable :
for _ , column := range view . Table . Columns {
if column . ID == operation . ID {
column . Wrap = operation . Data . ( bool )
break
}
2023-07-11 22:44:31 +08:00
}
}
err = av . SaveAttributeView ( attrView )
return
}
func ( tx * Transaction ) doSetAttrViewColumnHidden ( operation * Operation ) ( ret * TxErr ) {
err := setAttributeViewColHidden ( operation )
if nil != err {
2023-07-13 00:29:22 +08:00
return & TxErr { code : TxErrWriteAttributeView , id : operation . AvID , msg : err . Error ( ) }
2023-07-11 22:44:31 +08:00
}
return
}
func setAttributeViewColHidden ( operation * Operation ) ( err error ) {
attrView , err := av . ParseAttributeView ( operation . AvID )
if nil != err {
return
}
2024-03-04 16:41:41 +08:00
view , err := getAttrViewViewByBlockID ( attrView , operation . BlockID )
2023-07-11 23:47:17 +08:00
if nil != err {
2023-07-11 23:40:05 +08:00
return
}
2023-07-12 21:39:55 +08:00
switch view . LayoutType {
2023-07-12 19:10:05 +08:00
case av . LayoutTypeTable :
for _ , column := range view . Table . Columns {
if column . ID == operation . ID {
column . Hidden = operation . Data . ( bool )
break
}
2023-07-11 22:44:31 +08:00
}
}
err = av . SaveAttributeView ( attrView )
return
}
2023-11-10 10:22:19 +08:00
func ( tx * Transaction ) doSetAttrViewColumnPin ( operation * Operation ) ( ret * TxErr ) {
err := setAttributeViewColPin ( operation )
if nil != err {
return & TxErr { code : TxErrWriteAttributeView , id : operation . AvID , msg : err . Error ( ) }
}
return
}
func setAttributeViewColPin ( operation * Operation ) ( err error ) {
attrView , err := av . ParseAttributeView ( operation . AvID )
if nil != err {
return
}
2024-03-04 16:41:41 +08:00
view , err := getAttrViewViewByBlockID ( attrView , operation . BlockID )
2023-11-10 10:22:19 +08:00
if nil != err {
return
}
switch view . LayoutType {
case av . LayoutTypeTable :
for _ , column := range view . Table . Columns {
if column . ID == operation . ID {
column . Pin = operation . Data . ( bool )
break
}
}
}
err = av . SaveAttributeView ( attrView )
return
}
2023-10-05 11:12:57 +08:00
func ( tx * Transaction ) doSetAttrViewColumnIcon ( operation * Operation ) ( ret * TxErr ) {
err := setAttributeViewColIcon ( operation )
if nil != err {
return & TxErr { code : TxErrWriteAttributeView , id : operation . AvID , msg : err . Error ( ) }
}
return
}
func setAttributeViewColIcon ( operation * Operation ) ( err error ) {
attrView , err := av . ParseAttributeView ( operation . AvID )
if nil != err {
return
}
for _ , keyValues := range attrView . KeyValues {
if keyValues . Key . ID == operation . ID {
keyValues . Key . Icon = operation . Data . ( string )
break
}
}
err = av . SaveAttributeView ( attrView )
return
}
2023-07-11 22:47:19 +08:00
func ( tx * Transaction ) doSortAttrViewRow ( operation * Operation ) ( ret * TxErr ) {
err := sortAttributeViewRow ( operation )
if nil != err {
2023-07-13 00:29:22 +08:00
return & TxErr { code : TxErrWriteAttributeView , id : operation . AvID , msg : err . Error ( ) }
2023-07-11 22:47:19 +08:00
}
return
}
func sortAttributeViewRow ( operation * Operation ) ( err error ) {
attrView , err := av . ParseAttributeView ( operation . AvID )
if nil != err {
return
}
2024-03-04 16:41:41 +08:00
view , err := getAttrViewViewByBlockID ( attrView , operation . BlockID )
2023-07-12 10:35:17 +08:00
if nil != err {
return
}
var rowID string
2023-07-11 22:47:19 +08:00
var index , previousIndex int
2023-07-12 10:35:17 +08:00
for i , r := range view . Table . RowIDs {
if r == operation . ID {
rowID = r
2023-07-11 22:47:19 +08:00
index = i
break
}
}
2023-07-12 10:35:17 +08:00
if "" == rowID {
2023-12-14 10:49:14 +08:00
rowID = operation . ID
view . Table . RowIDs = append ( view . Table . RowIDs , rowID )
index = len ( view . Table . RowIDs ) - 1
2023-07-11 22:47:19 +08:00
}
2023-07-12 21:39:55 +08:00
switch view . LayoutType {
2023-07-12 19:10:05 +08:00
case av . LayoutTypeTable :
view . Table . RowIDs = append ( view . Table . RowIDs [ : index ] , view . Table . RowIDs [ index + 1 : ] ... )
for i , r := range view . Table . RowIDs {
if r == operation . PreviousID {
previousIndex = i + 1
break
}
2023-07-11 22:47:19 +08:00
}
2023-07-12 19:10:05 +08:00
view . Table . RowIDs = util . InsertElem ( view . Table . RowIDs , previousIndex , rowID )
2023-07-11 22:47:19 +08:00
}
err = av . SaveAttributeView ( attrView )
return
}
func ( tx * Transaction ) doSortAttrViewColumn ( operation * Operation ) ( ret * TxErr ) {
2024-03-04 16:41:41 +08:00
err := SortAttributeViewKey ( operation . AvID , operation . BlockID , operation . ID , operation . PreviousID )
2023-07-11 22:47:19 +08:00
if nil != err {
2023-07-13 00:29:22 +08:00
return & TxErr { code : TxErrWriteAttributeView , id : operation . AvID , msg : err . Error ( ) }
2023-07-11 22:47:19 +08:00
}
return
}
2024-03-04 16:41:41 +08:00
func SortAttributeViewKey ( avID , blockID , keyID , previousKeyID string ) ( err error ) {
2024-01-12 21:24:55 +08:00
attrView , err := av . ParseAttributeView ( avID )
2023-07-11 22:47:19 +08:00
if nil != err {
return
}
2024-03-04 16:41:41 +08:00
view , err := getAttrViewViewByBlockID ( attrView , blockID )
2023-07-12 10:35:17 +08:00
if nil != err {
return
}
2023-07-12 21:39:55 +08:00
switch view . LayoutType {
2023-07-12 19:10:05 +08:00
case av . LayoutTypeTable :
var col * av . ViewTableColumn
var index , previousIndex int
for i , column := range view . Table . Columns {
2024-01-12 21:24:55 +08:00
if column . ID == keyID {
2023-07-12 19:10:05 +08:00
col = column
index = i
break
}
2023-07-11 22:47:19 +08:00
}
2023-07-12 19:10:05 +08:00
if nil == col {
return
2023-07-11 22:47:19 +08:00
}
2023-07-12 19:10:05 +08:00
view . Table . Columns = append ( view . Table . Columns [ : index ] , view . Table . Columns [ index + 1 : ] ... )
for i , column := range view . Table . Columns {
2024-01-12 21:24:55 +08:00
if column . ID == previousKeyID {
2023-07-12 19:10:05 +08:00
previousIndex = i + 1
break
}
}
view . Table . Columns = util . InsertElem ( view . Table . Columns , previousIndex , col )
2023-07-11 22:47:19 +08:00
}
err = av . SaveAttributeView ( attrView )
return
}
2023-07-11 23:40:05 +08:00
func ( tx * Transaction ) doAddAttrViewColumn ( operation * Operation ) ( ret * TxErr ) {
2024-01-12 21:24:55 +08:00
var icon string
if nil != operation . Data {
icon = operation . Data . ( string )
}
err := AddAttributeViewKey ( operation . AvID , operation . ID , operation . Name , operation . Typ , icon , operation . PreviousID )
2023-07-11 23:40:05 +08:00
if nil != err {
2023-07-13 00:29:22 +08:00
return & TxErr { code : TxErrWriteAttributeView , id : operation . AvID , msg : err . Error ( ) }
2023-07-11 23:40:05 +08:00
}
return
}
2024-01-12 21:24:55 +08:00
func AddAttributeViewKey ( avID , keyID , keyName , keyType , keyIcon , previousKeyID string ) ( err error ) {
attrView , err := av . ParseAttributeView ( avID )
2023-07-11 23:40:05 +08:00
if nil != err {
return
}
2024-01-12 21:24:55 +08:00
keyTyp := av . KeyType ( keyType )
switch keyTyp {
2023-12-15 20:05:14 +08:00
case av . KeyTypeText , av . KeyTypeNumber , av . KeyTypeDate , av . KeyTypeSelect , av . KeyTypeMSelect , av . KeyTypeURL , av . KeyTypeEmail ,
av . KeyTypePhone , av . KeyTypeMAsset , av . KeyTypeTemplate , av . KeyTypeCreated , av . KeyTypeUpdated , av . KeyTypeCheckbox ,
av . KeyTypeRelation , av . KeyTypeRollup :
2024-01-12 21:24:55 +08:00
key := av . NewKey ( keyID , keyName , keyIcon , keyTyp )
if av . KeyTypeRollup == keyTyp {
2024-01-01 21:17:36 +08:00
key . Rollup = & av . Rollup { Calc : & av . RollupCalc { Operator : av . CalcOperatorNone } }
}
2023-07-12 19:10:05 +08:00
attrView . KeyValues = append ( attrView . KeyValues , & av . KeyValues { Key : key } )
2023-07-11 23:40:05 +08:00
2023-12-29 09:11:40 +08:00
for _ , view := range attrView . Views {
switch view . LayoutType {
2023-12-01 20:15:14 +08:00
case av . LayoutTypeTable :
2024-01-12 21:24:55 +08:00
if "" == previousKeyID {
2023-12-29 10:09:21 +08:00
view . Table . Columns = append ( [ ] * av . ViewTableColumn { { ID : key . ID } } , view . Table . Columns ... )
break
}
2023-12-29 09:11:40 +08:00
added := false
for i , column := range view . Table . Columns {
2024-01-12 21:24:55 +08:00
if column . ID == previousKeyID {
2023-12-29 09:11:40 +08:00
view . Table . Columns = append ( view . Table . Columns [ : i + 1 ] , append ( [ ] * av . ViewTableColumn { { ID : key . ID } } , view . Table . Columns [ i + 1 : ] ... ) ... )
added = true
break
}
}
if ! added {
view . Table . Columns = append ( view . Table . Columns , & av . ViewTableColumn { ID : key . ID } )
}
2023-12-01 20:15:14 +08:00
}
2023-07-11 23:40:05 +08:00
}
}
err = av . SaveAttributeView ( attrView )
return
}
2023-10-01 17:33:53 +08:00
func ( tx * Transaction ) doUpdateAttrViewColTemplate ( operation * Operation ) ( ret * TxErr ) {
err := updateAttributeViewColTemplate ( operation )
if nil != err {
return & TxErr { code : TxErrWriteAttributeView , id : operation . AvID , msg : err . Error ( ) }
}
return
}
func updateAttributeViewColTemplate ( operation * Operation ) ( err error ) {
attrView , err := av . ParseAttributeView ( operation . AvID )
if nil != err {
return
}
colType := av . KeyType ( operation . Typ )
switch colType {
case av . KeyTypeTemplate :
for _ , keyValues := range attrView . KeyValues {
if keyValues . Key . ID == operation . ID && av . KeyTypeTemplate == keyValues . Key . Type {
keyValues . Key . Template = operation . Data . ( string )
break
}
}
}
err = av . SaveAttributeView ( attrView )
return
}
2023-08-03 23:38:45 +08:00
func ( tx * Transaction ) doUpdateAttrViewColNumberFormat ( operation * Operation ) ( ret * TxErr ) {
err := updateAttributeViewColNumberFormat ( operation )
if nil != err {
return & TxErr { code : TxErrWriteAttributeView , id : operation . AvID , msg : err . Error ( ) }
}
return
}
func updateAttributeViewColNumberFormat ( operation * Operation ) ( err error ) {
attrView , err := av . ParseAttributeView ( operation . AvID )
if nil != err {
return
}
colType := av . KeyType ( operation . Typ )
switch colType {
case av . KeyTypeNumber :
for _ , keyValues := range attrView . KeyValues {
if keyValues . Key . ID == operation . ID && av . KeyTypeNumber == keyValues . Key . Type {
keyValues . Key . NumberFormat = av . NumberFormat ( operation . Format )
break
}
}
}
err = av . SaveAttributeView ( attrView )
return
}
2023-07-11 23:47:17 +08:00
func ( tx * Transaction ) doUpdateAttrViewColumn ( operation * Operation ) ( ret * TxErr ) {
err := updateAttributeViewColumn ( operation )
if nil != err {
2023-07-13 00:29:22 +08:00
return & TxErr { code : TxErrWriteAttributeView , id : operation . AvID , msg : err . Error ( ) }
2023-07-11 23:47:17 +08:00
}
return
}
func updateAttributeViewColumn ( operation * Operation ) ( err error ) {
attrView , err := av . ParseAttributeView ( operation . AvID )
if nil != err {
return
}
2023-07-12 19:10:05 +08:00
colType := av . KeyType ( operation . Typ )
2023-07-11 23:47:17 +08:00
switch colType {
2023-12-15 20:05:14 +08:00
case av . KeyTypeBlock , av . KeyTypeText , av . KeyTypeNumber , av . KeyTypeDate , av . KeyTypeSelect , av . KeyTypeMSelect , av . KeyTypeURL , av . KeyTypeEmail ,
av . KeyTypePhone , av . KeyTypeMAsset , av . KeyTypeTemplate , av . KeyTypeCreated , av . KeyTypeUpdated , av . KeyTypeCheckbox ,
av . KeyTypeRelation , av . KeyTypeRollup :
2023-07-12 19:10:05 +08:00
for _ , keyValues := range attrView . KeyValues {
if keyValues . Key . ID == operation . ID {
2023-12-01 11:47:20 +08:00
keyValues . Key . Name = strings . TrimSpace ( operation . Name )
2023-07-12 19:10:05 +08:00
keyValues . Key . Type = colType
2023-07-11 23:47:17 +08:00
break
}
}
}
err = av . SaveAttributeView ( attrView )
return
}
2023-07-12 00:02:40 +08:00
func ( tx * Transaction ) doRemoveAttrViewColumn ( operation * Operation ) ( ret * TxErr ) {
2024-01-12 21:24:55 +08:00
err := RemoveAttributeViewKey ( operation . AvID , operation . ID )
2023-07-12 00:02:40 +08:00
if nil != err {
2023-07-13 00:29:22 +08:00
return & TxErr { code : TxErrWriteAttributeView , id : operation . AvID , msg : err . Error ( ) }
2023-07-12 00:02:40 +08:00
}
return
}
2024-01-12 21:24:55 +08:00
func RemoveAttributeViewKey ( avID , keyID string ) ( err error ) {
attrView , err := av . ParseAttributeView ( avID )
2023-07-12 00:02:40 +08:00
if nil != err {
return
}
2023-12-24 14:19:00 +08:00
var removedKey * av . Key
2023-07-12 19:10:05 +08:00
for i , keyValues := range attrView . KeyValues {
2024-01-12 21:24:55 +08:00
if keyValues . Key . ID == keyID {
2023-07-12 19:10:05 +08:00
attrView . KeyValues = append ( attrView . KeyValues [ : i ] , attrView . KeyValues [ i + 1 : ] ... )
2023-12-24 14:19:00 +08:00
removedKey = keyValues . Key
2023-07-12 00:02:40 +08:00
break
}
}
2023-12-31 10:30:39 +08:00
if nil != removedKey && av . KeyTypeRelation == removedKey . Type && nil != removedKey . Relation {
if removedKey . Relation . IsTwoWay {
// 删除双向关联的目标列
destAv , _ := av . ParseAttributeView ( removedKey . Relation . AvID )
if nil != destAv {
destAvRelSrcAv := false
for i , keyValues := range destAv . KeyValues {
if keyValues . Key . ID == removedKey . Relation . BackKeyID {
destAv . KeyValues = append ( destAv . KeyValues [ : i ] , destAv . KeyValues [ i + 1 : ] ... )
continue
}
if av . KeyTypeRelation == keyValues . Key . Type && keyValues . Key . Relation . AvID == attrView . ID {
destAvRelSrcAv = true
}
2023-12-24 14:19:00 +08:00
}
2023-12-31 10:30:39 +08:00
for _ , view := range destAv . Views {
switch view . LayoutType {
case av . LayoutTypeTable :
for i , column := range view . Table . Columns {
if column . ID == removedKey . Relation . BackKeyID {
view . Table . Columns = append ( view . Table . Columns [ : i ] , view . Table . Columns [ i + 1 : ] ... )
break
}
2023-12-24 14:19:00 +08:00
}
}
}
2023-12-31 10:30:39 +08:00
av . SaveAttributeView ( destAv )
util . BroadcastByType ( "protyle" , "refreshAttributeView" , 0 , "" , map [ string ] interface { } { "id" : destAv . ID } )
if ! destAvRelSrcAv {
av . RemoveAvRel ( destAv . ID , attrView . ID )
}
2023-12-24 14:19:00 +08:00
}
2023-12-24 14:24:42 +08:00
2023-12-31 10:30:39 +08:00
srcAvRelDestAv := false
for _ , keyValues := range attrView . KeyValues {
2024-01-09 11:18:44 +08:00
if av . KeyTypeRelation == keyValues . Key . Type && nil != keyValues . Key . Relation && keyValues . Key . Relation . AvID == removedKey . Relation . AvID {
2023-12-31 10:30:39 +08:00
srcAvRelDestAv = true
}
}
if ! srcAvRelDestAv {
av . RemoveAvRel ( attrView . ID , removedKey . Relation . AvID )
}
2023-12-24 14:19:00 +08:00
}
}
2023-07-12 00:02:40 +08:00
for _ , view := range attrView . Views {
2023-07-12 21:39:55 +08:00
switch view . LayoutType {
2023-07-12 19:10:05 +08:00
case av . LayoutTypeTable :
for i , column := range view . Table . Columns {
2024-01-12 21:24:55 +08:00
if column . ID == keyID {
2023-07-12 19:10:05 +08:00
view . Table . Columns = append ( view . Table . Columns [ : i ] , view . Table . Columns [ i + 1 : ] ... )
break
}
2023-07-12 00:02:40 +08:00
}
}
}
err = av . SaveAttributeView ( attrView )
return
}
2023-09-28 10:50:35 +08:00
func ( tx * Transaction ) doReplaceAttrViewBlock ( operation * Operation ) ( ret * TxErr ) {
2023-09-28 12:48:09 +08:00
err := replaceAttributeViewBlock ( operation , tx )
2023-09-28 10:50:35 +08:00
if nil != err {
return & TxErr { code : TxErrWriteAttributeView , id : operation . AvID }
}
return
}
2023-09-28 12:48:09 +08:00
func replaceAttributeViewBlock ( operation * Operation , tx * Transaction ) ( err error ) {
2023-09-28 10:50:35 +08:00
attrView , err := av . ParseAttributeView ( operation . AvID )
if nil != err {
return
}
2023-09-28 12:48:09 +08:00
var node * ast . Node
if ! operation . IsDetached {
node , _ , _ = getNodeByBlockID ( tx , operation . NextID )
}
2024-04-10 22:58:59 +08:00
// 检查是否已经存在绑定块
// Improve database primary key binding block https://github.com/siyuan-note/siyuan/issues/10945
for _ , keyValues := range attrView . KeyValues {
for _ , value := range keyValues . Values {
if value . BlockID == operation . NextID {
util . PushMsg ( Conf . language ( 242 ) , 3000 )
return
}
}
}
2023-09-28 10:50:35 +08:00
for _ , keyValues := range attrView . KeyValues {
for _ , value := range keyValues . Values {
if value . BlockID == operation . PreviousID {
2023-12-18 13:11:56 +08:00
if value . BlockID != operation . NextID {
// 换绑
unbindBlockAv ( tx , operation . AvID , value . BlockID )
}
2023-09-28 10:50:35 +08:00
value . BlockID = operation . NextID
if nil != value . Block {
value . Block . ID = operation . NextID
value . IsDetached = operation . IsDetached
2023-09-28 12:48:09 +08:00
if ! operation . IsDetached {
value . Block . Content = getNodeRefText ( node )
}
2023-09-28 10:50:35 +08:00
}
2023-09-28 11:17:49 +08:00
if ! operation . IsDetached {
2023-09-28 12:48:09 +08:00
bindBlockAv ( tx , operation . AvID , operation . NextID )
2023-09-28 11:17:49 +08:00
}
2023-09-28 10:50:35 +08:00
}
}
}
2023-12-14 00:14:31 +08:00
replacedRowID := false
2023-09-28 10:50:35 +08:00
for _ , v := range attrView . Views {
switch v . LayoutType {
case av . LayoutTypeTable :
for i , rowID := range v . Table . RowIDs {
if rowID == operation . PreviousID {
v . Table . RowIDs [ i ] = operation . NextID
2023-12-14 00:14:31 +08:00
replacedRowID = true
break
2023-09-28 10:50:35 +08:00
}
}
2023-12-14 00:14:31 +08:00
if ! replacedRowID {
v . Table . RowIDs = append ( v . Table . RowIDs , operation . NextID )
}
2023-09-28 10:50:35 +08:00
}
}
err = av . SaveAttributeView ( attrView )
return
}
2023-06-10 15:00:04 +08:00
func ( tx * Transaction ) doUpdateAttrViewCell ( operation * Operation ) ( ret * TxErr ) {
2023-07-12 19:10:05 +08:00
err := updateAttributeViewCell ( operation , tx )
2023-07-03 15:29:54 +08:00
if nil != err {
2023-07-13 00:29:22 +08:00
return & TxErr { code : TxErrWriteAttributeView , id : operation . AvID , msg : err . Error ( ) }
2023-07-03 15:29:54 +08:00
}
return
}
2023-07-12 19:10:05 +08:00
func updateAttributeViewCell ( operation * Operation , tx * Transaction ) ( err error ) {
2023-09-28 12:48:09 +08:00
err = UpdateAttributeViewCell ( tx , operation . AvID , operation . KeyID , operation . RowID , operation . ID , operation . Data )
2023-07-31 23:17:31 +08:00
return
}
2023-09-28 12:48:09 +08:00
func UpdateAttributeViewCell ( tx * Transaction , avID , keyID , rowID , cellID string , valueData interface { } ) ( err error ) {
2023-07-31 23:17:31 +08:00
attrView , err := av . ParseAttributeView ( avID )
2023-07-10 10:57:18 +08:00
if nil != err {
return
}
2023-12-18 12:53:53 +08:00
var blockVal * av . Value
for _ , kv := range attrView . KeyValues {
if av . KeyTypeBlock == kv . Key . Type {
for _ , v := range kv . Values {
if rowID == v . Block . ID {
blockVal = v
break
}
}
break
}
}
2024-03-07 09:45:27 +08:00
now := time . Now ( ) . UnixMilli ( )
2023-07-12 19:10:05 +08:00
var val * av . Value
2023-12-18 21:13:53 +08:00
oldIsDetached := true
if nil != blockVal {
oldIsDetached = blockVal . IsDetached
}
2023-07-12 19:10:05 +08:00
for _ , keyValues := range attrView . KeyValues {
2023-07-31 23:17:31 +08:00
if keyID != keyValues . Key . ID {
2023-07-13 00:49:09 +08:00
continue
}
for _ , value := range keyValues . Values {
2024-04-14 09:51:54 +08:00
if cellID == value . ID || rowID == value . BlockID {
2023-07-13 00:49:09 +08:00
val = value
2023-07-13 10:44:47 +08:00
val . Type = keyValues . Key . Type
2023-07-13 00:49:09 +08:00
break
2023-07-10 10:57:18 +08:00
}
}
2023-07-13 00:49:09 +08:00
if nil == val {
2024-03-07 09:45:27 +08:00
val = & av . Value { ID : cellID , KeyID : keyValues . Key . ID , BlockID : rowID , Type : keyValues . Key . Type , CreatedAt : now , UpdatedAt : now }
2023-07-13 00:49:09 +08:00
keyValues . Values = append ( keyValues . Values , val )
}
break
2023-07-10 10:39:54 +08:00
}
2023-12-18 13:02:54 +08:00
isUpdatingBlockKey := av . KeyTypeBlock == val . Type
2023-12-18 10:55:10 +08:00
oldBoundBlockID := val . BlockID
2023-12-24 23:25:06 +08:00
var oldRelationBlockIDs [ ] string
2023-12-24 23:18:35 +08:00
if av . KeyTypeRelation == val . Type {
2023-12-25 00:33:40 +08:00
if nil != val . Relation {
for _ , bID := range val . Relation . BlockIDs {
oldRelationBlockIDs = append ( oldRelationBlockIDs , bID )
}
2023-12-24 23:34:30 +08:00
}
2023-12-24 23:18:35 +08:00
}
2023-07-31 23:18:07 +08:00
data , err := gulu . JSON . MarshalJSON ( valueData )
2023-07-03 15:29:54 +08:00
if nil != err {
return
}
2023-07-12 19:10:05 +08:00
if err = gulu . JSON . UnmarshalJSON ( data , & val ) ; nil != err {
2023-07-03 15:29:54 +08:00
return
}
2024-01-25 19:26:43 +08:00
if av . KeyTypeNumber == val . Type {
if nil != val . Number && ! val . Number . IsNotEmpty {
val . Number . Content = 0
2024-03-01 10:08:56 +08:00
val . Number . FormattedContent = ""
2024-01-25 19:26:43 +08:00
}
2024-03-01 10:33:25 +08:00
} else if av . KeyTypeDate == val . Type {
if nil != val . Date && ! val . Date . IsNotEmpty {
val . Date . Content = 0
val . Date . FormattedContent = ""
}
2024-01-25 19:26:43 +08:00
}
2023-12-25 00:30:49 +08:00
relationChangeMode := 0 // 0: 不变( 仅排序) , 1: 增加, 2: 减少
2023-12-24 23:18:35 +08:00
if av . KeyTypeRelation == val . Type {
// 关联列得 content 是自动渲染的,所以不需要保存
val . Relation . Contents = nil
2023-12-25 00:30:49 +08:00
2023-12-31 10:58:40 +08:00
// 计算关联变更模式
2023-12-25 00:30:49 +08:00
if len ( oldRelationBlockIDs ) == len ( val . Relation . BlockIDs ) {
relationChangeMode = 0
} else {
if len ( oldRelationBlockIDs ) > len ( val . Relation . BlockIDs ) {
relationChangeMode = 2
} else {
relationChangeMode = 1
}
}
2023-12-24 23:18:35 +08:00
}
2023-07-03 15:29:54 +08:00
2023-12-18 13:02:54 +08:00
// val.IsDetached 只有更新主键的时候才会传入,所以下面需要结合 isUpdatingBlockKey 来判断
2023-12-18 10:55:10 +08:00
if oldIsDetached { // 之前是游离行
if ! val . IsDetached { // 现在绑定了块
// 将游离行绑定到新建的块上
bindBlockAv ( tx , avID , rowID )
}
2024-04-05 23:04:43 +08:00
} else { // 之前绑定了块
2023-12-18 13:02:54 +08:00
if isUpdatingBlockKey { // 正在更新主键
if val . IsDetached { // 现在是游离行
// 将绑定的块从属性视图中移除
unbindBlockAv ( tx , avID , rowID )
2024-04-05 23:04:43 +08:00
} else { // 现在绑定了块
2023-12-18 13:02:54 +08:00
if oldBoundBlockID != val . BlockID { // 之前绑定的块和现在绑定的块不一样
// 换绑块
unbindBlockAv ( tx , avID , oldBoundBlockID )
bindBlockAv ( tx , avID , val . BlockID )
} else { // 之前绑定的块和现在绑定的块一样
2023-12-18 12:35:48 +08:00
// 直接返回,因为锚文本不允许更改
return
}
2023-12-18 10:55:10 +08:00
}
}
2023-09-28 00:38:56 +08:00
}
2023-12-18 12:53:53 +08:00
if nil != blockVal {
2024-03-03 16:39:34 +08:00
blockVal . Block . Updated = now
2024-04-03 10:54:23 +08:00
blockVal . SetUpdatedAt ( now )
2023-12-18 13:02:54 +08:00
if isUpdatingBlockKey {
blockVal . IsDetached = val . IsDetached
}
2023-10-10 21:55:43 +08:00
}
2024-04-03 10:54:23 +08:00
val . SetUpdatedAt ( now )
2023-10-10 21:55:43 +08:00
2023-12-24 23:02:59 +08:00
key , _ := attrView . GetKey ( val . KeyID )
2023-12-31 10:58:40 +08:00
if nil != key && av . KeyTypeRelation == key . Type && nil != key . Relation {
2023-12-24 23:02:59 +08:00
destAv , _ := av . ParseAttributeView ( key . Relation . AvID )
if nil != destAv {
2023-12-31 10:58:40 +08:00
if key . Relation . IsTwoWay {
// relationChangeMode
// 0: 关联列值不变( 仅排序) , 不影响目标值
// 1: 关联列值增加, 增加目标值
// 2: 关联列值减少, 减少目标值
if 1 == relationChangeMode {
addBlockIDs := val . Relation . BlockIDs
for _ , bID := range oldRelationBlockIDs {
addBlockIDs = gulu . Str . RemoveElem ( addBlockIDs , bID )
}
2023-12-24 23:18:35 +08:00
2023-12-31 10:58:40 +08:00
for _ , blockID := range addBlockIDs {
for _ , keyValues := range destAv . KeyValues {
if keyValues . Key . ID != key . Relation . BackKeyID {
continue
}
2023-12-25 00:30:49 +08:00
2023-12-31 10:58:40 +08:00
destVal := keyValues . GetValue ( blockID )
if nil == destVal {
destVal = & av . Value { ID : ast . NewNodeID ( ) , KeyID : keyValues . Key . ID , BlockID : blockID , Type : keyValues . Key . Type , Relation : & av . ValueRelation { } }
keyValues . Values = append ( keyValues . Values , destVal )
}
2023-12-25 00:30:49 +08:00
2023-12-31 10:58:40 +08:00
destVal . Relation . BlockIDs = append ( destVal . Relation . BlockIDs , rowID )
destVal . Relation . BlockIDs = gulu . Str . RemoveDuplicatedElem ( destVal . Relation . BlockIDs )
2024-04-03 10:54:23 +08:00
destVal . SetUpdatedAt ( now )
2023-12-31 10:58:40 +08:00
break
}
}
} else if 2 == relationChangeMode {
removeBlockIDs := oldRelationBlockIDs
for _ , bID := range val . Relation . BlockIDs {
removeBlockIDs = gulu . Str . RemoveElem ( removeBlockIDs , bID )
2023-12-24 23:18:35 +08:00
}
2023-12-31 10:58:40 +08:00
for _ , blockID := range removeBlockIDs {
for _ , keyValues := range destAv . KeyValues {
if keyValues . Key . ID != key . Relation . BackKeyID {
continue
}
2023-12-24 23:02:59 +08:00
2023-12-31 10:58:40 +08:00
for _ , value := range keyValues . Values {
if value . BlockID == blockID {
value . Relation . BlockIDs = gulu . Str . RemoveElem ( value . Relation . BlockIDs , rowID )
2024-04-03 10:54:23 +08:00
value . SetUpdatedAt ( now )
2023-12-31 10:58:40 +08:00
break
}
2023-12-25 00:30:49 +08:00
}
}
2023-12-24 23:02:59 +08:00
}
}
2023-12-31 10:58:40 +08:00
av . SaveAttributeView ( destAv )
}
2023-12-24 23:02:59 +08:00
}
}
2023-12-31 10:58:40 +08:00
relatedAvIDs := av . GetSrcAvIDs ( avID )
for _ , relatedAvID := range relatedAvIDs {
util . BroadcastByType ( "protyle" , "refreshAttributeView" , 0 , "" , map [ string ] interface { } { "id" : relatedAvID } )
}
2023-07-12 19:10:05 +08:00
if err = av . SaveAttributeView ( attrView ) ; nil != err {
2023-07-03 15:29:54 +08:00
return
}
return
}
2023-12-18 10:55:10 +08:00
func unbindBlockAv ( tx * Transaction , avID , blockID string ) {
node , tree , err := getNodeByBlockID ( tx , blockID )
if nil != err {
return
}
attrs := parse . IAL2Map ( node . KramdownIAL )
if "" == attrs [ av . NodeAttrNameAvs ] {
return
}
avIDs := strings . Split ( attrs [ av . NodeAttrNameAvs ] , "," )
avIDs = gulu . Str . RemoveElem ( avIDs , avID )
if 0 == len ( avIDs ) {
delete ( attrs , av . NodeAttrNameAvs )
node . RemoveIALAttr ( av . NodeAttrNameAvs )
} else {
attrs [ av . NodeAttrNameAvs ] = strings . Join ( avIDs , "," )
node . SetIALAttr ( av . NodeAttrNameAvs , strings . Join ( avIDs , "," ) )
}
2024-04-08 09:25:19 +08:00
avNames := getAvNames ( attrs [ av . NodeAttrNameAvs ] )
if "" != avNames {
attrs [ av . NodeAttrViewNames ] = avNames
}
2023-12-18 10:55:10 +08:00
if nil != tx {
err = setNodeAttrsWithTx ( tx , node , tree , attrs )
} else {
err = setNodeAttrs ( node , tree , attrs )
}
if nil != err {
logging . LogWarnf ( "set node [%s] attrs failed: %s" , blockID , err )
return
}
return
}
2023-09-28 12:48:09 +08:00
func bindBlockAv ( tx * Transaction , avID , blockID string ) {
node , tree , err := getNodeByBlockID ( tx , blockID )
if nil != err {
2023-09-28 11:17:49 +08:00
return
}
2024-04-08 09:25:19 +08:00
bindBlockAv0 ( tx , avID , node , tree )
2024-03-28 21:27:31 +08:00
return
}
2024-04-08 09:25:19 +08:00
func bindBlockAv0 ( tx * Transaction , avID string , node * ast . Node , tree * parse . Tree ) {
2023-09-28 11:17:49 +08:00
attrs := parse . IAL2Map ( node . KramdownIAL )
2023-10-05 12:02:17 +08:00
if "" == attrs [ av . NodeAttrNameAvs ] {
attrs [ av . NodeAttrNameAvs ] = avID
2023-09-28 11:17:49 +08:00
} else {
2023-10-05 12:02:17 +08:00
avIDs := strings . Split ( attrs [ av . NodeAttrNameAvs ] , "," )
2023-09-28 11:17:49 +08:00
avIDs = append ( avIDs , avID )
avIDs = gulu . Str . RemoveDuplicatedElem ( avIDs )
2023-10-05 12:02:17 +08:00
attrs [ av . NodeAttrNameAvs ] = strings . Join ( avIDs , "," )
2023-09-28 11:17:49 +08:00
}
2024-03-28 21:27:31 +08:00
avNames := getAvNames ( attrs [ av . NodeAttrNameAvs ] )
if "" != avNames {
attrs [ av . NodeAttrViewNames ] = avNames
}
var err error
2023-09-28 12:48:09 +08:00
if nil != tx {
err = setNodeAttrsWithTx ( tx , node , tree , attrs )
} else {
err = setNodeAttrs ( node , tree , attrs )
}
if nil != err {
2024-04-08 09:25:19 +08:00
logging . LogWarnf ( "set node [%s] attrs failed: %s" , node . ID , err )
2023-09-28 12:48:09 +08:00
return
}
return
}
func getNodeByBlockID ( tx * Transaction , blockID string ) ( node * ast . Node , tree * parse . Tree , err error ) {
if nil != tx {
tree , err = tx . loadTree ( blockID )
} else {
2024-03-10 23:27:13 +08:00
tree , err = LoadTreeByBlockID ( blockID )
2023-09-28 12:48:09 +08:00
}
if nil != err {
return
}
node = treenode . GetNodeInTree ( tree , blockID )
if nil == node {
logging . LogWarnf ( "node [%s] not found in tree [%s]" , blockID , tree . ID )
return
2023-09-28 11:17:49 +08:00
}
return
}
2023-07-13 00:08:10 +08:00
func ( tx * Transaction ) doUpdateAttrViewColOptions ( operation * Operation ) ( ret * TxErr ) {
err := updateAttributeViewColumnOptions ( operation )
if nil != err {
2023-07-13 00:29:22 +08:00
return & TxErr { code : TxErrWriteAttributeView , id : operation . AvID , msg : err . Error ( ) }
2023-07-13 00:08:10 +08:00
}
return
}
2023-07-12 19:10:05 +08:00
2023-07-13 00:08:10 +08:00
func updateAttributeViewColumnOptions ( operation * Operation ) ( err error ) {
attrView , err := av . ParseAttributeView ( operation . AvID )
if nil != err {
return
}
jsonData , err := gulu . JSON . MarshalJSON ( operation . Data )
if nil != err {
return
}
2023-12-23 17:57:45 +08:00
options := [ ] * av . SelectOption { }
2023-07-13 00:08:10 +08:00
if err = gulu . JSON . UnmarshalJSON ( jsonData , & options ) ; nil != err {
return
}
for _ , keyValues := range attrView . KeyValues {
if keyValues . Key . ID == operation . ID {
keyValues . Key . Options = options
err = av . SaveAttributeView ( attrView )
return
}
}
return
}
2023-07-13 00:16:40 +08:00
func ( tx * Transaction ) doRemoveAttrViewColOption ( operation * Operation ) ( ret * TxErr ) {
err := removeAttributeViewColumnOption ( operation )
if nil != err {
2023-07-13 00:29:22 +08:00
return & TxErr { code : TxErrWriteAttributeView , id : operation . AvID , msg : err . Error ( ) }
2023-07-13 00:16:40 +08:00
}
return
}
2023-07-13 00:08:10 +08:00
2023-07-13 00:16:40 +08:00
func removeAttributeViewColumnOption ( operation * Operation ) ( err error ) {
2023-07-13 00:29:22 +08:00
attrView , err := av . ParseAttributeView ( operation . AvID )
2023-07-13 00:16:40 +08:00
if nil != err {
return
}
optName := operation . Data . ( string )
key , err := attrView . GetKey ( operation . ID )
if nil != err {
return
}
for i , opt := range key . Options {
if optName == opt . Name {
key . Options = append ( key . Options [ : i ] , key . Options [ i + 1 : ] ... )
break
}
}
for _ , keyValues := range attrView . KeyValues {
if keyValues . Key . ID != operation . ID {
continue
}
for _ , value := range keyValues . Values {
if nil == value || nil == value . MSelect {
continue
}
for i , opt := range value . MSelect {
if optName == opt . Content {
value . MSelect = append ( value . MSelect [ : i ] , value . MSelect [ i + 1 : ] ... )
break
}
}
}
break
}
err = av . SaveAttributeView ( attrView )
return
}
func ( tx * Transaction ) doUpdateAttrViewColOption ( operation * Operation ) ( ret * TxErr ) {
err := updateAttributeViewColumnOption ( operation )
if nil != err {
2023-07-13 00:29:22 +08:00
return & TxErr { code : TxErrWriteAttributeView , id : operation . AvID , msg : err . Error ( ) }
2023-07-13 00:16:40 +08:00
}
return
}
func updateAttributeViewColumnOption ( operation * Operation ) ( err error ) {
2023-07-13 00:29:22 +08:00
attrView , err := av . ParseAttributeView ( operation . AvID )
2023-07-13 00:16:40 +08:00
if nil != err {
return
}
key , err := attrView . GetKey ( operation . ID )
if nil != err {
return
}
data := operation . Data . ( map [ string ] interface { } )
oldName := data [ "oldName" ] . ( string )
newName := data [ "newName" ] . ( string )
newColor := data [ "newColor" ] . ( string )
for i , opt := range key . Options {
if oldName == opt . Name {
key . Options [ i ] . Name = newName
key . Options [ i ] . Color = newColor
break
}
}
2024-04-06 10:11:23 +08:00
// 如果存在选项对应的值,需要更新值中的选项
2023-07-13 00:16:40 +08:00
for _ , keyValues := range attrView . KeyValues {
if keyValues . Key . ID != operation . ID {
continue
}
for _ , value := range keyValues . Values {
if nil == value || nil == value . MSelect {
continue
}
for i , opt := range value . MSelect {
if oldName == opt . Content {
value . MSelect [ i ] . Content = newName
value . MSelect [ i ] . Color = newColor
break
}
}
}
break
}
2024-04-06 10:11:23 +08:00
// 如果存在选项对应的过滤器,需要更新过滤器中设置的选项值
// Database select field filters follow option editing changes https://github.com/siyuan-note/siyuan/issues/10881
for _ , view := range attrView . Views {
switch view . LayoutType {
case av . LayoutTypeTable :
table := view . Table
for _ , filter := range table . Filters {
if filter . Column != key . ID {
continue
}
if nil != filter . Value && ( av . KeyTypeSelect == filter . Value . Type || av . KeyTypeMSelect == filter . Value . Type ) {
for i , opt := range filter . Value . MSelect {
if oldName == opt . Content {
filter . Value . MSelect [ i ] . Content = newName
filter . Value . MSelect [ i ] . Color = newColor
break
}
}
}
}
}
}
2023-07-13 00:16:40 +08:00
err = av . SaveAttributeView ( attrView )
return
}
2024-03-04 16:41:41 +08:00
func getAttrViewViewByBlockID ( attrView * av . AttributeView , blockID string ) ( ret * av . View , err error ) {
node , _ , _ := getNodeByBlockID ( nil , blockID )
var viewID string
if nil != node {
viewID = node . IALAttr ( av . NodeAttrView )
}
return attrView . GetCurrentView ( viewID )
}