fix: propagate is_archived from parent to child projects in ReadAll CTE
Replace the Go-side propagateArchivedState function with in-CTE propagation. The recursive SELECT uses (ap.is_archived OR p.is_archived) to inherit archived state from parent projects. The outer query uses GROUP BY with MAX(CAST(is_archived AS int)) to handle projects accessible via both direct permissions and parent traversal. When getArchived=false, a HAVING clause filters out archived projects. The is_archived filter is removed from getUserProjectsStatement so archived parents enter the CTE and propagation works correctly.
This commit is contained in:
parent
dca041459f
commit
e3045dfd00
|
|
@ -70,7 +70,7 @@ func (l *Label) hasAccessToLabel(s *xorm.Session, a web.Auth) (has bool, maxPerm
|
||||||
if isLinkShare {
|
if isLinkShare {
|
||||||
where = builder.Eq{"project_id": linkShare.ProjectID}
|
where = builder.Eq{"project_id": linkShare.ProjectID}
|
||||||
} else {
|
} else {
|
||||||
where = builder.In("project_id", getUserProjectsStatement(a.GetID(), "", false).Select("l.id"))
|
where = builder.In("project_id", getUserProjectsStatement(a.GetID(), "").Select("l.id"))
|
||||||
createdByID = a.GetID()
|
createdByID = a.GetID()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -454,7 +454,7 @@ type projectOptions struct {
|
||||||
getArchived bool
|
getArchived bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func getUserProjectsStatement(userID int64, search string, getArchived bool) *builder.Builder {
|
func getUserProjectsStatement(userID int64, search string) *builder.Builder {
|
||||||
dialect := db.GetDialect()
|
dialect := db.GetDialect()
|
||||||
|
|
||||||
conds := []builder.Cond{
|
conds := []builder.Cond{
|
||||||
|
|
@ -507,16 +507,8 @@ func getUserProjectsStatement(userID int64, search string, getArchived bool) *bu
|
||||||
conds = append(conds, filterCond, parentCondition)
|
conds = append(conds, filterCond, parentCondition)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !getArchived {
|
|
||||||
conds = append(conds,
|
|
||||||
builder.And(
|
|
||||||
builder.Eq{"l.is_archived": false},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return builder.Dialect(dialect).
|
return builder.Dialect(dialect).
|
||||||
Select("l.*").
|
Select("l.id, l.title, l.description, l.identifier, l.hex_color, l.owner_id, l.parent_project_id, l.is_archived, l.background_file_id, l.background_blur_hash, l.position, l.created, l.updated").
|
||||||
From("projects", "l").
|
From("projects", "l").
|
||||||
Join("LEFT", "team_projects tl", "tl.project_id = l.id").
|
Join("LEFT", "team_projects tl", "tl.project_id = l.id").
|
||||||
Join("LEFT", "team_members tm2", "tm2.team_id = tl.team_id").
|
Join("LEFT", "team_members tm2", "tm2.team_id = tl.team_id").
|
||||||
|
|
@ -541,14 +533,14 @@ func accessibleProjectIDsSubquery(a web.Auth, column string) builder.Cond {
|
||||||
}
|
}
|
||||||
|
|
||||||
return builder.In(column,
|
return builder.In(column,
|
||||||
getUserProjectsStatement(u.ID, "", false).Select("l.id"),
|
getUserProjectsStatement(u.ID, "").Select("l.id"),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAllProjectsForUser(s *xorm.Session, userID int64, opts *projectOptions) (projects []*Project, totalCount int64, err error) {
|
func getAllProjectsForUser(s *xorm.Session, userID int64, opts *projectOptions) (projects []*Project, totalCount int64, err error) {
|
||||||
|
|
||||||
limit, start := getLimitFromPageIndex(opts.page, opts.perPage)
|
limit, start := getLimitFromPageIndex(opts.page, opts.perPage)
|
||||||
query := getUserProjectsStatement(userID, opts.search, opts.getArchived)
|
query := getUserProjectsStatement(userID, opts.search)
|
||||||
|
|
||||||
querySQLString, args, err := query.ToSQL()
|
querySQLString, args, err := query.ToSQL()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -562,7 +554,7 @@ func getAllProjectsForUser(s *xorm.Session, userID int64, opts *projectOptions)
|
||||||
|
|
||||||
baseQuery := querySQLString + `
|
baseQuery := querySQLString + `
|
||||||
UNION ALL
|
UNION ALL
|
||||||
SELECT p.* FROM projects p
|
SELECT p.id, p.title, p.description, p.identifier, p.hex_color, p.owner_id, p.parent_project_id, (ap.is_archived OR p.is_archived) AS is_archived, p.background_file_id, p.background_blur_hash, p.position, p.created, p.updated FROM projects p
|
||||||
INNER JOIN all_projects ap ON p.parent_project_id = ap.id`
|
INNER JOIN all_projects ap ON p.parent_project_id = ap.id`
|
||||||
|
|
||||||
columnStr := strings.Join([]string{
|
columnStr := strings.Join([]string{
|
||||||
|
|
@ -573,17 +565,38 @@ INNER JOIN all_projects ap ON p.parent_project_id = ap.id`
|
||||||
"all_projects.hex_color",
|
"all_projects.hex_color",
|
||||||
"all_projects.owner_id",
|
"all_projects.owner_id",
|
||||||
"CASE WHEN all_projects.parent_project_id IS NULL THEN 0 ELSE all_projects.parent_project_id END AS parent_project_id",
|
"CASE WHEN all_projects.parent_project_id IS NULL THEN 0 ELSE all_projects.parent_project_id END AS parent_project_id",
|
||||||
"all_projects.is_archived",
|
"MAX(CAST(all_projects.is_archived AS int)) AS is_archived",
|
||||||
"all_projects.background_file_id",
|
"all_projects.background_file_id",
|
||||||
"all_projects.background_blur_hash",
|
"all_projects.background_blur_hash",
|
||||||
"all_projects.position",
|
"all_projects.position",
|
||||||
"all_projects.created",
|
"all_projects.created",
|
||||||
"all_projects.updated",
|
"all_projects.updated",
|
||||||
}, ", ")
|
}, ", ")
|
||||||
|
|
||||||
|
groupByStr := strings.Join([]string{
|
||||||
|
"all_projects.id",
|
||||||
|
"all_projects.title",
|
||||||
|
"all_projects.description",
|
||||||
|
"all_projects.identifier",
|
||||||
|
"all_projects.hex_color",
|
||||||
|
"all_projects.owner_id",
|
||||||
|
"all_projects.parent_project_id",
|
||||||
|
"all_projects.background_file_id",
|
||||||
|
"all_projects.background_blur_hash",
|
||||||
|
"all_projects.position",
|
||||||
|
"all_projects.created",
|
||||||
|
"all_projects.updated",
|
||||||
|
}, ", ")
|
||||||
|
|
||||||
|
var archivedFilter string
|
||||||
|
if !opts.getArchived {
|
||||||
|
archivedFilter = "HAVING MAX(CAST(all_projects.is_archived AS int)) = 0 "
|
||||||
|
}
|
||||||
|
|
||||||
currentProjects := []*Project{}
|
currentProjects := []*Project{}
|
||||||
err = s.SQL(`WITH RECURSIVE all_projects as (`+baseQuery+`)
|
err = s.SQL(`WITH RECURSIVE all_projects as (`+baseQuery+`)
|
||||||
SELECT DISTINCT `+columnStr+` FROM all_projects
|
SELECT `+columnStr+` FROM all_projects
|
||||||
ORDER BY all_projects.position `+limitSQL, args...).Find(¤tProjects)
|
GROUP BY `+groupByStr+` `+archivedFilter+`ORDER BY all_projects.position `+limitSQL, args...).Find(¤tProjects)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -594,7 +607,7 @@ ORDER BY all_projects.position `+limitSQL, args...).Find(¤tProjects)
|
||||||
|
|
||||||
totalCount, err = s.
|
totalCount, err = s.
|
||||||
SQL(`WITH RECURSIVE all_projects as (`+baseQuery+`)
|
SQL(`WITH RECURSIVE all_projects as (`+baseQuery+`)
|
||||||
SELECT COUNT(DISTINCT all_projects.id) FROM all_projects`, args...).
|
SELECT COUNT(*) FROM (SELECT all_projects.id FROM all_projects GROUP BY all_projects.id `+archivedFilter+`) sub`, args...).
|
||||||
Count(&Project{})
|
Count(&Project{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue