diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 6a60067782cff..bc5368d44c641 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -380,6 +380,7 @@ func prepareMigrationTasks() []*migration { newMigration(316, "Add description for secrets and variables", v1_24.AddDescriptionForSecretsAndVariables), newMigration(317, "Add new index for action for heatmap", v1_24.AddNewIndexForUserDashboard), newMigration(318, "Add anonymous_access_mode for repo_unit", v1_24.AddRepoUnitAnonymousAccessMode), + newMigration(319, "Add star_list, star_list_repo table", v1_24.AddStarList), } return preparedMigrations } diff --git a/models/migrations/v1_24/v319.go b/models/migrations/v1_24/v319.go new file mode 100644 index 0000000000000..02223ab6ec35c --- /dev/null +++ b/models/migrations/v1_24/v319.go @@ -0,0 +1,44 @@ +// Copyright 2025 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_24 //nolint + +import ( + "code.gitea.io/gitea/modules/timeutil" + + "xorm.io/xorm" +) + +type StarList struct { + ID int64 `xorm:"pk autoincr"` + UID int64 `xorm:"INDEX"` + Name string + Desc string + + CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` + UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` +} + +type StarListRepo struct { + ID int64 `xorm:"pk autoincr"` + UID int64 `xorm:"UNIQUE(s)"` + StarListID int64 `xorm:"UNIQUE(s)"` + RepoID int64 `xorm:"UNIQUE(s)"` +} + +func AddStarList(x *xorm.Engine) error { + sess := x.NewSession() + defer sess.Close() + if err := sess.Begin(); err != nil { + return err + } + err := sess.Sync(new(StarList)) + if err != nil { + return err + } + err = sess.Sync(new(StarListRepo)) + if err != nil { + return err + } + return sess.Commit() +} diff --git a/models/repo/issue.go b/models/repo/issue.go index 0dd4fd5ed480e..4f186aae9cc9b 100644 --- a/models/repo/issue.go +++ b/models/repo/issue.go @@ -11,13 +11,6 @@ import ( "code.gitea.io/gitea/modules/setting" ) -// ___________.__ ___________ __ -// \__ ___/|__| _____ ___\__ ___/___________ ____ | | __ ___________ -// | | | |/ \_/ __ \| | \_ __ \__ \ _/ ___\| |/ // __ \_ __ \ -// | | | | Y Y \ ___/| | | | \// __ \\ \___| <\ ___/| | \/ -// |____| |__|__|_| /\___ >____| |__| (____ /\___ >__|_ \\___ >__| -// \/ \/ \/ \/ \/ \/ - // CanEnableTimetracker returns true when the server admin enabled time tracking // This overrules IsTimetrackerEnabled func (repo *Repository) CanEnableTimetracker() bool { diff --git a/models/repo/repo.go b/models/repo/repo.go index 2977dfb9f1d8a..f798e4581c39c 100644 --- a/models/repo/repo.go +++ b/models/repo/repo.go @@ -755,13 +755,6 @@ func (repo *Repository) MustNotBeArchived() error { return nil } -// __________ .__ __ -// \______ \ ____ ______ ____ _____|__|/ |_ ___________ ___.__. -// | _// __ \\____ \ / _ \/ ___/ \ __\/ _ \_ __ < | | -// | | \ ___/| |_> > <_> )___ \| || | ( <_> ) | \/\___ | -// |____|_ /\___ > __/ \____/____ >__||__| \____/|__| / ____| -// \/ \/|__| \/ \/ - // ErrRepoNotExist represents a "RepoNotExist" kind of error. type ErrRepoNotExist struct { ID int64 diff --git a/models/repo/star_list.go b/models/repo/star_list.go new file mode 100644 index 0000000000000..8cfbe293cd4e5 --- /dev/null +++ b/models/repo/star_list.go @@ -0,0 +1,58 @@ +// Copyright 2025 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package repo + +import ( + "context" + + "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/modules/timeutil" +) + +type StarList struct { + ID int64 `xorm:"pk autoincr"` + UID int64 `xorm:"INDEX"` + Name string + Desc string + + Repos []Repository `xorm:"-"` + + CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` + UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` +} + +func init() { + db.RegisterModel(new(StarList)) +} + +func InsertStarList(ctx context.Context, starList *StarList) error { + _, err := db.GetEngine(ctx).Insert(starList) + return err +} + +func UpdateStarList(ctx context.Context, starList *StarList) error { + _, err := db.GetEngine(ctx).Where("id = ?", starList.ID).AllCols().Update(starList) + return err +} + +func DeleteStarListByID(ctx context.Context, id int64) error { + _, err := db.GetEngine(ctx).Delete(&StarList{ID: id}) + return err +} + +func GetStarListByID(ctx context.Context, id int64) (*StarList, error) { + starList := new(StarList) + if has, err := db.GetEngine(ctx).Where("id = ?", id).Get(starList); err != nil { + return nil, err + } else if !has { + return nil, nil + } + return starList, nil +} + +func GetStarListsForUser(ctx context.Context, id int64) ([]*StarList, error) { + starLists := make([]*StarList, 0, 10) + err := db.GetEngine(ctx).Where("uid = ?", id).Find(&starLists) + return starLists, err +} diff --git a/models/repo/star_list_repo.go b/models/repo/star_list_repo.go new file mode 100644 index 0000000000000..9c28e863d01e0 --- /dev/null +++ b/models/repo/star_list_repo.go @@ -0,0 +1,55 @@ +package repo + +import ( + "context" + + "code.gitea.io/gitea/models/db" +) + +type StarListRepo struct { + ID int64 `xorm:"pk autoincr"` + UID int64 `xorm:"UNIQUE(s)"` + StarListID int64 `xorm:"UNIQUE(s)"` + RepoID int64 `xorm:"UNIQUE(s)"` +} + +func init() { + db.RegisterModel(new(StarListRepo)) +} + +func StarLists(ctx context.Context, uid, repoID int64, ids []int64) error { + starListRepos := make([]*StarListRepo, 0, len(ids)) + for _, id := range ids { + starListRepos = append(starListRepos, &StarListRepo{ + UID: uid, + StarListID: id, + RepoID: repoID, + }) + } + + ctx, committer, err := db.TxContext(ctx) + if err != nil { + return err + } + defer committer.Close() + + _, err = db.GetEngine(ctx).Insert(&starListRepos) + if err != nil { + return err + } + + _, err = db.GetEngine(ctx).Where("uid = ? AND repo_id = ? AND star_list_id NOT IN (?)", uid, repoID, ids).Delete(new(StarListRepo)) + if err != nil { + return err + } + + return committer.Commit() +} + +func UnStarLists(ctx context.Context, uid, repoID int64, ids []int64) error { + _, err := db.GetEngine(ctx).Where("uid = ? AND repo_id = ? AND star_list_id NOT IN (?)", uid, repoID, ids).Delete(new(StarListRepo)) + if err != nil { + return err + } + return nil +} diff --git a/routers/web/repo/view_home.go b/routers/web/repo/view_home.go index 3b053821ee751..70b79b7b91a2e 100644 --- a/routers/web/repo/view_home.go +++ b/routers/web/repo/view_home.go @@ -434,6 +434,14 @@ func Home(ctx *context.Context) { ctx.Data["ParentPath"] = "/" + paths[len(paths)-2] } } + + starList, err := repo_model.GetStarListsForUser(ctx, ctx.Doer.ID) + if err != nil { + ctx.ServerError("SearchStarList", err) + return + } + log.Error("=============================") + ctx.Data["StarList"] = starList ctx.Data["Paths"] = paths ctx.Data["TreeLink"] = treeLink ctx.Data["TreeNames"] = treeNames diff --git a/routers/web/shared/starlists/star_lists.go b/routers/web/shared/starlists/star_lists.go new file mode 100644 index 0000000000000..ea51bd0e62584 --- /dev/null +++ b/routers/web/shared/starlists/star_lists.go @@ -0,0 +1,47 @@ +package starlists + +import ( + repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/context" + "code.gitea.io/gitea/services/forms" +) + +func GetByName(ctx *context.Context) { + +} + +func Create(ctx *context.Context) { + form := web.GetForm(ctx).(*forms.StarListForm) + + log.Info("form: %+v", *form) + + err := repo_model.InsertStarList(ctx, &repo_model.StarList{ + UID: ctx.Doer.ID, + Name: form.Name, + Desc: form.Desc, + }) + if err != nil { + ctx.ServerError("InsertStarList", err) + return + } + + ctx.Redirect(ctx.Doer.HomeLink() + "?tab=stars") +} + +func UpdateByName(ctx *context.Context) { + +} + +func DeleteByName(ctx *context.Context) { + +} + +func List(ctx *context.Context) { + +} + +func UpdateListRepos(ctx *context.Context) { + +} diff --git a/routers/web/user/profile.go b/routers/web/user/profile.go index 39f066a53c904..ecd9b8a139317 100644 --- a/routers/web/user/profile.go +++ b/routers/web/user/profile.go @@ -190,6 +190,12 @@ func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileDb total = int(count) case "stars": ctx.Data["PageIsProfileStarList"] = true + starList, err := repo_model.GetStarListsForUser(ctx, ctx.ContextUser.ID) + if err != nil { + ctx.ServerError("SearchStarList", err) + return + } + ctx.Data["StarList"] = starList repos, count, err = repo_model.SearchRepository(ctx, &repo_model.SearchRepoOptions{ ListOptions: db.ListOptions{ PageSize: pagingNum, diff --git a/routers/web/web.go b/routers/web/web.go index fcddcad1b12d6..d4bba732c8e49 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -39,6 +39,7 @@ import ( repo_setting "code.gitea.io/gitea/routers/web/repo/setting" shared_actions "code.gitea.io/gitea/routers/web/shared/actions" "code.gitea.io/gitea/routers/web/shared/project" + "code.gitea.io/gitea/routers/web/shared/starlists" "code.gitea.io/gitea/routers/web/user" user_setting "code.gitea.io/gitea/routers/web/user/setting" "code.gitea.io/gitea/routers/web/user/setting/security" @@ -1620,6 +1621,18 @@ func registerWebRoutes(m *web.Router) { m.Post("/action/{action:accept_transfer|reject_transfer}", reqSignIn, repo.ActionTransfer) }, optSignIn, context.RepoAssignment) + m.Group("/stars/lists", func() { + m.Post("", web.Bind(forms.StarListForm{}), starlists.Create) // 创建一个新的list + m.Get("/{listname}", starlists.GetByName) // 获取当前listname的所有repo + m.Post("/{listname}", starlists.UpdateByName) // 更新当前listname的所有repo + m.Delete("/{listname}", starlists.DeleteByName) // 删除当前listname + }) + + m.Group("/{username}/{reponame}", func() { + m.Get("/lists", starlists.List) // List the repo in all star lists + m.Post("/lists", starlists.UpdateListRepos) // Update the repo star lists + }) + common.AddOwnerRepoGitLFSRoutes(m, optSignInIgnoreCsrf, lfsServerEnabled) // "/{username}/{reponame}/{lfs-paths}": git-lfs support addOwnerRepoGitHTTPRouters(m) // "/{username}/{reponame}/{git-paths}": git http support diff --git a/services/forms/star_list_form.go b/services/forms/star_list_form.go new file mode 100644 index 0000000000000..b341df78a2cb9 --- /dev/null +++ b/services/forms/star_list_form.go @@ -0,0 +1,22 @@ +package forms + +import ( + "net/http" + + "code.gitea.io/gitea/modules/web/middleware" + "code.gitea.io/gitea/services/context" + "gitea.com/go-chi/binding" +) + +type StarListForm struct { + Action string `binding:"Required"` + ID int64 `binding:"OmitEmpty"` + UID int64 `binding:"Required"` + Name string `binding:"Required;MaxSize(50)"` + Desc string `binding:"OmitEmpty"` +} + +func (s *StarListForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetValidateContext(req) + return middleware.Validate(errs, ctx.Data, s, ctx.Locale) +} diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl index c7c53b4f5dc01..862c0b7704bbc 100644 --- a/templates/repo/header.tmpl +++ b/templates/repo/header.tmpl @@ -93,9 +93,10 @@ {{end}} > {{svg "octicon-repo-forked"}}{{ctx.Locale.Tr "repo.fork"}} + {{CountFmt .NumForks}} - {{CountFmt .NumForks}} + {{svg "octicon-triangle-down"}}