Files
gitea/models/organization/org_pinned.go
logikonline 81bb23f0da fix: resolve golangci-lint errors (batch 1)
- cmd/gitea-cli: fix errcheck, perfsprint, use modules/json, http constants
- models/migrations: remove unused nolint directive
- models/organization: interface{} -> any
- modules/health: rename HealthResponse -> Response to avoid stutter
- modules/idempotency: use modules/json, fix errcheck, rename IdempotencyInfo -> Info
- modules/structs: fix Verified_At naming, use omitzero

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 17:46:44 -05:00

274 lines
7.8 KiB
Go

// Copyright 2026 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package organization
import (
"context"
"code.gitea.io/gitea/models/db"
"code.gitea.io/gitea/modules/timeutil"
)
// OrgPinnedGroup represents a named group of pinned repositories for an organization
type OrgPinnedGroup struct {
ID int64 `xorm:"pk autoincr"`
OrgID int64 `xorm:"INDEX NOT NULL"`
Name string `xorm:"NOT NULL"`
DisplayOrder int `xorm:"DEFAULT 0"`
Collapsed bool `xorm:"DEFAULT false"`
CreatedUnix timeutil.TimeStamp `xorm:"created"`
UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
}
// TableName returns the table name for OrgPinnedGroup
func (g *OrgPinnedGroup) TableName() string {
return "org_pinned_group"
}
func init() {
db.RegisterModel(new(OrgPinnedGroup))
db.RegisterModel(new(OrgPinnedRepo))
}
// OrgPinnedRepo represents a pinned repository for an organization
type OrgPinnedRepo struct {
ID int64 `xorm:"pk autoincr"`
OrgID int64 `xorm:"INDEX NOT NULL"`
RepoID int64 `xorm:"INDEX NOT NULL"`
GroupID int64 `xorm:"INDEX"` // 0 = ungrouped
DisplayOrder int `xorm:"DEFAULT 0"`
CreatedUnix timeutil.TimeStamp `xorm:"created"`
Repo any `xorm:"-"` // Will be set by caller (repo_model.Repository)
Group *OrgPinnedGroup `xorm:"-"`
}
// TableName returns the table name for OrgPinnedRepo
func (p *OrgPinnedRepo) TableName() string {
return "org_pinned_repo"
}
// GetOrgPinnedGroups returns all pinned groups for an organization
func GetOrgPinnedGroups(ctx context.Context, orgID int64) ([]*OrgPinnedGroup, error) {
groups := make([]*OrgPinnedGroup, 0, 10)
return groups, db.GetEngine(ctx).
Where("org_id = ?", orgID).
OrderBy("display_order ASC, id ASC").
Find(&groups)
}
// GetOrgPinnedGroup returns a pinned group by ID
func GetOrgPinnedGroup(ctx context.Context, id int64) (*OrgPinnedGroup, error) {
group := new(OrgPinnedGroup)
has, err := db.GetEngine(ctx).ID(id).Get(group)
if err != nil {
return nil, err
}
if !has {
return nil, ErrOrgPinnedGroupNotExist{ID: id}
}
return group, nil
}
// CreateOrgPinnedGroup creates a new pinned group for an organization
func CreateOrgPinnedGroup(ctx context.Context, group *OrgPinnedGroup) error {
_, err := db.GetEngine(ctx).Insert(group)
return err
}
// UpdateOrgPinnedGroup updates a pinned group
func UpdateOrgPinnedGroup(ctx context.Context, group *OrgPinnedGroup) error {
_, err := db.GetEngine(ctx).ID(group.ID).Cols("name", "display_order", "collapsed").Update(group)
return err
}
// DeleteOrgPinnedGroup deletes a pinned group and moves its repos to ungrouped
func DeleteOrgPinnedGroup(ctx context.Context, groupID int64) error {
ctx, committer, err := db.TxContext(ctx)
if err != nil {
return err
}
defer committer.Close()
// Move all repos in this group to ungrouped (group_id = 0)
if _, err := db.GetEngine(ctx).
Where("group_id = ?", groupID).
Cols("group_id").
Update(&OrgPinnedRepo{GroupID: 0}); err != nil {
return err
}
// Delete the group
if _, err := db.GetEngine(ctx).ID(groupID).Delete(new(OrgPinnedGroup)); err != nil {
return err
}
return committer.Commit()
}
// GetOrgPinnedRepos returns all pinned repos for an organization
func GetOrgPinnedRepos(ctx context.Context, orgID int64) ([]*OrgPinnedRepo, error) {
pinnedRepos := make([]*OrgPinnedRepo, 0, 20)
return pinnedRepos, db.GetEngine(ctx).
Where("org_id = ?", orgID).
OrderBy("group_id ASC, display_order ASC, id ASC").
Find(&pinnedRepos)
}
// GetOrgPinnedRepoIDs returns the repo IDs of all pinned repos for an organization
func GetOrgPinnedRepoIDs(ctx context.Context, orgID int64) ([]int64, error) {
pinnedRepos, err := GetOrgPinnedRepos(ctx, orgID)
if err != nil {
return nil, err
}
repoIDs := make([]int64, len(pinnedRepos))
for i, p := range pinnedRepos {
repoIDs[i] = p.RepoID
}
return repoIDs, nil
}
// LoadPinnedRepoGroups loads the groups for pinned repos
func LoadPinnedRepoGroups(ctx context.Context, pinnedRepos []*OrgPinnedRepo, orgID int64) error {
if len(pinnedRepos) == 0 {
return nil
}
groups, err := GetOrgPinnedGroups(ctx, orgID)
if err != nil {
return err
}
groupMap := make(map[int64]*OrgPinnedGroup)
for _, g := range groups {
groupMap[g.ID] = g
}
for _, p := range pinnedRepos {
if p.GroupID > 0 {
p.Group = groupMap[p.GroupID]
}
}
return nil
}
// IsRepoPinned checks if a repo is already pinned for an organization
func IsRepoPinned(ctx context.Context, orgID, repoID int64) (bool, error) {
return db.GetEngine(ctx).
Where("org_id = ? AND repo_id = ?", orgID, repoID).
Exist(new(OrgPinnedRepo))
}
// CreateOrgPinnedRepo pins a repository to an organization
func CreateOrgPinnedRepo(ctx context.Context, pinned *OrgPinnedRepo) error {
// Check if already pinned
exists, err := IsRepoPinned(ctx, pinned.OrgID, pinned.RepoID)
if err != nil {
return err
}
if exists {
return ErrOrgPinnedRepoAlreadyExist{OrgID: pinned.OrgID, RepoID: pinned.RepoID}
}
_, err = db.GetEngine(ctx).Insert(pinned)
return err
}
// UpdateOrgPinnedRepo updates a pinned repo's group or order
func UpdateOrgPinnedRepo(ctx context.Context, pinned *OrgPinnedRepo) error {
_, err := db.GetEngine(ctx).ID(pinned.ID).Cols("group_id", "display_order").Update(pinned)
return err
}
// DeleteOrgPinnedRepo unpins a repository from an organization
func DeleteOrgPinnedRepo(ctx context.Context, orgID, repoID int64) error {
_, err := db.GetEngine(ctx).
Where("org_id = ? AND repo_id = ?", orgID, repoID).
Delete(new(OrgPinnedRepo))
return err
}
// DeleteOrgPinnedRepoByID unpins a repository by pinned ID
func DeleteOrgPinnedRepoByID(ctx context.Context, id int64) error {
_, err := db.GetEngine(ctx).ID(id).Delete(new(OrgPinnedRepo))
return err
}
// ReorderOrgPinnedRepos updates the display order of pinned repos
func ReorderOrgPinnedRepos(ctx context.Context, orgID int64, repoOrders []PinnedRepoOrder) error {
ctx, committer, err := db.TxContext(ctx)
if err != nil {
return err
}
defer committer.Close()
for _, order := range repoOrders {
if _, err := db.GetEngine(ctx).
Where("org_id = ? AND repo_id = ?", orgID, order.RepoID).
Cols("group_id", "display_order").
Update(&OrgPinnedRepo{
GroupID: order.GroupID,
DisplayOrder: order.DisplayOrder,
}); err != nil {
return err
}
}
return committer.Commit()
}
// ReorderOrgPinnedGroups updates the display order of pinned groups
func ReorderOrgPinnedGroups(ctx context.Context, orgID int64, groupOrders []PinnedGroupOrder) error {
ctx, committer, err := db.TxContext(ctx)
if err != nil {
return err
}
defer committer.Close()
for _, order := range groupOrders {
if _, err := db.GetEngine(ctx).
Where("org_id = ? AND id = ?", orgID, order.GroupID).
Cols("display_order").
Update(&OrgPinnedGroup{DisplayOrder: order.DisplayOrder}); err != nil {
return err
}
}
return committer.Commit()
}
// PinnedRepoOrder represents the order for a pinned repo
type PinnedRepoOrder struct {
RepoID int64 `json:"repo_id"`
GroupID int64 `json:"group_id"`
DisplayOrder int `json:"display_order"`
}
// PinnedGroupOrder represents the order for a pinned group
type PinnedGroupOrder struct {
GroupID int64 `json:"group_id"`
DisplayOrder int `json:"display_order"`
}
// ErrOrgPinnedGroupNotExist represents a "pinned group not exist" error
type ErrOrgPinnedGroupNotExist struct {
ID int64
}
func (err ErrOrgPinnedGroupNotExist) Error() string {
return "pinned group does not exist"
}
// ErrOrgPinnedRepoAlreadyExist represents a "repo already pinned" error
type ErrOrgPinnedRepoAlreadyExist struct {
OrgID int64
RepoID int64
}
func (err ErrOrgPinnedRepoAlreadyExist) Error() string {
return "repository is already pinned"
}