From d4133ec9d917ff7bd35a1f13f622c4805af5750f Mon Sep 17 00:00:00 2001 From: Doug Lauder Date: Wed, 16 Feb 2022 12:08:49 -0500 Subject: [PATCH] fix cardOrder when modifying block ids (#2345) --- server/model/block.go | 112 ----------------------------------- server/model/blockid.go | 126 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+), 112 deletions(-) create mode 100644 server/model/blockid.go diff --git a/server/model/block.go b/server/model/block.go index c3c9623c0..a845ab6b3 100644 --- a/server/model/block.go +++ b/server/model/block.go @@ -2,12 +2,7 @@ package model import ( "encoding/json" - "fmt" "io" - - "github.com/mattermost/mattermost-server/v6/shared/mlog" - - "github.com/mattermost/focalboard/server/utils" ) // Block is the basic data unit @@ -182,110 +177,3 @@ type QueryBlockHistoryOptions struct { Limit uint64 // if non-zero then limit the number of returned records Descending bool // if true then the records are sorted by insert_at in descending order } - -// GenerateBlockIDs generates new IDs for all the blocks of the list, -// keeping consistent any references that other blocks would made to -// the original IDs, so a tree of blocks can get new IDs and maintain -// its shape. -func GenerateBlockIDs(blocks []Block, logger *mlog.Logger) []Block { - blockIDs := map[string]BlockType{} - referenceIDs := map[string]bool{} - for _, block := range blocks { - if _, ok := blockIDs[block.ID]; !ok { - blockIDs[block.ID] = block.Type - } - - if _, ok := referenceIDs[block.RootID]; !ok { - referenceIDs[block.RootID] = true - } - if _, ok := referenceIDs[block.ParentID]; !ok { - referenceIDs[block.ParentID] = true - } - - if _, ok := block.Fields["contentOrder"]; ok { - contentOrder, typeOk := block.Fields["contentOrder"].([]interface{}) - if !typeOk { - logger.Warn( - "type assertion failed for content order when saving reference block IDs", - mlog.String("blockID", block.ID), - mlog.String("actionType", fmt.Sprintf("%T", block.Fields["contentOrder"])), - mlog.String("expectedType", "[]interface{}"), - mlog.String("contentOrder", fmt.Sprintf("%v", block.Fields["contentOrder"])), - ) - continue - } - - for _, blockID := range contentOrder { - switch v := blockID.(type) { - case []interface{}: - for _, columnBlockID := range v { - referenceIDs[columnBlockID.(string)] = true - } - case string: - referenceIDs[v] = true - default: - } - } - } - } - - newIDs := map[string]string{} - for id, blockType := range blockIDs { - for referenceID := range referenceIDs { - if id == referenceID { - newIDs[id] = utils.NewID(BlockType2IDType(blockType)) - continue - } - } - } - - getExistingOrOldID := func(id string) string { - if existingID, ok := newIDs[id]; ok { - return existingID - } - return id - } - - getExistingOrNewID := func(id string) string { - if existingID, ok := newIDs[id]; ok { - return existingID - } - return utils.NewID(BlockType2IDType(blockIDs[id])) - } - - newBlocks := make([]Block, len(blocks)) - for i, block := range blocks { - block.ID = getExistingOrNewID(block.ID) - block.RootID = getExistingOrOldID(block.RootID) - block.ParentID = getExistingOrOldID(block.ParentID) - - if _, ok := block.Fields["contentOrder"]; ok { - contentOrder, typeOk := block.Fields["contentOrder"].([]interface{}) - if !typeOk { - logger.Warn( - "type assertion failed for content order when setting new block IDs", - mlog.String("blockID", block.ID), - mlog.String("actionType", fmt.Sprintf("%T", block.Fields["contentOrder"])), - mlog.String("expectedType", "[]interface{}"), - mlog.String("contentOrder", fmt.Sprintf("%v", block.Fields["contentOrder"])), - ) - } else { - for j := range contentOrder { - switch v := contentOrder[j].(type) { - case string: - contentOrder[j] = getExistingOrOldID(v) - case []interface{}: - subOrder := contentOrder[j].([]interface{}) - for k := range v { - subOrder[k] = getExistingOrOldID(v[k].(string)) - } - } - } - } - } - - newBlocks[i] = block - } - - return newBlocks -} diff --git a/server/model/blockid.go b/server/model/blockid.go new file mode 100644 index 000000000..d7b92c116 --- /dev/null +++ b/server/model/blockid.go @@ -0,0 +1,126 @@ +package model + +import ( + "fmt" + + "github.com/mattermost/focalboard/server/utils" + + "github.com/mattermost/mattermost-server/v6/shared/mlog" +) + +// GenerateBlockIDs generates new IDs for all the blocks of the list, +// keeping consistent any references that other blocks would made to +// the original IDs, so a tree of blocks can get new IDs and maintain +// its shape. +func GenerateBlockIDs(blocks []Block, logger *mlog.Logger) []Block { + blockIDs := map[string]BlockType{} + referenceIDs := map[string]bool{} + for _, block := range blocks { + if _, ok := blockIDs[block.ID]; !ok { + blockIDs[block.ID] = block.Type + } + + if _, ok := referenceIDs[block.RootID]; !ok { + referenceIDs[block.RootID] = true + } + if _, ok := referenceIDs[block.ParentID]; !ok { + referenceIDs[block.ParentID] = true + } + + if _, ok := block.Fields["contentOrder"]; ok { + contentOrder, typeOk := block.Fields["contentOrder"].([]interface{}) + if !typeOk { + logger.Warn( + "type assertion failed for content order when saving reference block IDs", + mlog.String("blockID", block.ID), + mlog.String("actionType", fmt.Sprintf("%T", block.Fields["contentOrder"])), + mlog.String("expectedType", "[]interface{}"), + mlog.String("contentOrder", fmt.Sprintf("%v", block.Fields["contentOrder"])), + ) + continue + } + + for _, blockID := range contentOrder { + switch v := blockID.(type) { + case []interface{}: + for _, columnBlockID := range v { + referenceIDs[columnBlockID.(string)] = true + } + case string: + referenceIDs[v] = true + default: + } + } + } + } + + newIDs := map[string]string{} + for id, blockType := range blockIDs { + for referenceID := range referenceIDs { + if id == referenceID { + newIDs[id] = utils.NewID(BlockType2IDType(blockType)) + continue + } + } + } + + getExistingOrOldID := func(id string) string { + if existingID, ok := newIDs[id]; ok { + return existingID + } + return id + } + + getExistingOrNewID := func(id string) string { + if existingID, ok := newIDs[id]; ok { + return existingID + } + return utils.NewID(BlockType2IDType(blockIDs[id])) + } + + newBlocks := make([]Block, len(blocks)) + for i, block := range blocks { + block.ID = getExistingOrNewID(block.ID) + block.RootID = getExistingOrOldID(block.RootID) + block.ParentID = getExistingOrOldID(block.ParentID) + + blockMod := block + if _, ok := blockMod.Fields["contentOrder"]; ok { + fixFieldIDs(&blockMod, "contentOrder", getExistingOrOldID, logger) + } + + if _, ok := blockMod.Fields["cardOrder"]; ok { + fixFieldIDs(&blockMod, "cardOrder", getExistingOrOldID, logger) + } + + newBlocks[i] = blockMod + } + + return newBlocks +} + +func fixFieldIDs(block *Block, fieldName string, getExistingOrOldID func(string) string, logger *mlog.Logger) { + field, typeOk := block.Fields[fieldName].([]interface{}) + if !typeOk { + logger.Warn( + "type assertion failed for JSON field when setting new block IDs", + mlog.String("blockID", block.ID), + mlog.String("fieldName", fieldName), + mlog.String("actionType", fmt.Sprintf("%T", block.Fields[fieldName])), + mlog.String("expectedType", "[]interface{}"), + mlog.String("value", fmt.Sprintf("%v", block.Fields[fieldName])), + ) + } else { + for j := range field { + switch v := field[j].(type) { + case string: + field[j] = getExistingOrOldID(v) + case []interface{}: + subOrder := field[j].([]interface{}) + for k := range v { + subOrder[k] = getExistingOrOldID(v[k].(string)) + } + } + } + } +}