fix(projects): (un-)archive child projects when archiving parent (#775)

This commit is contained in:
kolaente
2025-05-15 16:31:56 +02:00
committed by GitHub
parent a924cb6e48
commit 9f30a099ee
3 changed files with 75 additions and 0 deletions

View File

@@ -44,6 +44,7 @@ async function archiveProject() {
})
useBaseStore().setCurrentProject(newProject)
success({message: t('project.archive.success')})
await projectStore.loadAllProjects()
} finally {
router.back()
}

View File

@@ -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
}

View File

@@ -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)
})
})
}