mirror of
https://github.com/go-vikunja/vikunja.git
synced 2026-03-12 01:59:34 -05:00
fix(projects): (un-)archive child projects when archiving parent (#775)
This commit is contained in:
@@ -44,6 +44,7 @@ async function archiveProject() {
|
||||
})
|
||||
useBaseStore().setCurrentProject(newProject)
|
||||
success({message: t('project.archive.success')})
|
||||
await projectStore.loadAllProjects()
|
||||
} finally {
|
||||
router.back()
|
||||
}
|
||||
|
||||
@@ -944,6 +944,11 @@ func UpdateProject(s *xorm.Session, project *Project, auth web.Auth, updateProje
|
||||
}
|
||||
}
|
||||
|
||||
err = setArchiveStateForProjectDescendants(s, project.ID, project.IsArchived)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// We need to specify the cols we want to update here to be able to un-archive projects
|
||||
colsToUpdate := []string{
|
||||
"title",
|
||||
@@ -1286,3 +1291,40 @@ func SetProjectBackground(s *xorm.Session, projectID int64, background *files.Fi
|
||||
Update(l)
|
||||
return
|
||||
}
|
||||
|
||||
// setArchiveStateForProjectDescendants uses a recursive CTE to find and set the archived status of all descendant projects.
|
||||
func setArchiveStateForProjectDescendants(s *xorm.Session, parentProjectID int64, shouldBeArchived bool) error {
|
||||
var descendantIDs []int64
|
||||
err := s.SQL(
|
||||
`
|
||||
WITH RECURSIVE descendant_ids (id) AS (
|
||||
SELECT id
|
||||
FROM projects
|
||||
WHERE parent_project_id = ?
|
||||
UNION ALL
|
||||
SELECT p.id
|
||||
FROM projects p
|
||||
INNER JOIN descendant_ids di ON p.parent_project_id = di.id
|
||||
)
|
||||
SELECT id FROM descendant_ids`,
|
||||
parentProjectID,
|
||||
).Find(&descendantIDs)
|
||||
if err != nil {
|
||||
log.Errorf("Error finding descendant projects for parent ID %d: %v", parentProjectID, err)
|
||||
return fmt.Errorf("failed to find descendant projects for parent ID %d: %w", parentProjectID, err)
|
||||
}
|
||||
|
||||
if len(descendantIDs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
_, err = s.In("id", descendantIDs).
|
||||
And("is_archived != ?", shouldBeArchived).
|
||||
Cols("is_archived").
|
||||
Update(&Project{IsArchived: shouldBeArchived})
|
||||
if err != nil {
|
||||
log.Errorf("Error updating is_archived for descendant projects for parent ID %d to %t: %v", parentProjectID, shouldBeArchived, err)
|
||||
return fmt.Errorf("failed to update is_archived for descendant projects for parent ID %d to %t: %w", parentProjectID, shouldBeArchived, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -305,6 +305,38 @@ func TestProject_CreateOrUpdate(t *testing.T) {
|
||||
require.Error(t, err)
|
||||
assert.True(t, IsErrCannotArchiveDefaultProject(err))
|
||||
})
|
||||
t.Run("archive parent archives child", func(t *testing.T) {
|
||||
db.LoadAndAssertFixtures(t)
|
||||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
actingUser := &user.User{ID: 6}
|
||||
|
||||
projectToArchive := Project{
|
||||
ID: 27,
|
||||
}
|
||||
|
||||
// We need to load the project first to have its fields populated for the update
|
||||
can, err := projectToArchive.CanUpdate(s, actingUser)
|
||||
require.NoError(t, err, "Failed to read project 27 before archiving")
|
||||
assert.True(t, can)
|
||||
projectToArchive.IsArchived = true // Ensure IsArchived is set after reading
|
||||
|
||||
err = projectToArchive.Update(s, actingUser)
|
||||
require.NoError(t, err, "Failed to archive project")
|
||||
err = s.Commit()
|
||||
require.NoError(t, err, "Failed to commit session after archiving project")
|
||||
|
||||
db.AssertExists(t, "projects", map[string]interface{}{
|
||||
"id": 27,
|
||||
"is_archived": true,
|
||||
}, false)
|
||||
// Assert child project (ID 12) is also archived
|
||||
db.AssertExists(t, "projects", map[string]interface{}{
|
||||
"id": 12,
|
||||
"is_archived": true,
|
||||
}, false)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user