You've already forked focalboard
							
							
				mirror of
				https://github.com/mattermost/focalboard.git
				synced 2025-10-31 00:17:42 +02:00 
			
		
		
		
	* WIP * Added migrations * Updating store method * WIP * WIP * Updated DND * WIP * WIP * WIP * WIP * WIP * wip * WIP * Adding new DB tool * Used migration functions in new migrations * Unique constraint migration * Unique constraint migration * Added SQLITE migrations * Added SQLITE support in few more migrations * Added SQLITE support in few more migrations * WIP * Used old-fashioned way to add unique constraint * Using oldsqlite method * Using oldsqlite method * Fixed all store and app layer tests * fixed integration tests * test and lint fix * Updated migration for MySQL and Postgres on personal server * Types fix * sqlite fix * fix typo * misc cleanup * added new tests * added new tests * de-duping input for postgres * integration tests, rmeoved uneeded migration * Added some migration tests * Added some migration tests * Fixed a test * completed migration tests * completed migration tests * Removed leftover debug statements Co-authored-by: Mattermost Build <build@mattermost.com>
		
			
				
	
	
		
			246 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			246 lines
		
	
	
		
			6.3 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| package app
 | |
| 
 | |
| import (
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 
 | |
| 	"github.com/mattermost/focalboard/server/model"
 | |
| 	"github.com/mattermost/focalboard/server/utils"
 | |
| )
 | |
| 
 | |
| var errCategoryNotFound = errors.New("category ID specified in input does not exist for user")
 | |
| var errCategoriesLengthMismatch = errors.New("cannot update category order, passed list of categories different size than in database")
 | |
| var ErrCannotDeleteSystemCategory = errors.New("cannot delete a system category")
 | |
| var ErrCannotUpdateSystemCategory = errors.New("cannot update a system category")
 | |
| 
 | |
| func (a *App) GetCategory(categoryID string) (*model.Category, error) {
 | |
| 	return a.store.GetCategory(categoryID)
 | |
| }
 | |
| 
 | |
| func (a *App) CreateCategory(category *model.Category) (*model.Category, error) {
 | |
| 	category.Hydrate()
 | |
| 	if err := category.IsValid(); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	if err := a.store.CreateCategory(*category); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	createdCategory, err := a.store.GetCategory(category.ID)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	go func() {
 | |
| 		a.wsAdapter.BroadcastCategoryChange(*createdCategory)
 | |
| 	}()
 | |
| 
 | |
| 	return createdCategory, nil
 | |
| }
 | |
| 
 | |
| func (a *App) UpdateCategory(category *model.Category) (*model.Category, error) {
 | |
| 	category.Hydrate()
 | |
| 
 | |
| 	if err := category.IsValid(); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	// verify if category belongs to the user
 | |
| 	existingCategory, err := a.store.GetCategory(category.ID)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	if existingCategory.DeleteAt != 0 {
 | |
| 		return nil, model.ErrCategoryDeleted
 | |
| 	}
 | |
| 
 | |
| 	if existingCategory.UserID != category.UserID {
 | |
| 		return nil, model.ErrCategoryPermissionDenied
 | |
| 	}
 | |
| 
 | |
| 	if existingCategory.TeamID != category.TeamID {
 | |
| 		return nil, model.ErrCategoryPermissionDenied
 | |
| 	}
 | |
| 
 | |
| 	// in case type was defaulted above, set to existingCategory.Type
 | |
| 	category.Type = existingCategory.Type
 | |
| 	if existingCategory.Type == model.CategoryTypeSystem {
 | |
| 		// You cannot rename or delete a system category,
 | |
| 		// So restoring its name and undeleting it if set so.
 | |
| 		category.Name = existingCategory.Name
 | |
| 		category.DeleteAt = 0
 | |
| 	}
 | |
| 
 | |
| 	category.UpdateAt = utils.GetMillis()
 | |
| 	if err = category.IsValid(); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 	if err = a.store.UpdateCategory(*category); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	updatedCategory, err := a.store.GetCategory(category.ID)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	go func() {
 | |
| 		a.wsAdapter.BroadcastCategoryChange(*updatedCategory)
 | |
| 	}()
 | |
| 
 | |
| 	return updatedCategory, nil
 | |
| }
 | |
| 
 | |
| func (a *App) DeleteCategory(categoryID, userID, teamID string) (*model.Category, error) {
 | |
| 	existingCategory, err := a.store.GetCategory(categoryID)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	// category is already deleted. This avoids
 | |
| 	// overriding the original deleted at timestamp
 | |
| 	if existingCategory.DeleteAt != 0 {
 | |
| 		return existingCategory, nil
 | |
| 	}
 | |
| 
 | |
| 	// verify if category belongs to the user
 | |
| 	if existingCategory.UserID != userID {
 | |
| 		return nil, model.ErrCategoryPermissionDenied
 | |
| 	}
 | |
| 
 | |
| 	// verify if category belongs to the team
 | |
| 	if existingCategory.TeamID != teamID {
 | |
| 		return nil, model.NewErrInvalidCategory("category doesn't belong to the team")
 | |
| 	}
 | |
| 
 | |
| 	if existingCategory.Type == model.CategoryTypeSystem {
 | |
| 		return nil, ErrCannotDeleteSystemCategory
 | |
| 	}
 | |
| 
 | |
| 	if err = a.moveBoardsToDefaultCategory(userID, teamID, categoryID); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	if err = a.store.DeleteCategory(categoryID, userID, teamID); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	deletedCategory, err := a.store.GetCategory(categoryID)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	go func() {
 | |
| 		a.wsAdapter.BroadcastCategoryChange(*deletedCategory)
 | |
| 	}()
 | |
| 
 | |
| 	return deletedCategory, nil
 | |
| }
 | |
| 
 | |
| func (a *App) moveBoardsToDefaultCategory(userID, teamID, sourceCategoryID string) error {
 | |
| 	// we need a list of boards associated to this category
 | |
| 	// so we can move them to user's default Boards category
 | |
| 	categoryBoards, err := a.GetUserCategoryBoards(userID, teamID)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	var sourceCategoryBoards *model.CategoryBoards
 | |
| 	defaultCategoryID := ""
 | |
| 
 | |
| 	// iterate user's categories to find the source category
 | |
| 	// and the default category.
 | |
| 	// We need source category to get the list of its board
 | |
| 	// and the default category to know its ID to
 | |
| 	// move source category's boards to.
 | |
| 	for i := range categoryBoards {
 | |
| 		if categoryBoards[i].ID == sourceCategoryID {
 | |
| 			sourceCategoryBoards = &categoryBoards[i]
 | |
| 		}
 | |
| 
 | |
| 		if categoryBoards[i].Name == defaultCategoryBoards {
 | |
| 			defaultCategoryID = categoryBoards[i].ID
 | |
| 		}
 | |
| 
 | |
| 		// if both categories are found, no need to iterate furthur.
 | |
| 		if sourceCategoryBoards != nil && defaultCategoryID != "" {
 | |
| 			break
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if sourceCategoryBoards == nil {
 | |
| 		return errCategoryNotFound
 | |
| 	}
 | |
| 
 | |
| 	if defaultCategoryID == "" {
 | |
| 		return fmt.Errorf("moveBoardsToDefaultCategory: %w", errNoDefaultCategoryFound)
 | |
| 	}
 | |
| 
 | |
| 	boardIDs := make([]string, len(sourceCategoryBoards.BoardMetadata))
 | |
| 	for i := range sourceCategoryBoards.BoardMetadata {
 | |
| 		boardIDs[i] = sourceCategoryBoards.BoardMetadata[i].BoardID
 | |
| 	}
 | |
| 
 | |
| 	if err := a.AddUpdateUserCategoryBoard(teamID, userID, defaultCategoryID, boardIDs); err != nil {
 | |
| 		return fmt.Errorf("moveBoardsToDefaultCategory: %w", err)
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 | |
| 
 | |
| func (a *App) ReorderCategories(userID, teamID string, newCategoryOrder []string) ([]string, error) {
 | |
| 	if err := a.verifyNewCategoriesMatchExisting(userID, teamID, newCategoryOrder); err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	newOrder, err := a.store.ReorderCategories(userID, teamID, newCategoryOrder)
 | |
| 	if err != nil {
 | |
| 		return nil, err
 | |
| 	}
 | |
| 
 | |
| 	go func() {
 | |
| 		a.wsAdapter.BroadcastCategoryReorder(teamID, userID, newOrder)
 | |
| 	}()
 | |
| 
 | |
| 	return newOrder, nil
 | |
| }
 | |
| 
 | |
| func (a *App) verifyNewCategoriesMatchExisting(userID, teamID string, newCategoryOrder []string) error {
 | |
| 	existingCategories, err := a.store.GetUserCategories(userID, teamID)
 | |
| 	if err != nil {
 | |
| 		return err
 | |
| 	}
 | |
| 
 | |
| 	if len(newCategoryOrder) != len(existingCategories) {
 | |
| 		return fmt.Errorf(
 | |
| 			"%w length new categories: %d, length existing categories: %d, userID: %s, teamID: %s",
 | |
| 			errCategoriesLengthMismatch,
 | |
| 			len(newCategoryOrder),
 | |
| 			len(existingCategories),
 | |
| 			userID,
 | |
| 			teamID,
 | |
| 		)
 | |
| 	}
 | |
| 
 | |
| 	existingCategoriesMap := map[string]bool{}
 | |
| 	for _, category := range existingCategories {
 | |
| 		existingCategoriesMap[category.ID] = true
 | |
| 	}
 | |
| 
 | |
| 	for _, newCategoryID := range newCategoryOrder {
 | |
| 		if _, found := existingCategoriesMap[newCategoryID]; !found {
 | |
| 			return fmt.Errorf(
 | |
| 				"%w specified category ID: %s, userID: %s, teamID: %s",
 | |
| 				errCategoryNotFound,
 | |
| 				newCategoryID,
 | |
| 				userID,
 | |
| 				teamID,
 | |
| 			)
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return nil
 | |
| }
 |