From 870c56e6b337e1bd172fb0bcc1e67b3e3b414679 Mon Sep 17 00:00:00 2001 From: Doug Lauder Date: Fri, 4 Feb 2022 17:12:28 -0500 Subject: [PATCH] Copy images when duplicating card/board (#2253) * copy image for card/board dup * revert store changes * revert store changes --- server/api/api.go | 9 ++++--- server/app/blocks.go | 30 ++++++++++++++++++------ server/services/store/sqlstore/blocks.go | 7 ++++-- webapp/src/mutator.ts | 2 ++ 4 files changed, 34 insertions(+), 14 deletions(-) diff --git a/server/api/api.go b/server/api/api.go index 89b19b1b8..991068d4a 100644 --- a/server/api/api.go +++ b/server/api/api.go @@ -297,7 +297,7 @@ func (a *API) handleGetBlocks(w http.ResponseWriter, r *http.Request) { return } case blockID != "": - block, err = a.app.GetBlockWithID(*container, blockID) + block, err = a.app.GetBlockByID(*container, blockID) if err != nil { a.errorResponse(w, r.URL.Path, http.StatusInternalServerError, "", err) return @@ -440,11 +440,10 @@ func (a *API) handlePostBlocks(w http.ResponseWriter, r *http.Request) { ctx := r.Context() session := ctx.Value(sessionContextKey).(*model.Session) - // this query param exists only when creating - // template from board + // this query param exists when creating template from board, or board from template sourceBoardID := r.URL.Query().Get("sourceBoardID") if sourceBoardID != "" { - if updateFileIDsErr := a.app.CopyCardFiles(sourceBoardID, blocks); updateFileIDsErr != nil { + if updateFileIDsErr := a.app.CopyCardFiles(sourceBoardID, container.WorkspaceID, blocks); updateFileIDsErr != nil { a.errorResponse(w, r.URL.Path, http.StatusInternalServerError, "", updateFileIDsErr) return } @@ -1460,7 +1459,7 @@ func (a *API) handleCreateSubscription(w http.ResponseWriter, r *http.Request) { } // check for valid block - block, err := a.app.GetBlockWithID(*container, sub.BlockID) + block, err := a.app.GetBlockByID(*container, sub.BlockID) if err != nil || block == nil { a.errorResponse(w, r.URL.Path, http.StatusBadRequest, "invalid blockID", err) return diff --git a/server/app/blocks.go b/server/app/blocks.go index 30bc584da..0905f2fc5 100644 --- a/server/app/blocks.go +++ b/server/app/blocks.go @@ -1,11 +1,13 @@ package app import ( + "fmt" "path/filepath" "github.com/mattermost/focalboard/server/model" "github.com/mattermost/focalboard/server/services/notify" "github.com/mattermost/focalboard/server/services/store" + "github.com/mattermost/focalboard/server/utils" "github.com/mattermost/mattermost-server/v6/shared/mlog" ) @@ -22,10 +24,6 @@ func (a *App) GetBlocks(c store.Container, parentID string, blockType string) ([ return a.store.GetBlocksWithParent(c, parentID) } -func (a *App) GetBlockWithID(c store.Container, blockID string) (*model.Block, error) { - return a.store.GetBlock(c, blockID) -} - func (a *App) GetBlocksWithRootID(c store.Container, rootID string) ([]model.Block, error) { return a.store.GetBlocksWithRootID(c, rootID) } @@ -133,20 +131,37 @@ func (a *App) InsertBlocks(c store.Container, blocks []model.Block, modifiedByID return blocks, nil } -func (a *App) CopyCardFiles(sourceBoardID string, blocks []model.Block) error { +func (a *App) CopyCardFiles(sourceBoardID string, destWorkspaceID string, blocks []model.Block) error { // Images attached in cards have a path comprising the card's board ID. // When we create a template from this board, we need to copy the files // with the new board ID in path. // Not doing so causing images in templates (and boards created from this // template) to fail to load. + // look up ID of source board, which may be different than the blocks. + board, err := a.GetBlockByID(store.Container{}, sourceBoardID) + if err != nil || board == nil { + return fmt.Errorf("cannot fetch board %s for CopyCardFiles: %w", sourceBoardID, err) + } + for i := range blocks { block := blocks[i] fileName, ok := block.Fields["fileId"] if block.Type == model.TypeImage && ok { - sourceFilePath := filepath.Join(block.WorkspaceID, sourceBoardID, fileName.(string)) - destinationFilePath := filepath.Join(block.WorkspaceID, block.RootID, fileName.(string)) + // create unique filename in case we are copying cards within the same board. + ext := filepath.Ext(fileName.(string)) + destFilename := utils.NewID(utils.IDTypeNone) + ext + + sourceFilePath := filepath.Join(board.WorkspaceID, sourceBoardID, fileName.(string)) + destinationFilePath := filepath.Join(destWorkspaceID, block.RootID, destFilename) + + a.logger.Debug( + "Copying card file", + mlog.String("sourceFilePath", sourceFilePath), + mlog.String("destinationFilePath", destinationFilePath), + ) + if err := a.filesBackend.CopyFile(sourceFilePath, destinationFilePath); err != nil { a.logger.Error( "CopyCardFiles failed to copy file", @@ -157,6 +172,7 @@ func (a *App) CopyCardFiles(sourceBoardID string, blocks []model.Block) error { return err } + block.Fields["fileId"] = destFilename } } diff --git a/server/services/store/sqlstore/blocks.go b/server/services/store/sqlstore/blocks.go index f77479cf3..1e8affe0e 100644 --- a/server/services/store/sqlstore/blocks.go +++ b/server/services/store/sqlstore/blocks.go @@ -553,8 +553,11 @@ func (s *SQLStore) getBlock(db sq.BaseRunner, c store.Container, blockID string) query := s.getQueryBuilder(db). Select(s.blockFields()...). From(s.tablePrefix + "blocks"). - Where(sq.Eq{"id": blockID}). - Where(sq.Eq{"coalesce(workspace_id, '0')": c.WorkspaceID}) + Where(sq.Eq{"id": blockID}) + + if c.WorkspaceID != "" { + query = query.Where(sq.Eq{"coalesce(workspace_id, '0')": c.WorkspaceID}) + } rows, err := query.Query() if err != nil { diff --git a/webapp/src/mutator.ts b/webapp/src/mutator.ts index e06798035..89896c315 100644 --- a/webapp/src/mutator.ts +++ b/webapp/src/mutator.ts @@ -760,6 +760,7 @@ class Mutator { } }, beforeUndo, + board.id, ) return [newBlocks, newCard.id] } @@ -829,6 +830,7 @@ class Mutator { await afterRedo?.(board?.id || '') }, beforeUndo, + boardId, ) const board = createdBlocks.find((b: Block) => b.type === 'board') return [createdBlocks, board.id]