package app import ( "bytes" "fmt" "strings" "github.com/mattermost/focalboard/server/assets" "github.com/mattermost/focalboard/server/model" "github.com/mattermost/mattermost-server/v6/shared/mlog" ) const ( defaultTemplateVersion = 2 globalTeamID = "0" ) func (a *App) InitTemplates() error { _, err := a.initializeTemplates() return err } // initializeTemplates imports default templates if the boards table is empty. func (a *App) initializeTemplates() (bool, error) { boards, err := a.store.GetTemplateBoards(globalTeamID, "") if err != nil { return false, fmt.Errorf("cannot initialize templates: %w", err) } a.logger.Debug("Fetched template boards", mlog.Int("count", len(boards))) isNeeded, reason := a.isInitializationNeeded(boards) if !isNeeded { a.logger.Debug("Template import not needed, skipping") return false, nil } a.logger.Debug("Importing new default templates", mlog.String("reason", reason), mlog.Int("size", len(assets.DefaultTemplatesArchive)), ) // Remove in case of newer Templates if err = a.store.RemoveDefaultTemplates(boards); err != nil { return false, fmt.Errorf("cannot remove old template boards: %w", err) } r := bytes.NewReader(assets.DefaultTemplatesArchive) opt := model.ImportArchiveOptions{ TeamID: globalTeamID, ModifiedBy: "system", BlockModifier: fixTemplateBlock, BoardModifier: fixTemplateBoard, } if err = a.ImportArchive(r, opt); err != nil { return false, fmt.Errorf("cannot initialize global templates for team %s: %w", globalTeamID, err) } return true, nil } // isInitializationNeeded returns true if the blocks table contains no default templates, // or contains at least one default template with an old version number. func (a *App) isInitializationNeeded(boards []*model.Board) (bool, string) { if len(boards) == 0 { return true, "no default templates found" } // look for any built-in template boards with the wrong version number (or no version #). for _, board := range boards { // if not built-in board...skip if board.CreatedBy != "system" { continue } if board.TemplateVersion < defaultTemplateVersion { return true, "template_version too old" } } return false, "" } // fixTemplateBlock fixes a block to be inserted as part of a template. func fixTemplateBlock(block *model.Block, cache map[string]interface{}) bool { // cache contains ids of skipped boards. Ensure their children are skipped as well. if _, ok := cache[block.BoardID]; ok { cache[block.ID] = struct{}{} return false } if _, ok := cache[block.ParentID]; ok { cache[block.ID] = struct{}{} return false } return true } // fixTemplateBoard fixes a board to be inserted as part of a template. func fixTemplateBoard(board *model.Board, cache map[string]interface{}) bool { // filter out template blocks; we only want the non-template // blocks which we will turn into default template blocks. if board.IsTemplate { cache[board.ID] = struct{}{} return false } // remove '(NEW)' from title & force template flag board.Title = strings.ReplaceAll(board.Title, "(NEW)", "") board.IsTemplate = true board.TemplateVersion = defaultTemplateVersion board.Type = model.BoardTypeOpen return true }