diff --git a/pkg/migration/20251001113831.go b/pkg/migration/20251001113831.go new file mode 100644 index 000000000..dcfbebb00 --- /dev/null +++ b/pkg/migration/20251001113831.go @@ -0,0 +1,113 @@ +// Vikunja is a to-do list application to facilitate your life. +// Copyright 2018-present Vikunja and contributors. All rights reserved. +// +// 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 . + +package migration + +import ( + "src.techknowlogick.com/xormigrate" + "xorm.io/xorm" + "xorm.io/xorm/schemas" +) + +// Old bucket configuration format (filter as string) +type bucketConfigurationCatchup struct { + Title string `json:"title"` + Filter string `json:"filter"` +} + +// New bucket configuration format (filter as object) +type bucketConfigurationCatchupNew struct { + Title string `json:"title"` + Filter *taskCollection20241118123644 `json:"filter"` +} + +// Old format project view +type projectViewBucketsCatchup struct { + ID int64 `xorm:"autoincr not null unique pk"` + BucketConfiguration []*bucketConfigurationCatchup `xorm:"json"` +} + +func (projectViewBucketsCatchup) TableName() string { + return "project_views" +} + +// New format project view +type projectViewBucketsCatchupNew struct { + ID int64 `xorm:"autoincr not null unique pk"` + BucketConfiguration []*bucketConfigurationCatchupNew `xorm:"json"` +} + +func (projectViewBucketsCatchupNew) TableName() string { + return "project_views" +} + +func init() { + migrations = append(migrations, &xormigrate.Migration{ + ID: "20251001113831", + Description: "catch up on bucket_configuration filter format conversions", + Migrate: func(tx *xorm.Engine) (err error) { + oldViews := []*projectViewBucketsCatchup{} + + // Find views with bucket_configuration in old string format + // Only check views with bucket_configuration_mode = 2 (filter mode) + // Pattern: bucket_configuration contains "filter":"" but not "filter":{"filter": + if tx.Dialect().URI().DBType == schemas.POSTGRES { + err = tx.Where("bucket_configuration_mode = 2 AND bucket_configuration::text like '%\"filter\":\"%'"). + And("bucket_configuration::text not like '%\"filter\":{\"filter\":%'"). + Find(&oldViews) + } else { + err = tx.Where("bucket_configuration_mode = 2 AND bucket_configuration like '%\"filter\":\"%'"). + And("bucket_configuration not like '%\"filter\":{\"filter\":%'"). + Find(&oldViews) + } + + if err != nil { + return + } + + // Transform each view's bucket_configuration + for _, view := range oldViews { + newView := &projectViewBucketsCatchupNew{ + ID: view.ID, + } + + // Convert each bucket configuration from old to new format + for _, configuration := range view.BucketConfiguration { + newView.BucketConfiguration = append(newView.BucketConfiguration, + &bucketConfigurationCatchupNew{ + Title: configuration.Title, + Filter: &taskCollection20241118123644{ + Filter: configuration.Filter, // Wrap string in object + }, + }) + } + + // Update only the bucket_configuration column + _, err = tx.Where("id = ?", view.ID). + Cols("bucket_configuration"). + Update(newView) + if err != nil { + return + } + } + + return + }, + Rollback: func(tx *xorm.Engine) error { + return nil + }, + }) +}