Files
forge/models/organization/org_list.go
zeekayandHanzo Dev 9a07b4d089 legal: Hanzo AI, Inc. copyright above the upstream notices
2978 files. The fork is 2026-07-15 (rebrand: gitea.dev -> github.com/hanzoai/git),
so 2026 is the year, and "Copyright 2026 Hanzo AI, Inc." was already the form
used by the handful of files that had ours.

Shape is now:

	// Copyright 2026 Hanzo AI, Inc. All rights reserved.
	// Copyright 2015 The Gogs Authors. All rights reserved.
	// Copyright 2019 The Gitea Authors. All rights reserved.
	// SPDX-License-Identifier: MIT

Ours on top, theirs retained verbatim. Retained because MIT requires it, and
separately because they are true — those authors did write that code.

Not renamed to "The Git Authors": Git is a different project under a different
license, and putting its name here would attribute this code to people who have
nothing to do with it. Adding our line is the fix; overwriting theirs would have
been both a licence violation and a false statement.

gofmt still reports import-order drift in some of these files. It predates this
change (verified against a stash) and is left alone rather than burying a
one-line-per-file legal change under 3000 files of unrelated reordering.

Build clean.

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-27 13:49:52 -07:00

176 lines
4.8 KiB
Go

// Copyright 2026 Hanzo AI, Inc. All rights reserved.
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package organization
import (
"context"
"fmt"
"strings"
"github.com/hanzoai/git/models/db"
"github.com/hanzoai/git/models/perm"
user_model "github.com/hanzoai/git/models/user"
"github.com/hanzoai/git/modules/structs"
"github.com/hanzoai/builder"
)
type OrgList []*Organization
func (orgs OrgList) LoadTeams(ctx context.Context) (map[int64]TeamList, error) {
if len(orgs) == 0 {
return map[int64]TeamList{}, nil
}
orgIDs := make([]int64, len(orgs))
for i, org := range orgs {
orgIDs[i] = org.ID
}
teams, err := GetTeamsByOrgIDs(ctx, orgIDs)
if err != nil {
return nil, err
}
teamMap := make(map[int64]TeamList, len(orgs))
for _, team := range teams {
teamMap[team.OrgID] = append(teamMap[team.OrgID], team)
}
return teamMap, nil
}
// SearchOrganizationsOptions options to filter organizations
type SearchOrganizationsOptions struct {
db.ListOptions
All bool
}
// FindOrgOptions finds orgs options
type FindOrgOptions struct {
db.ListOptions
UserID int64
IncludeVisibility structs.VisibleType
}
func (opts *FindOrgOptions) ApplyPublicOnly(publicOnly bool) {
if publicOnly {
opts.IncludeVisibility = structs.VisibleTypePublic
}
}
func queryUserOrgIDs(userID int64, includePrivate bool) *builder.Builder {
cond := builder.Eq{"uid": userID}
if !includePrivate {
cond["is_public"] = true
}
return builder.Select("org_id").From("org_user").Where(cond)
}
func (opts FindOrgOptions) ToConds() builder.Cond {
var cond builder.Cond = builder.Eq{"`user`.`type`": user_model.UserTypeOrganization}
if opts.UserID > 0 {
cond = cond.And(builder.In("`user`.`id`", queryUserOrgIDs(opts.UserID, opts.IncludeVisibility == structs.VisibleTypePrivate)))
}
// public=0, limited=1, private=2
cond = cond.And(builder.Lte{"`user`.visibility": opts.IncludeVisibility})
return cond
}
func (opts FindOrgOptions) ToOrders() string {
return "`user`.lower_name ASC"
}
func DoerViewOtherVisibility(doer, other *user_model.User) structs.VisibleType {
if doer == nil || other == nil {
return structs.VisibleTypePublic
}
if doer.IsAdmin || doer.ID == other.ID {
return structs.VisibleTypePrivate
}
return structs.VisibleTypeLimited
}
// GetOrgsCanCreateRepoByUserID returns a list of organizations where given user ID
// are allowed to create repos.
func GetOrgsCanCreateRepoByUserID(ctx context.Context, userID int64) ([]*Organization, error) {
orgs := make([]*Organization, 0, 10)
return orgs, db.GetEngine(ctx).Where(builder.In("id", builder.Select("`user`.id").From("`user`").
Join("INNER", "`team_user`", "`team_user`.org_id = `user`.id").
Join("INNER", "`team`", "`team`.id = `team_user`.team_id").
Where(builder.Eq{"`team_user`.uid": userID}).
And(builder.Eq{"`team`.authorize": perm.AccessModeOwner}.Or(builder.Eq{"`team`.can_create_org_repo": true})))).
Asc("`user`.name").
Find(&orgs)
}
// MinimalOrg represents a simple organization with only the needed columns
type MinimalOrg = Organization
// GetUserOrgsList returns all organizations the given user has access to
func GetUserOrgsList(ctx context.Context, user *user_model.User) ([]*MinimalOrg, error) {
outputCols := []string{
"id",
"name",
"full_name",
"visibility",
"avatar",
"avatar_email",
"use_custom_avatar",
}
selectColumns := &strings.Builder{}
for i, col := range outputCols {
_, _ = fmt.Fprintf(selectColumns, "`user`.%s", col)
if i < len(outputCols)-1 {
selectColumns.WriteString(", ")
}
}
columnsStr := selectColumns.String()
var orgs []*MinimalOrg
if err := db.GetEngine(ctx).Select(columnsStr).
Table("user").
Where(builder.In("`user`.`id`", queryUserOrgIDs(user.ID, true))).
OrderBy("`user`.lower_name ASC").
Find(&orgs); err != nil {
return nil, err
}
type orgCount struct {
OrgID int64
RepoCount int
}
var orgCounts []orgCount
if err := db.GetEngine(ctx).
Select("owner_id AS org_id, COUNT(DISTINCT(repository.id)) as repo_count").
Table("repository").
Join("INNER", "org_user", "owner_id = org_user.org_id").
Where("org_user.uid = ?", user.ID).
And(builder.Or(
builder.Eq{"repository.is_private": false},
builder.In("repository.id", builder.Select("repo_id").From("team_repo").
InnerJoin("team_user", "team_user.team_id = team_repo.team_id").
Where(builder.Eq{"team_user.uid": user.ID})),
builder.In("repository.id", builder.Select("repo_id").From("collaboration").
Where(builder.Eq{"user_id": user.ID})),
)).
GroupBy("owner_id").Find(&orgCounts); err != nil {
return nil, err
}
orgCountMap := make(map[int64]int, len(orgCounts))
for _, orgCount := range orgCounts {
orgCountMap[orgCount.OrgID] = orgCount.RepoCount
}
for _, org := range orgs {
org.NumRepos = orgCountMap[org.ID]
}
return orgs, nil
}