mirror of
https://codeberg.org/forgejo/forgejo.git
synced 2024-07-06 05:53:17 +02:00
352 lines
8.9 KiB
Go
352 lines
8.9 KiB
Go
// Copyright 2024 The Forgejo Authors. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package repo
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/url"
|
|
"slices"
|
|
"strings"
|
|
|
|
"code.gitea.io/gitea/models/db"
|
|
user_model "code.gitea.io/gitea/models/user"
|
|
"code.gitea.io/gitea/modules/timeutil"
|
|
|
|
"xorm.io/builder"
|
|
)
|
|
|
|
type ErrStarListNotFound struct {
|
|
Name string
|
|
ID int64
|
|
}
|
|
|
|
func (err ErrStarListNotFound) Error() string {
|
|
if err.Name == "" {
|
|
return fmt.Sprintf("A star list with the ID %d was not found", err.ID)
|
|
}
|
|
|
|
return fmt.Sprintf("A star list with the name %s was not found", err.Name)
|
|
}
|
|
|
|
// IsErrStarListNotFound returns if the error is, that the star is not found
|
|
func IsErrStarListNotFound(err error) bool {
|
|
_, ok := err.(ErrStarListNotFound)
|
|
return ok
|
|
}
|
|
|
|
type ErrStarListExists struct {
|
|
Name string
|
|
}
|
|
|
|
func (err ErrStarListExists) Error() string {
|
|
return fmt.Sprintf("A star list with the name %s exists", err.Name)
|
|
}
|
|
|
|
// IsErrIssueMaxPinReached returns if the error is, that the star list exists
|
|
func IsErrStarListExists(err error) bool {
|
|
_, ok := err.(ErrStarListExists)
|
|
return ok
|
|
}
|
|
|
|
type StarList struct {
|
|
ID int64 `xorm:"pk autoincr"`
|
|
UserID int64 `xorm:"INDEX UNIQUE(name)"`
|
|
Name string `xorm:"INDEX UNIQUE(name)"`
|
|
Description string
|
|
IsPrivate bool
|
|
RepositoryCount int64 `xorm:"-"`
|
|
User *user_model.User `xorm:"-"`
|
|
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
|
|
RepoIDs *[]int64 `xorm:"-"`
|
|
}
|
|
|
|
type StarListRepos struct {
|
|
ID int64 `xorm:"pk autoincr"`
|
|
StarListID int64 `xorm:"INDEX UNIQUE(repo)"`
|
|
RepoID int64 `xorm:"INDEX UNIQUE(repo)"`
|
|
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
|
|
}
|
|
|
|
type StarListSlice []*StarList
|
|
|
|
func init() {
|
|
db.RegisterModel(new(StarList))
|
|
db.RegisterModel(new(StarListRepos))
|
|
}
|
|
|
|
// GetStarListByID returne the star list for the given ID.
|
|
// If the ID do not exists, it returns a ErrStarListNotFound error.
|
|
func GetStarListByID(ctx context.Context, id int64) (*StarList, error) {
|
|
var starList StarList
|
|
|
|
found, err := db.GetEngine(ctx).Table("star_list").ID(id).Get(&starList)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if !found {
|
|
return nil, ErrStarListNotFound{ID: id}
|
|
}
|
|
|
|
return &starList, nil
|
|
}
|
|
|
|
// GetStarListByID returne the star list of the given user with the given name.
|
|
// If the name do not exists, it returns a ErrStarListNotFound error.
|
|
func GetStarListByName(ctx context.Context, userID int64, name string) (*StarList, error) {
|
|
var starList StarList
|
|
|
|
found, err := db.GetEngine(ctx).Table("star_list").Where("user_id = ?", userID).And("LOWER(name) = ?", strings.ToLower(name)).Get(&starList)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if !found {
|
|
return nil, ErrStarListNotFound{Name: name}
|
|
}
|
|
|
|
return &starList, nil
|
|
}
|
|
|
|
// GetStarListsByUserID retruns all star lists for the given user
|
|
func GetStarListsByUserID(ctx context.Context, userID int64, includePrivate bool) (StarListSlice, error) {
|
|
cond := builder.NewCond().And(builder.Eq{"user_id": userID})
|
|
|
|
if !includePrivate {
|
|
cond = cond.And(builder.Eq{"is_private": false})
|
|
}
|
|
|
|
starLists := make(StarListSlice, 0)
|
|
err := db.GetEngine(ctx).Table("star_list").Where(cond).Asc("created_unix").Asc("id").Find(&starLists)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return starLists, nil
|
|
}
|
|
|
|
// CreateStarLists creates a new star list
|
|
// It returns a ErrStarListExists if the user already have a star list with this name
|
|
func CreateStarList(ctx context.Context, userID int64, name, description string, isPrivate bool) (*StarList, error) {
|
|
_, err := GetStarListByName(ctx, userID, name)
|
|
if err != nil {
|
|
if !IsErrStarListNotFound(err) {
|
|
return nil, err
|
|
}
|
|
} else {
|
|
return nil, ErrStarListExists{Name: name}
|
|
}
|
|
|
|
starList := StarList{
|
|
UserID: userID,
|
|
Name: name,
|
|
Description: description,
|
|
IsPrivate: isPrivate,
|
|
}
|
|
|
|
_, err = db.GetEngine(ctx).Insert(starList)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &starList, nil
|
|
}
|
|
|
|
// DeleteStarListByID deletes the star list with the given ID
|
|
func DeleteStarListByID(ctx context.Context, id int64) error {
|
|
ctx, committer, err := db.TxContext(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = db.GetEngine(ctx).Exec("DELETE FROM star_list_repos WHERE star_list_id = ?", id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = db.GetEngine(ctx).Exec("DELETE FROM star_list WHERE id = ?", id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return committer.Commit()
|
|
}
|
|
|
|
// LoadRepositoryCount loads just the RepositoryCount.
|
|
// The count checks if how many repos in the list the actor is able to see.
|
|
func (starList *StarList) LoadRepositoryCount(ctx context.Context, actor *user_model.User) error {
|
|
count, err := CountRepository(ctx, &SearchRepoOptions{Actor: actor, StarListID: starList.ID})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
starList.RepositoryCount = count
|
|
|
|
return nil
|
|
}
|
|
|
|
// LoadUser loads the User field
|
|
func (starList *StarList) LoadUser(ctx context.Context) error {
|
|
user, err := user_model.GetUserByID(ctx, starList.UserID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
starList.User = user
|
|
return nil
|
|
}
|
|
|
|
// LoadRepoIDs loads all repo ids which are in the list
|
|
func (starList *StarList) LoadRepoIDs(ctx context.Context) error {
|
|
repoIDs := make([]int64, 0)
|
|
err := db.GetEngine(ctx).Table("star_list_repos").Where("star_list_id = ?", starList.ID).Cols("repo_id").Find(&repoIDs)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
starList.RepoIDs = &repoIDs
|
|
return nil
|
|
}
|
|
|
|
// Retruns if the list contains the given repo id.
|
|
// This function needs the repo ids loaded to work.
|
|
func (starList *StarList) ContainsRepoID(repoID int64) bool {
|
|
return slices.Contains(*starList.RepoIDs, repoID)
|
|
}
|
|
|
|
// AddRepo adds the given repo to the list
|
|
func (starList *StarList) AddRepo(ctx context.Context, repoID int64) error {
|
|
err := starList.LoadRepoIDs(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if starList.ContainsRepoID(repoID) {
|
|
return nil
|
|
}
|
|
|
|
err = starList.LoadUser(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
repo, err := GetRepositoryByID(ctx, repoID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = StarRepo(ctx, starList.User.ID, repo.ID, true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
starListRepo := StarListRepos{
|
|
StarListID: starList.ID,
|
|
RepoID: repoID,
|
|
}
|
|
|
|
_, err = db.GetEngine(ctx).Insert(starListRepo)
|
|
return err
|
|
}
|
|
|
|
// RemoveRepo removes the given repo from the list
|
|
func (starList *StarList) RemoveRepo(ctx context.Context, repoID int64) error {
|
|
_, err := db.GetEngine(ctx).Exec("DELETE FROM star_list_repos WHERE star_list_id = ? AND repo_id = ?", starList.ID, repoID)
|
|
return err
|
|
}
|
|
|
|
// EditData edits the star list and save it to the database
|
|
// It returns a ErrStarListExists if the user already have a star list with this name
|
|
func (starList *StarList) EditData(ctx context.Context, name, description string, isPrivate bool) error {
|
|
if !strings.EqualFold(starList.Name, name) {
|
|
_, err := GetStarListByName(ctx, starList.UserID, name)
|
|
if err != nil {
|
|
if !IsErrStarListNotFound(err) {
|
|
return err
|
|
}
|
|
} else {
|
|
return ErrStarListExists{Name: name}
|
|
}
|
|
}
|
|
|
|
oldName := starList.Name
|
|
oldDescription := starList.Description
|
|
oldIsPrivate := starList.IsPrivate
|
|
|
|
starList.Name = name
|
|
starList.Description = description
|
|
starList.IsPrivate = isPrivate
|
|
|
|
_, err := db.GetEngine(ctx).Table("star_list").ID(starList.ID).Cols("name", "description", "is_private").Update(starList)
|
|
if err != nil {
|
|
starList.Name = oldName
|
|
starList.Description = oldDescription
|
|
starList.IsPrivate = oldIsPrivate
|
|
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// HasAccess retruns if the given user has access to this star list
|
|
func (starList *StarList) HasAccess(user *user_model.User) bool {
|
|
if !starList.IsPrivate {
|
|
return true
|
|
}
|
|
|
|
if user == nil {
|
|
return false
|
|
}
|
|
|
|
return starList.UserID == user.ID
|
|
}
|
|
|
|
// MustHaveAccess returns a ErrStarListNotFound if the given user has no access to the star list
|
|
func (starList *StarList) MustHaveAccess(user *user_model.User) error {
|
|
if !starList.HasAccess(user) {
|
|
return ErrStarListNotFound{ID: starList.ID, Name: starList.Name}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Returns a Link to the star list.
|
|
// This function needs the user loaded to work.
|
|
func (starList *StarList) Link() string {
|
|
return fmt.Sprintf("%s/-/starlist/%s", starList.User.HomeLink(), url.PathEscape(starList.Name))
|
|
}
|
|
|
|
// LoadUser calls LoadUser on all elements of the list
|
|
func (starLists StarListSlice) LoadUser(ctx context.Context) error {
|
|
for _, list := range starLists {
|
|
err := list.LoadUser(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// LoadRepositoryCount calls LoadRepositoryCount on all elements of the list
|
|
func (starLists StarListSlice) LoadRepositoryCount(ctx context.Context, actor *user_model.User) error {
|
|
for _, list := range starLists {
|
|
err := list.LoadRepositoryCount(ctx, actor)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// LoadRepoIDs calls LoadRepoIDs on all elements of the list
|
|
func (starLists StarListSlice) LoadRepoIDs(ctx context.Context) error {
|
|
for _, list := range starLists {
|
|
err := list.LoadRepoIDs(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|