diff --git a/cmd/admin.go b/cmd/admin.go index e4210213cde..7463b21d81a 100644 --- a/cmd/admin.go +++ b/cmd/admin.go @@ -778,6 +778,7 @@ func runRepoSyncReleases(_ *cli.Context) error { func getReleaseCount(id int64) (int64, error) { return repo_model.GetReleaseCountByRepoID( + db.DefaultContext, id, repo_model.FindReleasesOptions{ IncludeTags: true, diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index 03f004ee901..8824d31a4ae 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -2147,7 +2147,7 @@ ROUTER = console ;[cron.update_checker] ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;ENABLED = false +;ENABLED = true ;RUN_AT_START = false ;ENABLE_SUCCESS_NOTICE = false ;SCHEDULE = @every 168h diff --git a/docs/content/doc/advanced/config-cheat-sheet.en-us.md b/docs/content/doc/advanced/config-cheat-sheet.en-us.md index 37f03b42ea7..dcf91c3396f 100644 --- a/docs/content/doc/advanced/config-cheat-sheet.en-us.md +++ b/docs/content/doc/advanced/config-cheat-sheet.en-us.md @@ -1005,7 +1005,7 @@ Default templates for project boards: #### Cron - Check for new Gitea versions ('cron.update_checker') -- `ENABLED`: **false**: Enable service. +- `ENABLED`: **true**: Enable service. - `RUN_AT_START`: **false**: Run tasks at start up time (if ENABLED). - `ENABLE_SUCCESS_NOTICE`: **true**: Set to false to switch off success notices. - `SCHEDULE`: **@every 168h**: Cron syntax for scheduling a work, e.g. `@every 168h`. diff --git a/docs/content/doc/installation/from-source.en-us.md b/docs/content/doc/installation/from-source.en-us.md index 01a5e1eaee8..4394d19203a 100644 --- a/docs/content/doc/installation/from-source.en-us.md +++ b/docs/content/doc/installation/from-source.en-us.md @@ -145,7 +145,7 @@ launched manually from command line, it can be killed by pressing `Ctrl + C`. Gitea will search for a number of things from the _`CustomPath`_. By default this is the `custom/` directory in the current working directory when running Gitea. It will also -look for its configuration file _`CustomConf`_ in _`CustomPath`_/conf/app.ini`, and will use the +look for its configuration file _`CustomConf`_ in `$(CustomPath)/conf/app.ini`, and will use the current working directory as the relative base path _`AppWorkPath`_ for a number configurable values. Finally the static files will be served from _`StaticRootPath`_ which defaults to the _`AppWorkPath`_. diff --git a/go.mod b/go.mod index de8629f1322..e8cb5547824 100644 --- a/go.mod +++ b/go.mod @@ -105,8 +105,8 @@ require ( gopkg.in/yaml.v3 v3.0.1 mvdan.cc/xurls/v2 v2.4.0 strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251 - xorm.io/builder v0.3.11 - xorm.io/xorm v1.3.2-0.20220714055524-c3bce556200f + xorm.io/builder v0.3.12 + xorm.io/xorm v1.3.3-0.20221209153726-f1bfc5ce9830 ) require ( @@ -182,7 +182,7 @@ require ( github.com/go-openapi/strfmt v0.21.3 // indirect github.com/go-openapi/swag v0.22.3 // indirect github.com/go-openapi/validate v0.22.0 // indirect - github.com/goccy/go-json v0.9.11 // indirect + github.com/goccy/go-json v0.10.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect github.com/golang-sql/sqlexp v0.1.0 // indirect diff --git a/go.sum b/go.sum index 2cacaaa0554..7a1787b863d 100644 --- a/go.sum +++ b/go.sum @@ -599,8 +599,8 @@ github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJA github.com/goccy/go-json v0.8.1/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.9.5/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.9.6/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= -github.com/goccy/go-json v0.9.11 h1:/pAaQDLHEoCq/5FFmSKBswWmK6H0e8g4159Kc/X/nqk= -github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/goccy/go-json v0.10.0 h1:mXKd9Qw4NuzShiRlOXKews24ufknHO7gx30lsDyokKA= +github.com/goccy/go-json v0.10.0/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= @@ -2379,7 +2379,7 @@ sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1 strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251 h1:mUcz5b3FJbP5Cvdq7Khzn6J9OCUQJaBwgBkCR+MOwSs= strk.kbt.io/projects/go/libravatar v0.0.0-20191008002943-06d1c002b251/go.mod h1:FJGmPh3vz9jSos1L/F91iAgnC/aejc0wIIrF2ZwJxdY= xorm.io/builder v0.3.11-0.20220531020008-1bd24a7dc978/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE= -xorm.io/builder v0.3.11 h1:naLkJitGyYW7ZZdncsh/JW+HF4HshmvTHTyUyPwJS00= -xorm.io/builder v0.3.11/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE= -xorm.io/xorm v1.3.2-0.20220714055524-c3bce556200f h1:3NvNsM4lnttTsHpk8ODHqrwN1MCEjsO3bD/rpd8A47k= -xorm.io/xorm v1.3.2-0.20220714055524-c3bce556200f/go.mod h1:9NbjqdnjX6eyjRRhh01GHm64r6N9shTb/8Ak3YRt8Nw= +xorm.io/builder v0.3.12 h1:ASZYX7fQmy+o8UJdhlLHSW57JDOkM8DNhcAF5d0LiJM= +xorm.io/builder v0.3.12/go.mod h1:aUW0S9eb9VCaPohFCH3j7czOx1PMW3i1HrSzbLYGBSE= +xorm.io/xorm v1.3.3-0.20221209153726-f1bfc5ce9830 h1:ohaHCvT7ocSDkTEa2/2z0BXfINYlHm/Z7IzN7MeXQlM= +xorm.io/xorm v1.3.3-0.20221209153726-f1bfc5ce9830/go.mod h1:9NbjqdnjX6eyjRRhh01GHm64r6N9shTb/8Ak3YRt8Nw= diff --git a/models/db/engine.go b/models/db/engine.go index 07ee6ca580a..3d05fa8b638 100755 --- a/models/db/engine.go +++ b/models/db/engine.go @@ -46,7 +46,7 @@ type Engine interface { Incr(column string, arg ...interface{}) *xorm.Session Insert(...interface{}) (int64, error) Iterate(interface{}, xorm.IterFunc) error - Join(joinOperator string, tablename interface{}, condition string, args ...interface{}) *xorm.Session + Join(joinOperator string, tablename, condition interface{}, args ...interface{}) *xorm.Session SQL(interface{}, ...interface{}) *xorm.Session Where(interface{}, ...interface{}) *xorm.Session Asc(colNames ...string) *xorm.Session diff --git a/models/db/iterate_test.go b/models/db/iterate_test.go index b0ea8b53ef1..57b1bd3cd19 100644 --- a/models/db/iterate_test.go +++ b/models/db/iterate_test.go @@ -25,7 +25,7 @@ func TestIterate(t *testing.T) { return nil }) assert.NoError(t, err) - assert.EqualValues(t, 79, repoCnt) + assert.EqualValues(t, 80, repoCnt) err = db.Iterate(db.DefaultContext, nil, func(ctx context.Context, repoUnit *repo_model.RepoUnit) error { reopUnit2 := repo_model.RepoUnit{ID: repoUnit.ID} diff --git a/models/fixtures/repo_unit.yml b/models/fixtures/repo_unit.yml index 59ab6183407..73db47218bc 100644 --- a/models/fixtures/repo_unit.yml +++ b/models/fixtures/repo_unit.yml @@ -544,3 +544,9 @@ repo_id: 51 type: 2 created_unix: 946684810 + +- + id: 80 + repo_id: 53 + type: 1 + created_unix: 946684810 diff --git a/models/fixtures/repository.yml b/models/fixtures/repository.yml index f09953be7e5..a5a3dd0601f 100644 --- a/models/fixtures/repository.yml +++ b/models/fixtures/repository.yml @@ -1558,3 +1558,30 @@ size: 0 is_fsck_enabled: true close_issues_via_commit_in_any_branch: false + +- + id: 53 + owner_id: 30 + owner_name: user30 + lower_name: renderer + name: renderer + is_archived: false + is_empty: false + is_private: false + num_issues: 0 + num_closed_issues: 0 + num_pulls: 0 + num_closed_pulls: 0 + num_milestones: 0 + num_closed_milestones: 0 + num_watches: 0 + num_projects: 0 + num_closed_projects: 0 + status: 0 + is_fork: false + fork_id: 0 + is_template: false + template_id: 0 + size: 0 + is_fsck_enabled: true + close_issues_via_commit_in_any_branch: false diff --git a/models/fixtures/user.yml b/models/fixtures/user.yml index 0e3348e146a..1b870e8999b 100644 --- a/models/fixtures/user.yml +++ b/models/fixtures/user.yml @@ -1102,7 +1102,7 @@ num_followers: 0 num_following: 0 num_stars: 0 - num_repos: 3 + num_repos: 4 num_teams: 0 num_members: 0 visibility: 0 diff --git a/models/issues/assignees.go b/models/issues/assignees.go index d3a1f5ffe83..159086bd010 100644 --- a/models/issues/assignees.go +++ b/models/issues/assignees.go @@ -102,7 +102,7 @@ func toggleIssueAssignee(ctx context.Context, issue *Issue, doer *user_model.Use AssigneeID: assigneeID, } // Comment - comment, err = CreateCommentCtx(ctx, opts) + comment, err = CreateComment(ctx, opts) if err != nil { return false, nil, fmt.Errorf("createComment: %w", err) } diff --git a/models/issues/comment.go b/models/issues/comment.go index f49c6e2c1f6..612f17aa5af 100644 --- a/models/issues/comment.go +++ b/models/issues/comment.go @@ -779,8 +779,8 @@ func (c *Comment) LoadPushCommits(ctx context.Context) (err error) { return err } -// CreateCommentCtx creates comment with context -func CreateCommentCtx(ctx context.Context, opts *CreateCommentOptions) (_ *Comment, err error) { +// CreateComment creates comment with context +func CreateComment(ctx context.Context, opts *CreateCommentOptions) (_ *Comment, err error) { e := db.GetEngine(ctx) var LabelID int64 if opts.Label != nil { @@ -875,6 +875,8 @@ func updateCommentInfos(ctx context.Context, opts *CreateCommentOptions, comment return fmt.Errorf("update attachment [%d]: %w", attachments[i].ID, err) } } + + comment.Attachments = attachments case CommentTypeReopen, CommentTypeClose: if err = repo_model.UpdateRepoIssueNumbers(ctx, opts.Issue.RepoID, opts.Issue.IsPull, true); err != nil { return err @@ -913,7 +915,7 @@ func createDeadlineComment(ctx context.Context, doer *user_model.User, issue *Is Issue: issue, Content: content, } - comment, err := CreateCommentCtx(ctx, opts) + comment, err := CreateComment(ctx, opts) if err != nil { return nil, err } @@ -938,7 +940,7 @@ func createIssueDependencyComment(ctx context.Context, doer *user_model.User, is Issue: issue, DependentIssueID: dependentIssue.ID, } - if _, err = CreateCommentCtx(ctx, opts); err != nil { + if _, err = CreateComment(ctx, opts); err != nil { return } @@ -949,7 +951,7 @@ func createIssueDependencyComment(ctx context.Context, doer *user_model.User, is Issue: dependentIssue, DependentIssueID: issue.ID, } - _, err = CreateCommentCtx(ctx, opts) + _, err = CreateComment(ctx, opts) return err } @@ -991,55 +993,6 @@ type CreateCommentOptions struct { Invalidated bool } -// CreateComment creates comment of issue or commit. -func CreateComment(opts *CreateCommentOptions) (comment *Comment, err error) { - ctx, committer, err := db.TxContext(db.DefaultContext) - if err != nil { - return nil, err - } - defer committer.Close() - - comment, err = CreateCommentCtx(ctx, opts) - if err != nil { - return nil, err - } - - if err = committer.Commit(); err != nil { - return nil, err - } - - return comment, nil -} - -// CreateRefComment creates a commit reference comment to issue. -func CreateRefComment(doer *user_model.User, repo *repo_model.Repository, issue *Issue, content, commitSHA string) error { - if len(commitSHA) == 0 { - return fmt.Errorf("cannot create reference with empty commit SHA") - } - - // Check if same reference from same commit has already existed. - has, err := db.GetEngine(db.DefaultContext).Get(&Comment{ - Type: CommentTypeCommitRef, - IssueID: issue.ID, - CommitSHA: commitSHA, - }) - if err != nil { - return fmt.Errorf("check reference comment: %w", err) - } else if has { - return nil - } - - _, err = CreateComment(&CreateCommentOptions{ - Type: CommentTypeCommitRef, - Doer: doer, - Repo: repo, - Issue: issue, - CommitSHA: commitSHA, - Content: content, - }) - return err -} - // GetCommentByID returns the comment by given ID. func GetCommentByID(ctx context.Context, id int64) (*Comment, error) { c := new(Comment) @@ -1315,39 +1268,6 @@ func UpdateCommentsMigrationsByType(tp structs.GitServiceType, originalAuthorID return err } -// CreatePushPullComment create push code to pull base comment -func CreatePushPullComment(ctx context.Context, pusher *user_model.User, pr *PullRequest, oldCommitID, newCommitID string) (comment *Comment, err error) { - if pr.HasMerged || oldCommitID == "" || newCommitID == "" { - return nil, nil - } - - ops := &CreateCommentOptions{ - Type: CommentTypePullRequestPush, - Doer: pusher, - Repo: pr.BaseRepo, - } - - var data PushActionContent - - data.CommitIDs, data.IsForcePush, err = getCommitIDsFromRepo(ctx, pr.BaseRepo, oldCommitID, newCommitID, pr.BaseBranch) - if err != nil { - return nil, err - } - - ops.Issue = pr.Issue - - dataJSON, err := json.Marshal(data) - if err != nil { - return nil, err - } - - ops.Content = string(dataJSON) - - comment, err = CreateComment(ops) - - return comment, err -} - // CreateAutoMergeComment is a internal function, only use it for CommentTypePRScheduledToAutoMerge and CommentTypePRUnScheduledToAutoMerge CommentTypes func CreateAutoMergeComment(ctx context.Context, typ CommentType, pr *PullRequest, doer *user_model.User) (comment *Comment, err error) { if typ != CommentTypePRScheduledToAutoMerge && typ != CommentTypePRUnScheduledToAutoMerge { @@ -1361,7 +1281,7 @@ func CreateAutoMergeComment(ctx context.Context, typ CommentType, pr *PullReques return } - comment, err = CreateCommentCtx(ctx, &CreateCommentOptions{ + comment, err = CreateComment(ctx, &CreateCommentOptions{ Type: typ, Doer: doer, Repo: pr.BaseRepo, @@ -1370,120 +1290,6 @@ func CreateAutoMergeComment(ctx context.Context, typ CommentType, pr *PullReques return comment, err } -// getCommitsFromRepo get commit IDs from repo in between oldCommitID and newCommitID -// isForcePush will be true if oldCommit isn't on the branch -// Commit on baseBranch will skip -func getCommitIDsFromRepo(ctx context.Context, repo *repo_model.Repository, oldCommitID, newCommitID, baseBranch string) (commitIDs []string, isForcePush bool, err error) { - repoPath := repo.RepoPath() - gitRepo, closer, err := git.RepositoryFromContextOrOpen(ctx, repoPath) - if err != nil { - return nil, false, err - } - defer closer.Close() - - oldCommit, err := gitRepo.GetCommit(oldCommitID) - if err != nil { - return nil, false, err - } - - if err = oldCommit.LoadBranchName(); err != nil { - return nil, false, err - } - - if len(oldCommit.Branch) == 0 { - commitIDs = make([]string, 2) - commitIDs[0] = oldCommitID - commitIDs[1] = newCommitID - - return commitIDs, true, err - } - - newCommit, err := gitRepo.GetCommit(newCommitID) - if err != nil { - return nil, false, err - } - - commits, err := newCommit.CommitsBeforeUntil(oldCommitID) - if err != nil { - return nil, false, err - } - - commitIDs = make([]string, 0, len(commits)) - commitChecks := make(map[string]*commitBranchCheckItem) - - for _, commit := range commits { - commitChecks[commit.ID.String()] = &commitBranchCheckItem{ - Commit: commit, - Checked: false, - } - } - - if err = commitBranchCheck(gitRepo, newCommit, oldCommitID, baseBranch, commitChecks); err != nil { - return - } - - for i := len(commits) - 1; i >= 0; i-- { - commitID := commits[i].ID.String() - if item, ok := commitChecks[commitID]; ok && item.Checked { - commitIDs = append(commitIDs, commitID) - } - } - - return commitIDs, isForcePush, err -} - -type commitBranchCheckItem struct { - Commit *git.Commit - Checked bool -} - -func commitBranchCheck(gitRepo *git.Repository, startCommit *git.Commit, endCommitID, baseBranch string, commitList map[string]*commitBranchCheckItem) error { - if startCommit.ID.String() == endCommitID { - return nil - } - - checkStack := make([]string, 0, 10) - checkStack = append(checkStack, startCommit.ID.String()) - - for len(checkStack) > 0 { - commitID := checkStack[0] - checkStack = checkStack[1:] - - item, ok := commitList[commitID] - if !ok { - continue - } - - if item.Commit.ID.String() == endCommitID { - continue - } - - if err := item.Commit.LoadBranchName(); err != nil { - return err - } - - if item.Commit.Branch == baseBranch { - continue - } - - if item.Checked { - continue - } - - item.Checked = true - - parentNum := item.Commit.ParentCount() - for i := 0; i < parentNum; i++ { - parentCommit, err := item.Commit.Parent(i) - if err != nil { - return err - } - checkStack = append(checkStack, parentCommit.ID.String()) - } - } - return nil -} - // RemapExternalUser ExternalUserRemappable interface func (c *Comment) RemapExternalUser(externalName string, externalID, userID int64) error { c.OriginalAuthor = externalName diff --git a/models/issues/comment_test.go b/models/issues/comment_test.go index 6a647474dca..0d0570ae34d 100644 --- a/models/issues/comment_test.go +++ b/models/issues/comment_test.go @@ -24,7 +24,7 @@ func TestCreateComment(t *testing.T) { doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) now := time.Now().Unix() - comment, err := issues_model.CreateComment(&issues_model.CreateCommentOptions{ + comment, err := issues_model.CreateComment(db.DefaultContext, &issues_model.CreateCommentOptions{ Type: issues_model.CommentTypeComment, Doer: doer, Repo: repo, diff --git a/models/issues/issue.go b/models/issues/issue.go index 3ab6d204ba0..fc93fcf4546 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -202,16 +202,12 @@ func (issue *Issue) LoadRepo(ctx context.Context) (err error) { } // IsTimetrackerEnabled returns true if the repo enables timetracking -func (issue *Issue) IsTimetrackerEnabled() bool { - return issue.isTimetrackerEnabled(db.DefaultContext) -} - -func (issue *Issue) isTimetrackerEnabled(ctx context.Context) bool { +func (issue *Issue) IsTimetrackerEnabled(ctx context.Context) bool { if err := issue.LoadRepo(ctx); err != nil { log.Error(fmt.Sprintf("loadRepo: %v", err)) return false } - return issue.Repo.IsTimetrackerEnabledCtx(ctx) + return issue.Repo.IsTimetrackerEnabled(ctx) } // GetPullRequest returns the issue pull request @@ -404,7 +400,7 @@ func (issue *Issue) LoadAttributes(ctx context.Context) (err error) { if err = CommentList(issue.Comments).loadAttributes(ctx); err != nil { return err } - if issue.isTimetrackerEnabled(ctx) { + if issue.IsTimetrackerEnabled(ctx) { if err = issue.LoadTotalTimes(ctx); err != nil { return err } @@ -673,7 +669,7 @@ func changeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.User, func doChangeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.User, isMergePull bool) (*Comment, error) { // Check for open dependencies - if issue.IsClosed && issue.Repo.IsDependenciesEnabledCtx(ctx) { + if issue.IsClosed && issue.Repo.IsDependenciesEnabled(ctx) { // only check if dependencies are enabled and we're about to close an issue, otherwise reopening an issue would fail when there are unsatisfied dependencies noDeps, err := IssueNoDependenciesLeft(ctx, issue) if err != nil { @@ -725,7 +721,7 @@ func doChangeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.Use cmtType = CommentTypeMergePull } - return CreateCommentCtx(ctx, &CreateCommentOptions{ + return CreateComment(ctx, &CreateCommentOptions{ Type: cmtType, Doer: doer, Repo: issue.Repo, @@ -769,7 +765,7 @@ func ChangeIssueTitle(issue *Issue, doer *user_model.User, oldTitle string) (err OldTitle: oldTitle, NewTitle: issue.Title, } - if _, err = CreateCommentCtx(ctx, opts); err != nil { + if _, err = CreateComment(ctx, opts); err != nil { return fmt.Errorf("createComment: %w", err) } if err = issue.AddCrossReferences(ctx, doer, true); err != nil { @@ -805,7 +801,7 @@ func ChangeIssueRef(issue *Issue, doer *user_model.User, oldRef string) (err err OldRef: oldRefFriendly, NewRef: newRefFriendly, } - if _, err = CreateCommentCtx(ctx, opts); err != nil { + if _, err = CreateComment(ctx, opts); err != nil { return fmt.Errorf("createComment: %w", err) } @@ -825,7 +821,7 @@ func AddDeletePRBranchComment(ctx context.Context, doer *user_model.User, repo * Issue: issue, OldRef: branchName, } - _, err = CreateCommentCtx(ctx, opts) + _, err = CreateComment(ctx, opts) return err } @@ -992,7 +988,7 @@ func NewIssueWithIndex(ctx context.Context, doer *user_model.User, opts NewIssue OldMilestoneID: 0, MilestoneID: opts.Issue.MilestoneID, } - if _, err = CreateCommentCtx(ctx, opts); err != nil { + if _, err = CreateComment(ctx, opts); err != nil { return err } } @@ -2000,7 +1996,7 @@ func UpdateIssueByAPI(issue *Issue, doer *user_model.User) (statusChangeComment OldTitle: currentIssue.Title, NewTitle: issue.Title, } - _, err := CreateCommentCtx(ctx, opts) + _, err := CreateComment(ctx, opts) if err != nil { return nil, false, fmt.Errorf("createComment: %w", err) } diff --git a/models/issues/issue_list.go b/models/issues/issue_list.go index 35a0c1f0e3a..e22e48c0bb4 100644 --- a/models/issues/issue_list.go +++ b/models/issues/issue_list.go @@ -461,7 +461,7 @@ func (issues IssueList) loadTotalTrackedTimes(ctx context.Context) (err error) { ids := make([]int64, 0, len(issues)) for _, issue := range issues { - if issue.Repo.IsTimetrackerEnabled() { + if issue.Repo.IsTimetrackerEnabled(ctx) { ids = append(ids, issue.ID) } } diff --git a/models/issues/issue_lock.go b/models/issues/issue_lock.go index 1376ffcadad..19cd6d31675 100644 --- a/models/issues/issue_lock.go +++ b/models/issues/issue_lock.go @@ -56,7 +56,7 @@ func updateIssueLock(opts *IssueLockOptions, lock bool) error { Type: commentType, Content: opts.Reason, } - if _, err := CreateCommentCtx(ctx, opt); err != nil { + if _, err := CreateComment(ctx, opt); err != nil { return err } diff --git a/models/issues/issue_project.go b/models/issues/issue_project.go index a366a92fad0..8e559f13c92 100644 --- a/models/issues/issue_project.go +++ b/models/issues/issue_project.go @@ -145,7 +145,7 @@ func addUpdateIssueProject(ctx context.Context, issue *Issue, doer *user_model.U } if oldProjectID > 0 || newProjectID > 0 { - if _, err := CreateCommentCtx(ctx, &CreateCommentOptions{ + if _, err := CreateComment(ctx, &CreateCommentOptions{ Type: CommentTypeProject, Doer: doer, Repo: issue.Repo, diff --git a/models/issues/issue_xref.go b/models/issues/issue_xref.go index 267ccc73a03..21ee24210f8 100644 --- a/models/issues/issue_xref.go +++ b/models/issues/issue_xref.go @@ -121,7 +121,7 @@ func (issue *Issue) createCrossReferences(stdCtx context.Context, ctx *crossRefe RefAction: xref.Action, RefIsPull: ctx.OrigIssue.IsPull, } - _, err := CreateCommentCtx(stdCtx, opts) + _, err := CreateComment(stdCtx, opts) if err != nil { return err } diff --git a/models/issues/label.go b/models/issues/label.go index e5583ff00f6..dbb7a139eff 100644 --- a/models/issues/label.go +++ b/models/issues/label.go @@ -613,7 +613,7 @@ func newIssueLabel(ctx context.Context, issue *Issue, label *Label, doer *user_m Label: label, Content: "1", } - if _, err = CreateCommentCtx(ctx, opts); err != nil { + if _, err = CreateComment(ctx, opts); err != nil { return err } @@ -714,7 +714,7 @@ func deleteIssueLabel(ctx context.Context, issue *Issue, label *Label, doer *use Issue: issue, Label: label, } - if _, err = CreateCommentCtx(ctx, opts); err != nil { + if _, err = CreateComment(ctx, opts); err != nil { return err } diff --git a/models/issues/review.go b/models/issues/review.go index db2d2686cb6..7dee28fe976 100644 --- a/models/issues/review.go +++ b/models/issues/review.go @@ -294,7 +294,7 @@ func IsOfficialReviewerTeam(ctx context.Context, issue *Issue, team *organizatio } if !pr.ProtectedBranch.EnableApprovalsWhitelist { - return team.UnitAccessModeCtx(ctx, unit.TypeCode) >= perm.AccessModeWrite, nil + return team.UnitAccessMode(ctx, unit.TypeCode) >= perm.AccessModeWrite, nil } return base.Int64sContains(pr.ProtectedBranch.ApprovalsWhitelistTeamIDs, team.ID), nil @@ -436,7 +436,7 @@ func SubmitReview(doer *user_model.User, issue *Issue, reviewType ReviewType, co } } - comm, err := CreateCommentCtx(ctx, &CreateCommentOptions{ + comm, err := CreateComment(ctx, &CreateCommentOptions{ Type: CommentTypeReview, Doer: doer, Content: review.Content, @@ -692,7 +692,7 @@ func AddReviewRequest(issue *Issue, reviewer, doer *user_model.User) (*Comment, return nil, err } - comment, err := CreateCommentCtx(ctx, &CreateCommentOptions{ + comment, err := CreateComment(ctx, &CreateCommentOptions{ Type: CommentTypeReviewRequest, Doer: doer, Repo: issue.Repo, @@ -746,7 +746,7 @@ func RemoveReviewRequest(issue *Issue, reviewer, doer *user_model.User) (*Commen } } - comment, err := CreateCommentCtx(ctx, &CreateCommentOptions{ + comment, err := CreateComment(ctx, &CreateCommentOptions{ Type: CommentTypeReviewRequest, Doer: doer, Repo: issue.Repo, @@ -804,7 +804,7 @@ func AddTeamReviewRequest(issue *Issue, reviewer *organization.Team, doer *user_ } } - comment, err := CreateCommentCtx(ctx, &CreateCommentOptions{ + comment, err := CreateComment(ctx, &CreateCommentOptions{ Type: CommentTypeReviewRequest, Doer: doer, Repo: issue.Repo, @@ -814,7 +814,7 @@ func AddTeamReviewRequest(issue *Issue, reviewer *organization.Team, doer *user_ ReviewID: review.ID, }) if err != nil { - return nil, fmt.Errorf("CreateCommentCtx(): %w", err) + return nil, fmt.Errorf("CreateComment(): %w", err) } return comment, committer.Commit() @@ -864,7 +864,7 @@ func RemoveTeamReviewRequest(issue *Issue, reviewer *organization.Team, doer *us return nil, committer.Commit() } - comment, err := CreateCommentCtx(ctx, &CreateCommentOptions{ + comment, err := CreateComment(ctx, &CreateCommentOptions{ Type: CommentTypeReviewRequest, Doer: doer, Repo: issue.Repo, @@ -873,7 +873,7 @@ func RemoveTeamReviewRequest(issue *Issue, reviewer *organization.Team, doer *us AssigneeTeamID: reviewer.ID, // Use AssigneeTeamID as reviewer team ID }) if err != nil { - return nil, fmt.Errorf("CreateCommentCtx(): %w", err) + return nil, fmt.Errorf("CreateComment(): %w", err) } return comment, committer.Commit() diff --git a/models/issues/stopwatch.go b/models/issues/stopwatch.go index 8a8fdca339c..6bf936c5d41 100644 --- a/models/issues/stopwatch.go +++ b/models/issues/stopwatch.go @@ -196,7 +196,7 @@ func FinishIssueStopwatch(ctx context.Context, user *user_model.User, issue *Iss return err } - if _, err := CreateCommentCtx(ctx, &CreateCommentOptions{ + if _, err := CreateComment(ctx, &CreateCommentOptions{ Doer: user, Issue: issue, Repo: issue.Repo, @@ -246,7 +246,7 @@ func CreateIssueStopwatch(ctx context.Context, user *user_model.User, issue *Iss return err } - if _, err := CreateCommentCtx(ctx, &CreateCommentOptions{ + if _, err := CreateComment(ctx, &CreateCommentOptions{ Doer: user, Issue: issue, Repo: issue.Repo, @@ -287,7 +287,7 @@ func cancelStopwatch(ctx context.Context, user *user_model.User, issue *Issue) e return err } - if _, err := CreateCommentCtx(ctx, &CreateCommentOptions{ + if _, err := CreateComment(ctx, &CreateCommentOptions{ Doer: user, Issue: issue, Repo: issue.Repo, diff --git a/models/issues/tracked_time.go b/models/issues/tracked_time.go index 554b01bd407..ac65d654f20 100644 --- a/models/issues/tracked_time.go +++ b/models/issues/tracked_time.go @@ -172,7 +172,7 @@ func AddTime(user *user_model.User, issue *Issue, amount int64, created time.Tim return nil, err } - if _, err := CreateCommentCtx(ctx, &CreateCommentOptions{ + if _, err := CreateComment(ctx, &CreateCommentOptions{ Issue: issue, Repo: issue.Repo, Doer: user, @@ -250,7 +250,7 @@ func DeleteIssueUserTimes(issue *Issue, user *user_model.User) error { if err := issue.LoadRepo(ctx); err != nil { return err } - if _, err := CreateCommentCtx(ctx, &CreateCommentOptions{ + if _, err := CreateComment(ctx, &CreateCommentOptions{ Issue: issue, Repo: issue.Repo, Doer: user, @@ -279,7 +279,7 @@ func DeleteTime(t *TrackedTime) error { return err } - if _, err := CreateCommentCtx(ctx, &CreateCommentOptions{ + if _, err := CreateComment(ctx, &CreateCommentOptions{ Issue: t.Issue, Repo: t.Issue.Repo, Doer: t.User, diff --git a/models/migrations/v1_10/v96.go b/models/migrations/v1_10/v96.go index 2abd260be41..422defe8387 100644 --- a/models/migrations/v1_10/v96.go +++ b/models/migrations/v1_10/v96.go @@ -30,19 +30,19 @@ func DeleteOrphanedAttachments(x *xorm.Engine) error { } for { - attachements := make([]Attachment, 0, limit) + attachments := make([]Attachment, 0, limit) if err := sess.Where("`issue_id` = 0 and (`release_id` = 0 or `release_id` not in (select `id` from `release`))"). Cols("id, uuid").Limit(limit). Asc("id"). - Find(&attachements); err != nil { + Find(&attachments); err != nil { return err } - if len(attachements) == 0 { + if len(attachments) == 0 { return nil } ids := make([]int64, 0, limit) - for _, attachment := range attachements { + for _, attachment := range attachments { ids = append(ids, attachment.ID) } if len(ids) > 0 { @@ -51,13 +51,13 @@ func DeleteOrphanedAttachments(x *xorm.Engine) error { } } - for _, attachment := range attachements { + for _, attachment := range attachments { uuid := attachment.UUID if err := util.RemoveAll(filepath.Join(setting.Attachment.Path, uuid[0:1], uuid[1:2], uuid)); err != nil { return err } } - if len(attachements) < limit { + if len(attachments) < limit { return nil } } diff --git a/models/organization/org.go b/models/organization/org.go index a5b07d5aae8..b3d77b4ec69 100644 --- a/models/organization/org.go +++ b/models/organization/org.go @@ -106,7 +106,7 @@ func (org *Organization) IsOrgMember(uid int64) (bool, error) { // CanCreateOrgRepo returns true if given user can create repo in organization func (org *Organization) CanCreateOrgRepo(uid int64) (bool, error) { - return CanCreateOrgRepo(org.ID, uid) + return CanCreateOrgRepo(db.DefaultContext, org.ID, uid) } func (org *Organization) getTeam(ctx context.Context, name string) (*Team, error) { diff --git a/models/organization/org_user.go b/models/organization/org_user.go index d514e401ba6..e5cbfe6c0f3 100644 --- a/models/organization/org_user.go +++ b/models/organization/org_user.go @@ -73,8 +73,8 @@ func IsPublicMembership(orgID, uid int64) (bool, error) { } // CanCreateOrgRepo returns true if user can create repo in organization -func CanCreateOrgRepo(orgID, uid int64) (bool, error) { - return db.GetEngine(db.DefaultContext). +func CanCreateOrgRepo(ctx context.Context, orgID, uid int64) (bool, error) { + return db.GetEngine(ctx). Where(builder.Eq{"team.can_create_org_repo": true}). Join("INNER", "team_user", "team_user.team_id = team.id"). And("team_user.uid = ?", uid). diff --git a/models/organization/team.go b/models/organization/team.go index 86ea30c8010..55d3f172766 100644 --- a/models/organization/team.go +++ b/models/organization/team.go @@ -242,18 +242,12 @@ func (t *Team) LoadMembers(ctx context.Context) (err error) { } // UnitEnabled returns if the team has the given unit type enabled -func (t *Team) UnitEnabled(tp unit.Type) bool { - return t.UnitAccessMode(tp) > perm.AccessModeNone +func (t *Team) UnitEnabled(ctx context.Context, tp unit.Type) bool { + return t.UnitAccessMode(ctx, tp) > perm.AccessModeNone } // UnitAccessMode returns if the team has the given unit type enabled -// it is called in templates, should not be replaced by `UnitAccessModeCtx(ctx ...)` -func (t *Team) UnitAccessMode(tp unit.Type) perm.AccessMode { // Notice: It will be used in template, don't remove it directly - return t.UnitAccessModeCtx(db.DefaultContext, tp) -} - -// UnitAccessModeCtx returns if the team has the given unit type enabled -func (t *Team) UnitAccessModeCtx(ctx context.Context, tp unit.Type) perm.AccessMode { +func (t *Team) UnitAccessMode(ctx context.Context, tp unit.Type) perm.AccessMode { if err := t.getUnits(ctx); err != nil { log.Warn("Error loading team (ID: %d) units: %s", t.ID, err.Error()) } diff --git a/models/perm/access/repo_permission.go b/models/perm/access/repo_permission.go index 88437dc4b25..a6bf9b6744a 100644 --- a/models/perm/access/repo_permission.go +++ b/models/perm/access/repo_permission.go @@ -244,7 +244,7 @@ func GetUserRepoPermission(ctx context.Context, repo *repo_model.Repository, use for _, u := range repo.Units { var found bool for _, team := range teams { - teamMode := team.UnitAccessModeCtx(ctx, u.Type) + teamMode := team.UnitAccessMode(ctx, u.Type) if teamMode > perm_model.AccessModeNone { m := perm.UnitsMode[u.Type] if m < teamMode { diff --git a/models/project/project.go b/models/project/project.go index 384f51c5ac2..bcf1166408f 100644 --- a/models/project/project.go +++ b/models/project/project.go @@ -298,44 +298,32 @@ func changeProjectStatus(ctx context.Context, p *Project, isClosed bool) error { return updateRepositoryProjectCount(ctx, p.RepoID) } -// DeleteProjectByID deletes a project from a repository. -func DeleteProjectByID(id int64) error { - ctx, committer, err := db.TxContext(db.DefaultContext) - if err != nil { - return err - } - defer committer.Close() - - if err := DeleteProjectByIDCtx(ctx, id); err != nil { - return err - } - - return committer.Commit() -} - -// DeleteProjectByIDCtx deletes a project from a repository. -func DeleteProjectByIDCtx(ctx context.Context, id int64) error { - p, err := GetProjectByID(ctx, id) - if err != nil { - if IsErrProjectNotExist(err) { - return nil +// DeleteProjectByID deletes a project from a repository. if it's not in a database +// transaction, it will start a new database transaction +func DeleteProjectByID(ctx context.Context, id int64) error { + return db.AutoTx(ctx, func(ctx context.Context) error { + p, err := GetProjectByID(ctx, id) + if err != nil { + if IsErrProjectNotExist(err) { + return nil + } + return err } - return err - } - if err := deleteProjectIssuesByProjectID(ctx, id); err != nil { - return err - } + if err := deleteProjectIssuesByProjectID(ctx, id); err != nil { + return err + } - if err := deleteBoardByProjectID(ctx, id); err != nil { - return err - } + if err := deleteBoardByProjectID(ctx, id); err != nil { + return err + } - if _, err = db.GetEngine(ctx).ID(p.ID).Delete(new(Project)); err != nil { - return err - } + if _, err = db.GetEngine(ctx).ID(p.ID).Delete(new(Project)); err != nil { + return err + } - return updateRepositoryProjectCount(ctx, p.RepoID) + return updateRepositoryProjectCount(ctx, p.RepoID) + }) } func DeleteProjectByRepoID(ctx context.Context, repoID int64) error { diff --git a/models/repo/collaboration.go b/models/repo/collaboration.go index bd49b9aec9a..29bcab70f36 100644 --- a/models/repo/collaboration.go +++ b/models/repo/collaboration.go @@ -99,55 +99,42 @@ func getCollaborations(ctx context.Context, repoID int64, listOptions db.ListOpt } // ChangeCollaborationAccessMode sets new access mode for the collaboration. -func ChangeCollaborationAccessModeCtx(ctx context.Context, repo *Repository, uid int64, mode perm.AccessMode) error { +func ChangeCollaborationAccessMode(ctx context.Context, repo *Repository, uid int64, mode perm.AccessMode) error { // Discard invalid input if mode <= perm.AccessModeNone || mode > perm.AccessModeOwner { return nil } - e := db.GetEngine(ctx) + return db.AutoTx(ctx, func(ctx context.Context) error { + e := db.GetEngine(ctx) + + collaboration := &Collaboration{ + RepoID: repo.ID, + UserID: uid, + } + has, err := e.Get(collaboration) + if err != nil { + return fmt.Errorf("get collaboration: %w", err) + } else if !has { + return nil + } + + if collaboration.Mode == mode { + return nil + } + collaboration.Mode = mode + + if _, err = e. + ID(collaboration.ID). + Cols("mode"). + Update(collaboration); err != nil { + return fmt.Errorf("update collaboration: %w", err) + } else if _, err = e.Exec("UPDATE access SET mode = ? WHERE user_id = ? AND repo_id = ?", mode, uid, repo.ID); err != nil { + return fmt.Errorf("update access table: %w", err) + } - collaboration := &Collaboration{ - RepoID: repo.ID, - UserID: uid, - } - has, err := e.Get(collaboration) - if err != nil { - return fmt.Errorf("get collaboration: %w", err) - } else if !has { return nil - } - - if collaboration.Mode == mode { - return nil - } - collaboration.Mode = mode - - if _, err = e. - ID(collaboration.ID). - Cols("mode"). - Update(collaboration); err != nil { - return fmt.Errorf("update collaboration: %w", err) - } else if _, err = e.Exec("UPDATE access SET mode = ? WHERE user_id = ? AND repo_id = ?", mode, uid, repo.ID); err != nil { - return fmt.Errorf("update access table: %w", err) - } - - return nil -} - -// ChangeCollaborationAccessMode sets new access mode for the collaboration. -func ChangeCollaborationAccessMode(repo *Repository, uid int64, mode perm.AccessMode) error { - ctx, committer, err := db.TxContext(db.DefaultContext) - if err != nil { - return err - } - defer committer.Close() - - if err := ChangeCollaborationAccessModeCtx(ctx, repo, uid, mode); err != nil { - return err - } - - return committer.Commit() + }) } // IsOwnerMemberCollaborator checks if a provided user is the owner, a collaborator or a member of a team in a repository diff --git a/models/repo/collaboration_test.go b/models/repo/collaboration_test.go index d240e9cbef0..0a6444de858 100644 --- a/models/repo/collaboration_test.go +++ b/models/repo/collaboration_test.go @@ -54,7 +54,7 @@ func TestRepository_ChangeCollaborationAccessMode(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 4}) - assert.NoError(t, repo_model.ChangeCollaborationAccessMode(repo, 4, perm.AccessModeAdmin)) + assert.NoError(t, repo_model.ChangeCollaborationAccessMode(db.DefaultContext, repo, 4, perm.AccessModeAdmin)) collaboration := unittest.AssertExistsAndLoadBean(t, &repo_model.Collaboration{RepoID: repo.ID, UserID: 4}) assert.EqualValues(t, perm.AccessModeAdmin, collaboration.Mode) @@ -62,9 +62,9 @@ func TestRepository_ChangeCollaborationAccessMode(t *testing.T) { access := unittest.AssertExistsAndLoadBean(t, &access_model.Access{UserID: 4, RepoID: repo.ID}) assert.EqualValues(t, perm.AccessModeAdmin, access.Mode) - assert.NoError(t, repo_model.ChangeCollaborationAccessMode(repo, 4, perm.AccessModeAdmin)) + assert.NoError(t, repo_model.ChangeCollaborationAccessMode(db.DefaultContext, repo, 4, perm.AccessModeAdmin)) - assert.NoError(t, repo_model.ChangeCollaborationAccessMode(repo, unittest.NonexistentID, perm.AccessModeAdmin)) + assert.NoError(t, repo_model.ChangeCollaborationAccessMode(db.DefaultContext, repo, unittest.NonexistentID, perm.AccessModeAdmin)) unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: repo.ID}) } diff --git a/models/repo/issue.go b/models/repo/issue.go index e27179caf51..6f6b565a00c 100644 --- a/models/repo/issue.go +++ b/models/repo/issue.go @@ -6,7 +6,6 @@ package repo import ( "context" - "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" @@ -26,44 +25,34 @@ func (repo *Repository) CanEnableTimetracker() bool { } // IsTimetrackerEnabled returns whether or not the timetracker is enabled. It returns the default value from config if an error occurs. -func (repo *Repository) IsTimetrackerEnabled() bool { // Notice: It will be used in template so don't remove directly - return repo.IsTimetrackerEnabledCtx(db.DefaultContext) -} - -// IsTimetrackerEnabledCtx returns whether or not the timetracker is enabled. It returns the default value from config if an error occurs. -func (repo *Repository) IsTimetrackerEnabledCtx(ctx context.Context) bool { +func (repo *Repository) IsTimetrackerEnabled(ctx context.Context) bool { if !setting.Service.EnableTimetracking { return false } var u *RepoUnit var err error - if u, err = repo.GetUnitCtx(ctx, unit.TypeIssues); err != nil { + if u, err = repo.GetUnit(ctx, unit.TypeIssues); err != nil { return setting.Service.DefaultEnableTimetracking } return u.IssuesConfig().EnableTimetracker } // AllowOnlyContributorsToTrackTime returns value of IssuesConfig or the default value -func (repo *Repository) AllowOnlyContributorsToTrackTime() bool { +func (repo *Repository) AllowOnlyContributorsToTrackTime(ctx context.Context) bool { var u *RepoUnit var err error - if u, err = repo.GetUnit(unit.TypeIssues); err != nil { + if u, err = repo.GetUnit(ctx, unit.TypeIssues); err != nil { return setting.Service.DefaultAllowOnlyContributorsToTrackTime } return u.IssuesConfig().AllowOnlyContributorsToTrackTime } // IsDependenciesEnabled returns if dependencies are enabled and returns the default setting if not set. -func (repo *Repository) IsDependenciesEnabled() bool { - return repo.IsDependenciesEnabledCtx(db.DefaultContext) -} - -// IsDependenciesEnabledCtx returns if dependencies are enabled and returns the default setting if not set. -func (repo *Repository) IsDependenciesEnabledCtx(ctx context.Context) bool { +func (repo *Repository) IsDependenciesEnabled(ctx context.Context) bool { var u *RepoUnit var err error - if u, err = repo.GetUnitCtx(ctx, unit.TypeIssues); err != nil { + if u, err = repo.GetUnit(ctx, unit.TypeIssues); err != nil { log.Trace("%s", err) return setting.Service.DefaultEnableDependencies } diff --git a/models/repo/release.go b/models/repo/release.go index c6cb7553445..25eba10e05d 100644 --- a/models/repo/release.go +++ b/models/repo/release.go @@ -286,8 +286,8 @@ func GetReleasesByRepoIDAndNames(ctx context.Context, repoID int64, tagNames []s } // GetReleaseCountByRepoID returns the count of releases of repository -func GetReleaseCountByRepoID(repoID int64, opts FindReleasesOptions) (int64, error) { - return db.GetEngine(db.DefaultContext).Where(opts.toConds(repoID)).Count(&Release{}) +func GetReleaseCountByRepoID(ctx context.Context, repoID int64, opts FindReleasesOptions) (int64, error) { + return db.GetEngine(ctx).Where(opts.toConds(repoID)).Count(&Release{}) } type releaseMetaSearch struct { diff --git a/models/repo/repo.go b/models/repo/repo.go index 028d713b32e..a2b3243ea09 100644 --- a/models/repo/repo.go +++ b/models/repo/repo.go @@ -316,12 +316,7 @@ func (repo *Repository) LoadUnits(ctx context.Context) (err error) { } // UnitEnabled if this repository has the given unit enabled -func (repo *Repository) UnitEnabled(tp unit.Type) (result bool) { // Notice: Don't remove this function directly, because it has been used in go template. - return repo.UnitEnabledCtx(db.DefaultContext, tp) -} - -// UnitEnabled if this repository has the given unit enabled -func (repo *Repository) UnitEnabledCtx(ctx context.Context, tp unit.Type) bool { +func (repo *Repository) UnitEnabled(ctx context.Context, tp unit.Type) bool { if err := repo.LoadUnits(ctx); err != nil { log.Warn("Error loading repository (ID: %d) units: %s", repo.ID, err.Error()) } @@ -334,8 +329,8 @@ func (repo *Repository) UnitEnabledCtx(ctx context.Context, tp unit.Type) bool { } // MustGetUnit always returns a RepoUnit object -func (repo *Repository) MustGetUnit(tp unit.Type) *RepoUnit { - ru, err := repo.GetUnit(tp) +func (repo *Repository) MustGetUnit(ctx context.Context, tp unit.Type) *RepoUnit { + ru, err := repo.GetUnit(ctx, tp) if err == nil { return ru } @@ -368,12 +363,7 @@ func (repo *Repository) MustGetUnit(tp unit.Type) *RepoUnit { } // GetUnit returns a RepoUnit object -func (repo *Repository) GetUnit(tp unit.Type) (*RepoUnit, error) { - return repo.GetUnitCtx(db.DefaultContext, tp) -} - -// GetUnitCtx returns a RepoUnit object -func (repo *Repository) GetUnitCtx(ctx context.Context, tp unit.Type) (*RepoUnit, error) { +func (repo *Repository) GetUnit(ctx context.Context, tp unit.Type) (*RepoUnit, error) { if err := repo.LoadUnits(ctx); err != nil { return nil, err } @@ -420,7 +410,7 @@ func (repo *Repository) ComposeMetas() map[string]string { "mode": "comment", } - unit, err := repo.GetUnit(unit.TypeExternalTracker) + unit, err := repo.GetUnit(db.DefaultContext, unit.TypeExternalTracker) if err == nil { metas["format"] = unit.ExternalTrackerConfig().ExternalTrackerFormat switch unit.ExternalTrackerConfig().ExternalTrackerStyle { @@ -558,7 +548,7 @@ func (repo *Repository) CanEnablePulls() bool { // AllowsPulls returns true if repository meets the requirements of accepting pulls and has them enabled. func (repo *Repository) AllowsPulls() bool { - return repo.CanEnablePulls() && repo.UnitEnabled(unit.TypePullRequests) + return repo.CanEnablePulls() && repo.UnitEnabled(db.DefaultContext, unit.TypePullRequests) } // CanEnableEditor returns true if repository meets the requirements of web editor. diff --git a/models/repo/repo_list_test.go b/models/repo/repo_list_test.go index bfbceb7e2ec..d87d70c189a 100644 --- a/models/repo/repo_list_test.go +++ b/models/repo/repo_list_test.go @@ -235,12 +235,12 @@ func TestSearchRepository(t *testing.T) { { name: "AllPublic/PublicRepositoriesOfUserIncludingCollaborative", opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, AllPublic: true, Template: util.OptionalBoolFalse}, - count: 28, + count: 29, }, { name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborative", opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 15, Private: true, AllPublic: true, AllLimited: true, Template: util.OptionalBoolFalse}, - count: 33, + count: 34, }, { name: "AllPublic/PublicAndPrivateRepositoriesOfUserIncludingCollaborativeByName", @@ -255,7 +255,7 @@ func TestSearchRepository(t *testing.T) { { name: "AllPublic/PublicRepositoriesOfOrganization", opts: &repo_model.SearchRepoOptions{ListOptions: db.ListOptions{Page: 1, PageSize: 10}, OwnerID: 17, AllPublic: true, Collaborate: util.OptionalBoolFalse, Template: util.OptionalBoolFalse}, - count: 28, + count: 29, }, { name: "AllTemplates", diff --git a/models/repo_transfer.go b/models/repo_transfer.go index 362253bdc8a..19e6c066272 100644 --- a/models/repo_transfer.go +++ b/models/repo_transfer.go @@ -39,9 +39,9 @@ func init() { } // LoadAttributes fetches the transfer recipient from the database -func (r *RepoTransfer) LoadAttributes() error { +func (r *RepoTransfer) LoadAttributes(ctx context.Context) error { if r.Recipient == nil { - u, err := user_model.GetUserByID(db.DefaultContext, r.RecipientID) + u, err := user_model.GetUserByID(ctx, r.RecipientID) if err != nil { return err } @@ -51,7 +51,7 @@ func (r *RepoTransfer) LoadAttributes() error { if r.Recipient.IsOrganization() && len(r.TeamIDs) != len(r.Teams) { for _, v := range r.TeamIDs { - team, err := organization.GetTeamByID(db.DefaultContext, v) + team, err := organization.GetTeamByID(ctx, v) if err != nil { return err } @@ -65,7 +65,7 @@ func (r *RepoTransfer) LoadAttributes() error { } if r.Doer == nil { - u, err := user_model.GetUserByID(db.DefaultContext, r.DoerID) + u, err := user_model.GetUserByID(ctx, r.DoerID) if err != nil { return err } @@ -80,7 +80,7 @@ func (r *RepoTransfer) LoadAttributes() error { // For user, it checks if it's himself // For organizations, it checks if the user is able to create repos func (r *RepoTransfer) CanUserAcceptTransfer(u *user_model.User) bool { - if err := r.LoadAttributes(); err != nil { + if err := r.LoadAttributes(db.DefaultContext); err != nil { log.Error("LoadAttributes: %v", err) return false } @@ -89,7 +89,7 @@ func (r *RepoTransfer) CanUserAcceptTransfer(u *user_model.User) bool { return r.RecipientID == u.ID } - allowed, err := organization.CanCreateOrgRepo(r.RecipientID, u.ID) + allowed, err := organization.CanCreateOrgRepo(db.DefaultContext, r.RecipientID, u.ID) if err != nil { log.Error("CanCreateOrgRepo: %v", err) return false @@ -100,10 +100,10 @@ func (r *RepoTransfer) CanUserAcceptTransfer(u *user_model.User) bool { // GetPendingRepositoryTransfer fetches the most recent and ongoing transfer // process for the repository -func GetPendingRepositoryTransfer(repo *repo_model.Repository) (*RepoTransfer, error) { +func GetPendingRepositoryTransfer(ctx context.Context, repo *repo_model.Repository) (*RepoTransfer, error) { transfer := new(RepoTransfer) - has, err := db.GetEngine(db.DefaultContext).Where("repo_id = ? ", repo.ID).Get(transfer) + has, err := db.GetEngine(ctx).Where("repo_id = ? ", repo.ID).Get(transfer) if err != nil { return nil, err } @@ -154,56 +154,48 @@ func TestRepositoryReadyForTransfer(status repo_model.RepositoryStatus) error { // CreatePendingRepositoryTransfer transfer a repo from one owner to a new one. // it marks the repository transfer as "pending" -func CreatePendingRepositoryTransfer(doer, newOwner *user_model.User, repoID int64, teams []*organization.Team) error { - ctx, committer, err := db.TxContext(db.DefaultContext) - if err != nil { - return err - } - defer committer.Close() - - repo, err := repo_model.GetRepositoryByID(ctx, repoID) - if err != nil { - return err - } - - // Make sure repo is ready to transfer - if err := TestRepositoryReadyForTransfer(repo.Status); err != nil { - return err - } - - repo.Status = repo_model.RepositoryPendingTransfer - if err := repo_model.UpdateRepositoryCols(ctx, repo, "status"); err != nil { - return err - } - - // Check if new owner has repository with same name. - if has, err := repo_model.IsRepositoryExist(ctx, newOwner, repo.Name); err != nil { - return fmt.Errorf("IsRepositoryExist: %w", err) - } else if has { - return repo_model.ErrRepoAlreadyExist{ - Uname: newOwner.LowerName, - Name: repo.Name, +func CreatePendingRepositoryTransfer(ctx context.Context, doer, newOwner *user_model.User, repoID int64, teams []*organization.Team) error { + return db.AutoTx(ctx, func(ctx context.Context) error { + repo, err := repo_model.GetRepositoryByID(ctx, repoID) + if err != nil { + return err } - } - transfer := &RepoTransfer{ - RepoID: repo.ID, - RecipientID: newOwner.ID, - CreatedUnix: timeutil.TimeStampNow(), - UpdatedUnix: timeutil.TimeStampNow(), - DoerID: doer.ID, - TeamIDs: make([]int64, 0, len(teams)), - } + // Make sure repo is ready to transfer + if err := TestRepositoryReadyForTransfer(repo.Status); err != nil { + return err + } - for k := range teams { - transfer.TeamIDs = append(transfer.TeamIDs, teams[k].ID) - } + repo.Status = repo_model.RepositoryPendingTransfer + if err := repo_model.UpdateRepositoryCols(ctx, repo, "status"); err != nil { + return err + } - if err := db.Insert(ctx, transfer); err != nil { - return err - } + // Check if new owner has repository with same name. + if has, err := repo_model.IsRepositoryExist(ctx, newOwner, repo.Name); err != nil { + return fmt.Errorf("IsRepositoryExist: %w", err) + } else if has { + return repo_model.ErrRepoAlreadyExist{ + Uname: newOwner.LowerName, + Name: repo.Name, + } + } - return committer.Commit() + transfer := &RepoTransfer{ + RepoID: repo.ID, + RecipientID: newOwner.ID, + CreatedUnix: timeutil.TimeStampNow(), + UpdatedUnix: timeutil.TimeStampNow(), + DoerID: doer.ID, + TeamIDs: make([]int64, 0, len(teams)), + } + + for k := range teams { + transfer.TeamIDs = append(transfer.TeamIDs, teams[k].ID) + } + + return db.Insert(ctx, transfer) + }) } // TransferOwnership transfers all corresponding repository items from old user to new one. diff --git a/models/repo_transfer_test.go b/models/repo_transfer_test.go index 9720071a989..8352adc9489 100644 --- a/models/repo_transfer_test.go +++ b/models/repo_transfer_test.go @@ -6,6 +6,7 @@ package models import ( "testing" + "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" @@ -19,36 +20,36 @@ func TestRepositoryTransfer(t *testing.T) { doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}) - transfer, err := GetPendingRepositoryTransfer(repo) + transfer, err := GetPendingRepositoryTransfer(db.DefaultContext, repo) assert.NoError(t, err) assert.NotNil(t, transfer) // Cancel transfer assert.NoError(t, CancelRepositoryTransfer(repo)) - transfer, err = GetPendingRepositoryTransfer(repo) + transfer, err = GetPendingRepositoryTransfer(db.DefaultContext, repo) assert.Error(t, err) assert.Nil(t, transfer) assert.True(t, IsErrNoPendingTransfer(err)) user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) - assert.NoError(t, CreatePendingRepositoryTransfer(doer, user2, repo.ID, nil)) + assert.NoError(t, CreatePendingRepositoryTransfer(db.DefaultContext, doer, user2, repo.ID, nil)) - transfer, err = GetPendingRepositoryTransfer(repo) + transfer, err = GetPendingRepositoryTransfer(db.DefaultContext, repo) assert.Nil(t, err) - assert.NoError(t, transfer.LoadAttributes()) + assert.NoError(t, transfer.LoadAttributes(db.DefaultContext)) assert.Equal(t, "user2", transfer.Recipient.Name) user6 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) // Only transfer can be started at any given time - err = CreatePendingRepositoryTransfer(doer, user6, repo.ID, nil) + err = CreatePendingRepositoryTransfer(db.DefaultContext, doer, user6, repo.ID, nil) assert.Error(t, err) assert.True(t, IsErrRepoTransferInProgress(err)) // Unknown user - err = CreatePendingRepositoryTransfer(doer, &user_model.User{ID: 1000, LowerName: "user1000"}, repo.ID, nil) + err = CreatePendingRepositoryTransfer(db.DefaultContext, doer, &user_model.User{ID: 1000, LowerName: "user1000"}, repo.ID, nil) assert.Error(t, err) // Cancel transfer diff --git a/modules/context/package.go b/modules/context/package.go index 65a9d74b775..2a55db3a77f 100644 --- a/modules/context/package.go +++ b/modules/context/package.go @@ -110,7 +110,7 @@ func determineAccessMode(ctx *Context) (perm.AccessMode, error) { return accessMode, err } for _, t := range teams { - perm := t.UnitAccessModeCtx(ctx, unit.TypePackages) + perm := t.UnitAccessMode(ctx, unit.TypePackages) if accessMode < perm { accessMode = perm } diff --git a/modules/context/repo.go b/modules/context/repo.go index a50cb15abe2..71a2b3c0c6e 100644 --- a/modules/context/repo.go +++ b/modules/context/repo.go @@ -163,13 +163,13 @@ func (r *Repository) CanUseTimetracker(issue *issues_model.Issue, user *user_mod // 1. Is timetracker enabled // 2. Is the user a contributor, admin, poster or assignee and do the repository policies require this? isAssigned, _ := issues_model.IsUserAssignedToIssue(db.DefaultContext, issue, user) - return r.Repository.IsTimetrackerEnabled() && (!r.Repository.AllowOnlyContributorsToTrackTime() || + return r.Repository.IsTimetrackerEnabled(db.DefaultContext) && (!r.Repository.AllowOnlyContributorsToTrackTime(db.DefaultContext) || r.Permission.CanWriteIssuesOrPulls(issue.IsPull) || issue.IsPoster(user.ID) || isAssigned) } // CanCreateIssueDependencies returns whether or not a user can create dependencies. func (r *Repository) CanCreateIssueDependencies(user *user_model.User, isPull bool) bool { - return r.Repository.IsDependenciesEnabled() && r.Permission.CanWriteIssuesOrPulls(isPull) + return r.Repository.IsDependenciesEnabled(db.DefaultContext) && r.Permission.CanWriteIssuesOrPulls(isPull) } // GetCommitsCount returns cached commit count for current view @@ -528,12 +528,12 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) { ctx.Data["RepoLink"] = ctx.Repo.RepoLink ctx.Data["RepoRelPath"] = ctx.Repo.Owner.Name + "/" + ctx.Repo.Repository.Name - unit, err := ctx.Repo.Repository.GetUnit(unit_model.TypeExternalTracker) + unit, err := ctx.Repo.Repository.GetUnit(ctx, unit_model.TypeExternalTracker) if err == nil { ctx.Data["RepoExternalIssuesLink"] = unit.ExternalTrackerConfig().ExternalTrackerURL } - ctx.Data["NumTags"], err = repo_model.GetReleaseCountByRepoID(ctx.Repo.Repository.ID, repo_model.FindReleasesOptions{ + ctx.Data["NumTags"], err = repo_model.GetReleaseCountByRepoID(ctx, ctx.Repo.Repository.ID, repo_model.FindReleasesOptions{ IncludeDrafts: true, IncludeTags: true, HasSha1: util.OptionalBoolTrue, // only draft releases which are created with existing tags @@ -542,7 +542,7 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) { ctx.ServerError("GetReleaseCountByRepoID", err) return } - ctx.Data["NumReleases"], err = repo_model.GetReleaseCountByRepoID(ctx.Repo.Repository.ID, repo_model.FindReleasesOptions{}) + ctx.Data["NumReleases"], err = repo_model.GetReleaseCountByRepoID(ctx, ctx.Repo.Repository.ID, repo_model.FindReleasesOptions{}) if err != nil { ctx.ServerError("GetReleaseCountByRepoID", err) return @@ -723,13 +723,13 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) { ctx.Data["PullRequestCtx"] = ctx.Repo.PullRequest if ctx.Repo.Repository.Status == repo_model.RepositoryPendingTransfer { - repoTransfer, err := models.GetPendingRepositoryTransfer(ctx.Repo.Repository) + repoTransfer, err := models.GetPendingRepositoryTransfer(ctx, ctx.Repo.Repository) if err != nil { ctx.ServerError("GetPendingRepositoryTransfer", err) return } - if err := repoTransfer.LoadAttributes(); err != nil { + if err := repoTransfer.LoadAttributes(ctx); err != nil { ctx.ServerError("LoadRecipient", err) return } diff --git a/modules/convert/attachment.go b/modules/convert/attachment.go new file mode 100644 index 00000000000..ddba181a120 --- /dev/null +++ b/modules/convert/attachment.go @@ -0,0 +1,30 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package convert + +import ( + repo_model "code.gitea.io/gitea/models/repo" + api "code.gitea.io/gitea/modules/structs" +) + +// ToAttachment converts models.Attachment to api.Attachment +func ToAttachment(a *repo_model.Attachment) *api.Attachment { + return &api.Attachment{ + ID: a.ID, + Name: a.Name, + Created: a.CreatedUnix.AsTime(), + DownloadCount: a.DownloadCount, + Size: a.Size, + UUID: a.UUID, + DownloadURL: a.DownloadURL(), + } +} + +func ToAttachments(attachments []*repo_model.Attachment) []*api.Attachment { + converted := make([]*api.Attachment, 0, len(attachments)) + for _, attachment := range attachments { + converted = append(converted, ToAttachment(attachment)) + } + return converted +} diff --git a/modules/convert/issue.go b/modules/convert/issue.go index 3bc10065073..f3af03ed949 100644 --- a/modules/convert/issue.go +++ b/modules/convert/issue.go @@ -37,20 +37,21 @@ func ToAPIIssue(ctx context.Context, issue *issues_model.Issue) *api.Issue { } apiIssue := &api.Issue{ - ID: issue.ID, - URL: issue.APIURL(), - HTMLURL: issue.HTMLURL(), - Index: issue.Index, - Poster: ToUser(issue.Poster, nil), - Title: issue.Title, - Body: issue.Content, - Ref: issue.Ref, - Labels: ToLabelList(issue.Labels, issue.Repo, issue.Repo.Owner), - State: issue.State(), - IsLocked: issue.IsLocked, - Comments: issue.NumComments, - Created: issue.CreatedUnix.AsTime(), - Updated: issue.UpdatedUnix.AsTime(), + ID: issue.ID, + URL: issue.APIURL(), + HTMLURL: issue.HTMLURL(), + Index: issue.Index, + Poster: ToUser(issue.Poster, nil), + Title: issue.Title, + Body: issue.Content, + Attachments: ToAttachments(issue.Attachments), + Ref: issue.Ref, + Labels: ToLabelList(issue.Labels, issue.Repo, issue.Repo.Owner), + State: issue.State(), + IsLocked: issue.IsLocked, + Comments: issue.NumComments, + Created: issue.CreatedUnix.AsTime(), + Updated: issue.UpdatedUnix.AsTime(), } apiIssue.Repo = &api.RepositoryMeta{ diff --git a/modules/convert/issue_comment.go b/modules/convert/issue_comment.go index c4fed6b8a1f..6044cbcf613 100644 --- a/modules/convert/issue_comment.go +++ b/modules/convert/issue_comment.go @@ -16,14 +16,15 @@ import ( // ToComment converts a issues_model.Comment to the api.Comment format func ToComment(c *issues_model.Comment) *api.Comment { return &api.Comment{ - ID: c.ID, - Poster: ToUser(c.Poster, nil), - HTMLURL: c.HTMLURL(), - IssueURL: c.IssueURL(), - PRURL: c.PRURL(), - Body: c.Content, - Created: c.CreatedUnix.AsTime(), - Updated: c.UpdatedUnix.AsTime(), + ID: c.ID, + Poster: ToUser(c.Poster, nil), + HTMLURL: c.HTMLURL(), + IssueURL: c.IssueURL(), + PRURL: c.PRURL(), + Body: c.Content, + Attachments: ToAttachments(c.Attachments), + Created: c.CreatedUnix.AsTime(), + Updated: c.UpdatedUnix.AsTime(), } } @@ -148,7 +149,7 @@ func ToTimelineComment(ctx context.Context, c *issues_model.Comment, doer *user_ var err error repo, err = repo_model.GetRepositoryByID(ctx, c.Label.RepoID) if err != nil { - log.Error("GetRepositoryByIDCtx(%d): %v", c.Label.RepoID, err) + log.Error("GetRepositoryByID(%d): %v", c.Label.RepoID, err) return nil } } diff --git a/modules/convert/release.go b/modules/convert/release.go index 95c6d03ab13..3afa53c03f5 100644 --- a/modules/convert/release.go +++ b/modules/convert/release.go @@ -10,10 +10,6 @@ import ( // ToRelease convert a repo_model.Release to api.Release func ToRelease(r *repo_model.Release) *api.Release { - assets := make([]*api.Attachment, 0) - for _, att := range r.Attachments { - assets = append(assets, ToReleaseAttachment(att)) - } return &api.Release{ ID: r.ID, TagName: r.TagName, @@ -29,19 +25,6 @@ func ToRelease(r *repo_model.Release) *api.Release { CreatedAt: r.CreatedUnix.AsTime(), PublishedAt: r.CreatedUnix.AsTime(), Publisher: ToUser(r.Publisher, nil), - Attachments: assets, - } -} - -// ToReleaseAttachment converts models.Attachment to api.Attachment -func ToReleaseAttachment(a *repo_model.Attachment) *api.Attachment { - return &api.Attachment{ - ID: a.ID, - Name: a.Name, - Created: a.CreatedUnix.AsTime(), - DownloadCount: a.DownloadCount, - Size: a.Size, - UUID: a.UUID, - DownloadURL: a.DownloadURL(), + Attachments: ToAttachments(r.Attachments), } } diff --git a/modules/convert/repository.go b/modules/convert/repository.go index 843556e1b12..ce53a666923 100644 --- a/modules/convert/repository.go +++ b/modules/convert/repository.go @@ -8,7 +8,6 @@ import ( "time" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/perm" repo_model "code.gitea.io/gitea/models/repo" unit_model "code.gitea.io/gitea/models/unit" @@ -44,7 +43,7 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, mode perm.Acc hasIssues := false var externalTracker *api.ExternalTracker var internalTracker *api.InternalTracker - if unit, err := repo.GetUnit(unit_model.TypeIssues); err == nil { + if unit, err := repo.GetUnit(ctx, unit_model.TypeIssues); err == nil { config := unit.IssuesConfig() hasIssues = true internalTracker = &api.InternalTracker{ @@ -52,7 +51,7 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, mode perm.Acc AllowOnlyContributorsToTrackTime: config.AllowOnlyContributorsToTrackTime, EnableIssueDependencies: config.EnableDependencies, } - } else if unit, err := repo.GetUnit(unit_model.TypeExternalTracker); err == nil { + } else if unit, err := repo.GetUnit(ctx, unit_model.TypeExternalTracker); err == nil { config := unit.ExternalTrackerConfig() hasIssues = true externalTracker = &api.ExternalTracker{ @@ -64,9 +63,9 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, mode perm.Acc } hasWiki := false var externalWiki *api.ExternalWiki - if _, err := repo.GetUnit(unit_model.TypeWiki); err == nil { + if _, err := repo.GetUnit(ctx, unit_model.TypeWiki); err == nil { hasWiki = true - } else if unit, err := repo.GetUnit(unit_model.TypeExternalWiki); err == nil { + } else if unit, err := repo.GetUnit(ctx, unit_model.TypeExternalWiki); err == nil { hasWiki = true config := unit.ExternalWikiConfig() externalWiki = &api.ExternalWiki{ @@ -82,7 +81,7 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, mode perm.Acc allowRebaseUpdate := false defaultDeleteBranchAfterMerge := false defaultMergeStyle := repo_model.MergeStyleMerge - if unit, err := repo.GetUnit(unit_model.TypePullRequests); err == nil { + if unit, err := repo.GetUnit(ctx, unit_model.TypePullRequests); err == nil { config := unit.PullRequestsConfig() hasPullRequests = true ignoreWhitespaceConflicts = config.IgnoreWhitespaceConflicts @@ -95,21 +94,21 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, mode perm.Acc defaultMergeStyle = config.GetDefaultMergeStyle() } hasProjects := false - if _, err := repo.GetUnit(unit_model.TypeProjects); err == nil { + if _, err := repo.GetUnit(ctx, unit_model.TypeProjects); err == nil { hasProjects = true } - if err := repo.GetOwner(db.DefaultContext); err != nil { + if err := repo.GetOwner(ctx); err != nil { return nil } - numReleases, _ := repo_model.GetReleaseCountByRepoID(repo.ID, repo_model.FindReleasesOptions{IncludeDrafts: false, IncludeTags: false}) + numReleases, _ := repo_model.GetReleaseCountByRepoID(ctx, repo.ID, repo_model.FindReleasesOptions{IncludeDrafts: false, IncludeTags: false}) mirrorInterval := "" var mirrorUpdated time.Time if repo.IsMirror { var err error - repo.Mirror, err = repo_model.GetMirrorByRepoID(db.DefaultContext, repo.ID) + repo.Mirror, err = repo_model.GetMirrorByRepoID(ctx, repo.ID) if err == nil { mirrorInterval = repo.Mirror.Interval.String() mirrorUpdated = repo.Mirror.UpdatedUnix.AsTime() @@ -118,11 +117,11 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, mode perm.Acc var transfer *api.RepoTransfer if repo.Status == repo_model.RepositoryPendingTransfer { - t, err := models.GetPendingRepositoryTransfer(repo) + t, err := models.GetPendingRepositoryTransfer(ctx, repo) if err != nil && !models.IsErrNoPendingTransfer(err) { log.Warn("GetPendingRepositoryTransfer: %v", err) } else { - if err := t.LoadAttributes(); err != nil { + if err := t.LoadAttributes(ctx); err != nil { log.Warn("LoadAttributes of RepoTransfer: %v", err) } else { transfer = ToRepoTransfer(t) diff --git a/modules/markup/html.go b/modules/markup/html.go index 80b19ba35f2..6b5a8e32d4e 100644 --- a/modules/markup/html.go +++ b/modules/markup/html.go @@ -305,18 +305,15 @@ func postProcess(ctx *RenderContext, procs []processor, input io.Reader, output return err } - res := bytes.NewBuffer(make([]byte, 0, len(rawHTML)+50)) - // prepend "
" - _, _ = res.WriteString("") - - // Strip out nuls - they're always invalid - _, _ = res.Write(tagCleaner.ReplaceAll([]byte(nulCleaner.Replace(string(rawHTML))), []byte("<$1"))) - - // close the tags - _, _ = res.WriteString("") - // parse the HTML - node, err := html.Parse(res) + node, err := html.Parse(io.MultiReader( + // prepend "" + strings.NewReader(""), + // Strip out nuls - they're always invalid + bytes.NewReader(tagCleaner.ReplaceAll([]byte(nulCleaner.Replace(string(rawHTML))), []byte("<$1"))), + // close the tags + strings.NewReader(""), + )) if err != nil { return &postProcessError{"invalid HTML", err} } diff --git a/modules/notification/webhook/webhook.go b/modules/notification/webhook/webhook.go index 63345830585..cf056f54c13 100644 --- a/modules/notification/webhook/webhook.go +++ b/modules/notification/webhook/webhook.go @@ -314,6 +314,11 @@ func (m *webhookNotifier) NotifyNewPullRequest(ctx context.Context, pull *issues } func (m *webhookNotifier) NotifyIssueChangeContent(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, oldContent string) { + if err := issue.LoadRepo(ctx); err != nil { + log.Error("LoadRepo: %v", err) + return + } + mode, _ := access_model.AccessLevel(ctx, issue.Poster, issue.Repo) var err error if issue.IsPull { diff --git a/modules/repository/collaborator.go b/modules/repository/collaborator.go index 44c03f999ee..f2b95151879 100644 --- a/modules/repository/collaborator.go +++ b/modules/repository/collaborator.go @@ -13,30 +13,25 @@ import ( user_model "code.gitea.io/gitea/models/user" ) -func addCollaborator(ctx context.Context, repo *repo_model.Repository, u *user_model.User) error { - collaboration := &repo_model.Collaboration{ - RepoID: repo.ID, - UserID: u.ID, - } +func AddCollaborator(ctx context.Context, repo *repo_model.Repository, u *user_model.User) error { + return db.AutoTx(ctx, func(ctx context.Context) error { + collaboration := &repo_model.Collaboration{ + RepoID: repo.ID, + UserID: u.ID, + } - has, err := db.GetByBean(ctx, collaboration) - if err != nil { - return err - } else if has { - return nil - } - collaboration.Mode = perm.AccessModeWrite + has, err := db.GetByBean(ctx, collaboration) + if err != nil { + return err + } else if has { + return nil + } + collaboration.Mode = perm.AccessModeWrite - if err = db.Insert(ctx, collaboration); err != nil { - return err - } + if err = db.Insert(ctx, collaboration); err != nil { + return err + } - return access_model.RecalculateUserAccess(ctx, repo, u.ID) -} - -// AddCollaborator adds new collaboration to a repository with default access mode. -func AddCollaborator(repo *repo_model.Repository, u *user_model.User) error { - return db.WithTx(db.DefaultContext, func(ctx context.Context) error { - return addCollaborator(ctx, repo, u) + return access_model.RecalculateUserAccess(ctx, repo, u.ID) }) } diff --git a/modules/repository/collaborator_test.go b/modules/repository/collaborator_test.go index ad835ae4d42..6cf239d0ead 100644 --- a/modules/repository/collaborator_test.go +++ b/modules/repository/collaborator_test.go @@ -25,7 +25,7 @@ func TestRepository_AddCollaborator(t *testing.T) { repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID}) assert.NoError(t, repo.GetOwner(db.DefaultContext)) user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userID}) - assert.NoError(t, AddCollaborator(repo, user)) + assert.NoError(t, AddCollaborator(db.DefaultContext, repo, user)) unittest.CheckConsistencyFor(t, &repo_model.Repository{ID: repoID}, &user_model.User{ID: userID}) } testSuccess(1, 4) @@ -50,7 +50,7 @@ func TestRepoPermissionPublicNonOrgRepo(t *testing.T) { } // change to collaborator - assert.NoError(t, AddCollaborator(repo, user)) + assert.NoError(t, AddCollaborator(db.DefaultContext, repo, user)) perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user) assert.NoError(t, err) for _, unit := range repo.Units { @@ -103,7 +103,7 @@ func TestRepoPermissionPrivateNonOrgRepo(t *testing.T) { } // change to collaborator to default write access - assert.NoError(t, AddCollaborator(repo, user)) + assert.NoError(t, AddCollaborator(db.DefaultContext, repo, user)) perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user) assert.NoError(t, err) for _, unit := range repo.Units { @@ -111,7 +111,7 @@ func TestRepoPermissionPrivateNonOrgRepo(t *testing.T) { assert.True(t, perm.CanWrite(unit.Type)) } - assert.NoError(t, repo_model.ChangeCollaborationAccessMode(repo, user.ID, perm_model.AccessModeRead)) + assert.NoError(t, repo_model.ChangeCollaborationAccessMode(db.DefaultContext, repo, user.ID, perm_model.AccessModeRead)) perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user) assert.NoError(t, err) for _, unit := range repo.Units { @@ -155,7 +155,7 @@ func TestRepoPermissionPublicOrgRepo(t *testing.T) { } // change to collaborator to default write access - assert.NoError(t, AddCollaborator(repo, user)) + assert.NoError(t, AddCollaborator(db.DefaultContext, repo, user)) perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user) assert.NoError(t, err) for _, unit := range repo.Units { @@ -163,7 +163,7 @@ func TestRepoPermissionPublicOrgRepo(t *testing.T) { assert.True(t, perm.CanWrite(unit.Type)) } - assert.NoError(t, repo_model.ChangeCollaborationAccessMode(repo, user.ID, perm_model.AccessModeRead)) + assert.NoError(t, repo_model.ChangeCollaborationAccessMode(db.DefaultContext, repo, user.ID, perm_model.AccessModeRead)) perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user) assert.NoError(t, err) for _, unit := range repo.Units { @@ -217,7 +217,7 @@ func TestRepoPermissionPrivateOrgRepo(t *testing.T) { } // change to collaborator to default write access - assert.NoError(t, AddCollaborator(repo, user)) + assert.NoError(t, AddCollaborator(db.DefaultContext, repo, user)) perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user) assert.NoError(t, err) for _, unit := range repo.Units { @@ -225,7 +225,7 @@ func TestRepoPermissionPrivateOrgRepo(t *testing.T) { assert.True(t, perm.CanWrite(unit.Type)) } - assert.NoError(t, repo_model.ChangeCollaborationAccessMode(repo, user.ID, perm_model.AccessModeRead)) + assert.NoError(t, repo_model.ChangeCollaborationAccessMode(db.DefaultContext, repo, user.ID, perm_model.AccessModeRead)) perm, err = access_model.GetUserRepoPermission(db.DefaultContext, repo, user) assert.NoError(t, err) for _, unit := range repo.Units { diff --git a/modules/repository/create.go b/modules/repository/create.go index 3324b357ca4..423250add32 100644 --- a/modules/repository/create.go +++ b/modules/repository/create.go @@ -125,10 +125,10 @@ func CreateRepositoryByExample(ctx context.Context, doer, u *user_model.User, re return fmt.Errorf("IsUserRepoAdmin: %w", err) } else if !isAdmin { // Make creator repo admin if it wasn't assigned automatically - if err = addCollaborator(ctx, repo, doer); err != nil { - return fmt.Errorf("addCollaborator: %w", err) + if err = AddCollaborator(ctx, repo, doer); err != nil { + return fmt.Errorf("AddCollaborator: %w", err) } - if err = repo_model.ChangeCollaborationAccessModeCtx(ctx, repo, doer.ID, perm.AccessModeAdmin); err != nil { + if err = repo_model.ChangeCollaborationAccessMode(ctx, repo, doer.ID, perm.AccessModeAdmin); err != nil { return fmt.Errorf("ChangeCollaborationAccessModeCtx: %w", err) } } diff --git a/modules/structs/issue.go b/modules/structs/issue.go index 00166b7a07e..48e4e0e7e36 100644 --- a/modules/structs/issue.go +++ b/modules/structs/issue.go @@ -41,18 +41,19 @@ type RepositoryMeta struct { // Issue represents an issue in a repository // swagger:model type Issue struct { - ID int64 `json:"id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - Index int64 `json:"number"` - Poster *User `json:"user"` - OriginalAuthor string `json:"original_author"` - OriginalAuthorID int64 `json:"original_author_id"` - Title string `json:"title"` - Body string `json:"body"` - Ref string `json:"ref"` - Labels []*Label `json:"labels"` - Milestone *Milestone `json:"milestone"` + ID int64 `json:"id"` + URL string `json:"url"` + HTMLURL string `json:"html_url"` + Index int64 `json:"number"` + Poster *User `json:"user"` + OriginalAuthor string `json:"original_author"` + OriginalAuthorID int64 `json:"original_author_id"` + Title string `json:"title"` + Body string `json:"body"` + Ref string `json:"ref"` + Attachments []*Attachment `json:"assets"` + Labels []*Label `json:"labels"` + Milestone *Milestone `json:"milestone"` // deprecated Assignee *User `json:"assignee"` Assignees []*User `json:"assignees"` diff --git a/modules/structs/issue_comment.go b/modules/structs/issue_comment.go index 4a1085ba501..9e8f5c4bf33 100644 --- a/modules/structs/issue_comment.go +++ b/modules/structs/issue_comment.go @@ -9,14 +9,15 @@ import ( // Comment represents a comment on a commit or issue type Comment struct { - ID int64 `json:"id"` - HTMLURL string `json:"html_url"` - PRURL string `json:"pull_request_url"` - IssueURL string `json:"issue_url"` - Poster *User `json:"user"` - OriginalAuthor string `json:"original_author"` - OriginalAuthorID int64 `json:"original_author_id"` - Body string `json:"body"` + ID int64 `json:"id"` + HTMLURL string `json:"html_url"` + PRURL string `json:"pull_request_url"` + IssueURL string `json:"issue_url"` + Poster *User `json:"user"` + OriginalAuthor string `json:"original_author"` + OriginalAuthorID int64 `json:"original_author_id"` + Body string `json:"body"` + Attachments []*Attachment `json:"assets"` // swagger:strfmt date-time Created time.Time `json:"created_at"` // swagger:strfmt date-time diff --git a/modules/web/route.go b/modules/web/route.go index cd72aabae5e..0f2fdc33b5a 100644 --- a/modules/web/route.go +++ b/modules/web/route.go @@ -7,7 +7,6 @@ import ( goctx "context" "fmt" "net/http" - "reflect" "strings" "code.gitea.io/gitea/modules/context" @@ -18,16 +17,9 @@ import ( ) // Bind binding an obj to a handler -func Bind(obj interface{}) http.HandlerFunc { - tp := reflect.TypeOf(obj) - if tp.Kind() == reflect.Ptr { - tp = tp.Elem() - } - if tp.Kind() != reflect.Struct { - panic("Only structs are allowed to bind") - } +func Bind[T any](obj T) http.HandlerFunc { return Wrap(func(ctx *context.Context) { - theObj := reflect.New(tp).Interface() // create a new form obj for every request but not use obj directly + theObj := new(T) // create a new form obj for every request but not use obj directly binding.Bind(ctx.Req, theObj) SetForm(ctx, theObj) middleware.AssignForm(theObj, ctx.Data) diff --git a/options/license/Symlinks b/options/license/Symlinks new file mode 100644 index 00000000000..35420d2ba9b --- /dev/null +++ b/options/license/Symlinks @@ -0,0 +1,10 @@ + My "symlinks" utility pre-dates the "open source licensing" +fad by a number of years. Just to clarify, this is 100% +freeware, written entirely by myself. The intent is to use +it to detect missing/obsolete symlink targets on an installed +distro, before creating the "gold" (or "final") release discs. + +Use and distribute and modify as you (or anyone +else) sees fit. There have no formal restrictions or +requirements whatsoever regarding distribution of either +binaries or source code, whether modified or original. diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 3f5cf431f82..82ff7ae0bef 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -67,7 +67,6 @@ import ( gocontext "context" "fmt" "net/http" - "reflect" "strings" "code.gitea.io/gitea/models/organization" @@ -567,14 +566,17 @@ func mustNotBeArchived(ctx *context.APIContext) { } } -// bind binding an obj to a func(ctx *context.APIContext) -func bind(obj interface{}) http.HandlerFunc { - tp := reflect.TypeOf(obj) - for tp.Kind() == reflect.Ptr { - tp = tp.Elem() +func mustEnableAttachments(ctx *context.APIContext) { + if !setting.Attachment.Enabled { + ctx.NotFound() + return } +} + +// bind binding an obj to a func(ctx *context.APIContext) +func bind[T any](obj T) http.HandlerFunc { return web.Wrap(func(ctx *context.APIContext) { - theObj := reflect.New(tp).Interface() // create a new form obj for every request but not use obj directly + theObj := new(T) // create a new form obj for every request but not use obj directly errs := binding.Bind(ctx.Req, theObj) if len(errs) > 0 { ctx.Error(http.StatusUnprocessableEntity, "validationError", fmt.Sprintf("%s: %s", errs[0].FieldNames, errs[0].Error())) @@ -892,6 +894,15 @@ func Routes(ctx gocontext.Context) *web.Route { Get(repo.GetIssueCommentReactions). Post(reqToken(), bind(api.EditReactionOption{}), repo.PostIssueCommentReaction). Delete(reqToken(), bind(api.EditReactionOption{}), repo.DeleteIssueCommentReaction) + m.Group("/assets", func() { + m.Combo(""). + Get(repo.ListIssueCommentAttachments). + Post(reqToken(), mustNotBeArchived, repo.CreateIssueCommentAttachment) + m.Combo("/{asset}"). + Get(repo.GetIssueCommentAttachment). + Patch(reqToken(), mustNotBeArchived, bind(api.EditAttachmentOptions{}), repo.EditIssueCommentAttachment). + Delete(reqToken(), mustNotBeArchived, repo.DeleteIssueCommentAttachment) + }, mustEnableAttachments) }) }) m.Group("/{index}", func() { @@ -935,6 +946,15 @@ func Routes(ctx gocontext.Context) *web.Route { Get(repo.GetIssueReactions). Post(reqToken(), bind(api.EditReactionOption{}), repo.PostIssueReaction). Delete(reqToken(), bind(api.EditReactionOption{}), repo.DeleteIssueReaction) + m.Group("/assets", func() { + m.Combo(""). + Get(repo.ListIssueAttachments). + Post(reqToken(), mustNotBeArchived, repo.CreateIssueAttachment) + m.Combo("/{asset}"). + Get(repo.GetIssueAttachment). + Patch(reqToken(), mustNotBeArchived, bind(api.EditAttachmentOptions{}), repo.EditIssueAttachment). + Delete(reqToken(), mustNotBeArchived, repo.DeleteIssueAttachment) + }, mustEnableAttachments) }) }, mustEnableIssuesOrPulls) m.Group("/labels", func() { diff --git a/routers/api/v1/repo/collaborators.go b/routers/api/v1/repo/collaborators.go index 383193daa71..202418bd2be 100644 --- a/routers/api/v1/repo/collaborators.go +++ b/routers/api/v1/repo/collaborators.go @@ -174,13 +174,13 @@ func AddCollaborator(ctx *context.APIContext) { return } - if err := repo_module.AddCollaborator(ctx.Repo.Repository, collaborator); err != nil { + if err := repo_module.AddCollaborator(ctx, ctx.Repo.Repository, collaborator); err != nil { ctx.Error(http.StatusInternalServerError, "AddCollaborator", err) return } if form.Permission != nil { - if err := repo_model.ChangeCollaborationAccessMode(ctx.Repo.Repository, collaborator.ID, perm.ParseAccessMode(*form.Permission)); err != nil { + if err := repo_model.ChangeCollaborationAccessMode(ctx, ctx.Repo.Repository, collaborator.ID, perm.ParseAccessMode(*form.Permission)); err != nil { ctx.Error(http.StatusInternalServerError, "ChangeCollaborationAccessMode", err) return } diff --git a/routers/api/v1/repo/issue_attachment.go b/routers/api/v1/repo/issue_attachment.go new file mode 100644 index 00000000000..4cf108b4137 --- /dev/null +++ b/routers/api/v1/repo/issue_attachment.go @@ -0,0 +1,372 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package repo + +import ( + "net/http" + + issues_model "code.gitea.io/gitea/models/issues" + repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/convert" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" + api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/attachment" + issue_service "code.gitea.io/gitea/services/issue" +) + +// GetIssueAttachment gets a single attachment of the issue +func GetIssueAttachment(ctx *context.APIContext) { + // swagger:operation GET /repos/{owner}/{repo}/issues/{index}/assets/{attachment_id} issue issueGetIssueAttachment + // --- + // summary: Get an issue attachment + // produces: + // - application/json + // parameters: + // - name: owner + // in: path + // description: owner of the repo + // type: string + // required: true + // - name: repo + // in: path + // description: name of the repo + // type: string + // required: true + // - name: index + // in: path + // description: index of the issue + // type: integer + // format: int64 + // required: true + // - name: attachment_id + // in: path + // description: id of the attachment to get + // type: integer + // format: int64 + // required: true + // responses: + // "200": + // "$ref": "#/responses/Attachment" + // "404": + // "$ref": "#/responses/error" + + issue := getIssueFromContext(ctx) + if issue == nil { + return + } + + attach := getIssueAttachmentSafeRead(ctx, issue) + if attach == nil { + return + } + + ctx.JSON(http.StatusOK, convert.ToAttachment(attach)) +} + +// ListIssueAttachments lists all attachments of the issue +func ListIssueAttachments(ctx *context.APIContext) { + // swagger:operation GET /repos/{owner}/{repo}/issues/{index}/assets issue issueListIssueAttachments + // --- + // summary: List issue's attachments + // produces: + // - application/json + // parameters: + // - name: owner + // in: path + // description: owner of the repo + // type: string + // required: true + // - name: repo + // in: path + // description: name of the repo + // type: string + // required: true + // - name: index + // in: path + // description: index of the issue + // type: integer + // format: int64 + // required: true + // responses: + // "200": + // "$ref": "#/responses/AttachmentList" + // "404": + // "$ref": "#/responses/error" + + issue := getIssueFromContext(ctx) + if issue == nil { + return + } + + if err := issue.LoadAttributes(ctx); err != nil { + ctx.Error(http.StatusInternalServerError, "LoadAttributes", err) + return + } + + ctx.JSON(http.StatusOK, convert.ToAPIIssue(ctx, issue).Attachments) +} + +// CreateIssueAttachment creates an attachment and saves the given file +func CreateIssueAttachment(ctx *context.APIContext) { + // swagger:operation POST /repos/{owner}/{repo}/issues/{index}/assets issue issueCreateIssueAttachment + // --- + // summary: Create an issue attachment + // produces: + // - application/json + // consumes: + // - multipart/form-data + // parameters: + // - name: owner + // in: path + // description: owner of the repo + // type: string + // required: true + // - name: repo + // in: path + // description: name of the repo + // type: string + // required: true + // - name: index + // in: path + // description: index of the issue + // type: integer + // format: int64 + // required: true + // - name: name + // in: query + // description: name of the attachment + // type: string + // required: false + // - name: attachment + // in: formData + // description: attachment to upload + // type: file + // required: true + // responses: + // "201": + // "$ref": "#/responses/Attachment" + // "400": + // "$ref": "#/responses/error" + // "404": + // "$ref": "#/responses/error" + + issue := getIssueFromContext(ctx) + if issue == nil { + return + } + + if !canUserWriteIssueAttachment(ctx, issue) { + return + } + + // Get uploaded file from request + file, header, err := ctx.Req.FormFile("attachment") + if err != nil { + ctx.Error(http.StatusInternalServerError, "FormFile", err) + return + } + defer file.Close() + + filename := header.Filename + if query := ctx.FormString("name"); query != "" { + filename = query + } + + attachment, err := attachment.UploadAttachment(file, setting.Attachment.AllowedTypes, &repo_model.Attachment{ + Name: filename, + UploaderID: ctx.Doer.ID, + RepoID: ctx.Repo.Repository.ID, + IssueID: issue.ID, + }) + if err != nil { + ctx.Error(http.StatusInternalServerError, "UploadAttachment", err) + return + } + + issue.Attachments = append(issue.Attachments, attachment) + + if err := issue_service.ChangeContent(issue, ctx.Doer, issue.Content); err != nil { + ctx.Error(http.StatusInternalServerError, "ChangeContent", err) + return + } + + ctx.JSON(http.StatusCreated, convert.ToAttachment(attachment)) +} + +// EditIssueAttachment updates the given attachment +func EditIssueAttachment(ctx *context.APIContext) { + // swagger:operation PATCH /repos/{owner}/{repo}/issues/{index}/assets/{attachment_id} issue issueEditIssueAttachment + // --- + // summary: Edit an issue attachment + // produces: + // - application/json + // consumes: + // - application/json + // parameters: + // - name: owner + // in: path + // description: owner of the repo + // type: string + // required: true + // - name: repo + // in: path + // description: name of the repo + // type: string + // required: true + // - name: index + // in: path + // description: index of the issue + // type: integer + // format: int64 + // required: true + // - name: attachment_id + // in: path + // description: id of the attachment to edit + // type: integer + // format: int64 + // required: true + // - name: body + // in: body + // schema: + // "$ref": "#/definitions/EditAttachmentOptions" + // responses: + // "201": + // "$ref": "#/responses/Attachment" + // "404": + // "$ref": "#/responses/error" + + attachment := getIssueAttachmentSafeWrite(ctx) + if attachment == nil { + return + } + + // do changes to attachment. only meaningful change is name. + form := web.GetForm(ctx).(*api.EditAttachmentOptions) + if form.Name != "" { + attachment.Name = form.Name + } + + if err := repo_model.UpdateAttachment(ctx, attachment); err != nil { + ctx.Error(http.StatusInternalServerError, "UpdateAttachment", err) + } + + ctx.JSON(http.StatusCreated, convert.ToAttachment(attachment)) +} + +// DeleteIssueAttachment delete a given attachment +func DeleteIssueAttachment(ctx *context.APIContext) { + // swagger:operation DELETE /repos/{owner}/{repo}/issues/{index}/assets/{attachment_id} issue issueDeleteIssueAttachment + // --- + // summary: Delete an issue attachment + // produces: + // - application/json + // parameters: + // - name: owner + // in: path + // description: owner of the repo + // type: string + // required: true + // - name: repo + // in: path + // description: name of the repo + // type: string + // required: true + // - name: index + // in: path + // description: index of the issue + // type: integer + // format: int64 + // required: true + // - name: attachment_id + // in: path + // description: id of the attachment to delete + // type: integer + // format: int64 + // required: true + // responses: + // "204": + // "$ref": "#/responses/empty" + // "404": + // "$ref": "#/responses/error" + + attachment := getIssueAttachmentSafeWrite(ctx) + if attachment == nil { + return + } + + if err := repo_model.DeleteAttachment(attachment, true); err != nil { + ctx.Error(http.StatusInternalServerError, "DeleteAttachment", err) + return + } + + ctx.Status(http.StatusNoContent) +} + +func getIssueFromContext(ctx *context.APIContext) *issues_model.Issue { + issue, err := issues_model.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64("index")) + if err != nil { + ctx.NotFoundOrServerError("GetIssueByIndex", issues_model.IsErrIssueNotExist, err) + return nil + } + + issue.Repo = ctx.Repo.Repository + + return issue +} + +func getIssueAttachmentSafeWrite(ctx *context.APIContext) *repo_model.Attachment { + issue := getIssueFromContext(ctx) + if issue == nil { + return nil + } + + if !canUserWriteIssueAttachment(ctx, issue) { + return nil + } + + return getIssueAttachmentSafeRead(ctx, issue) +} + +func getIssueAttachmentSafeRead(ctx *context.APIContext, issue *issues_model.Issue) *repo_model.Attachment { + attachment, err := repo_model.GetAttachmentByID(ctx, ctx.ParamsInt64("asset")) + if err != nil { + ctx.NotFoundOrServerError("GetAttachmentByID", repo_model.IsErrAttachmentNotExist, err) + return nil + } + if !attachmentBelongsToRepoOrIssue(ctx, attachment, issue) { + return nil + } + return attachment +} + +func canUserWriteIssueAttachment(ctx *context.APIContext, issue *issues_model.Issue) bool { + canEditIssue := ctx.IsSigned && (ctx.Doer.ID == issue.PosterID || ctx.IsUserRepoAdmin() || ctx.IsUserSiteAdmin()) && ctx.Repo.CanWriteIssuesOrPulls(issue.IsPull) + if !canEditIssue { + ctx.Error(http.StatusForbidden, "", "user should have permission to write issue") + return false + } + + return true +} + +func attachmentBelongsToRepoOrIssue(ctx *context.APIContext, attachment *repo_model.Attachment, issue *issues_model.Issue) bool { + if attachment.RepoID != ctx.Repo.Repository.ID { + log.Debug("Requested attachment[%d] does not belong to repo[%-v].", attachment.ID, ctx.Repo.Repository) + ctx.NotFound("no such attachment in repo") + return false + } + if attachment.IssueID == 0 { + log.Debug("Requested attachment[%d] is not in an issue.", attachment.ID) + ctx.NotFound("no such attachment in issue") + return false + } else if issue != nil && attachment.IssueID != issue.ID { + log.Debug("Requested attachment[%d] does not belong to issue[%d, #%d].", attachment.ID, issue.ID, issue.Index) + ctx.NotFound("no such attachment in issue") + return false + } + return true +} diff --git a/routers/api/v1/repo/issue_comment.go b/routers/api/v1/repo/issue_comment.go index 120c1d88a0d..9097004186f 100644 --- a/routers/api/v1/repo/issue_comment.go +++ b/routers/api/v1/repo/issue_comment.go @@ -18,7 +18,7 @@ import ( api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" - comment_service "code.gitea.io/gitea/services/comments" + issue_service "code.gitea.io/gitea/services/issue" ) // ListIssueComments list all the comments of an issue @@ -95,6 +95,11 @@ func ListIssueComments(ctx *context.APIContext) { return } + if err := issues_model.CommentList(comments).LoadAttachments(ctx); err != nil { + ctx.Error(http.StatusInternalServerError, "LoadAttachments", err) + return + } + apiComments := make([]*api.Comment, len(comments)) for i, comment := range comments { comment.Issue = issue @@ -294,6 +299,10 @@ func ListRepoIssueComments(ctx *context.APIContext) { ctx.Error(http.StatusInternalServerError, "LoadPosters", err) return } + if err := issues_model.CommentList(comments).LoadAttachments(ctx); err != nil { + ctx.Error(http.StatusInternalServerError, "LoadAttachments", err) + return + } if _, err := issues_model.CommentList(comments).Issues().LoadRepositories(ctx); err != nil { ctx.Error(http.StatusInternalServerError, "LoadRepositories", err) return @@ -353,7 +362,7 @@ func CreateIssueComment(ctx *context.APIContext) { return } - comment, err := comment_service.CreateIssueComment(ctx, ctx.Doer, ctx.Repo.Repository, issue, form.Body, nil) + comment, err := issue_service.CreateIssueComment(ctx, ctx.Doer, ctx.Repo.Repository, issue, form.Body, nil) if err != nil { ctx.Error(http.StatusInternalServerError, "CreateIssueComment", err) return @@ -547,7 +556,7 @@ func editIssueComment(ctx *context.APIContext, form api.EditIssueCommentOption) oldContent := comment.Content comment.Content = form.Body - if err := comment_service.UpdateComment(ctx, comment, ctx.Doer, oldContent); err != nil { + if err := issue_service.UpdateComment(ctx, comment, ctx.Doer, oldContent); err != nil { ctx.Error(http.StatusInternalServerError, "UpdateComment", err) return } @@ -646,7 +655,7 @@ func deleteIssueComment(ctx *context.APIContext) { return } - if err = comment_service.DeleteComment(ctx, ctx.Doer, comment); err != nil { + if err = issue_service.DeleteComment(ctx, ctx.Doer, comment); err != nil { ctx.Error(http.StatusInternalServerError, "DeleteCommentByID", err) return } diff --git a/routers/api/v1/repo/issue_comment_attachment.go b/routers/api/v1/repo/issue_comment_attachment.go new file mode 100644 index 00000000000..2198a6a33d5 --- /dev/null +++ b/routers/api/v1/repo/issue_comment_attachment.go @@ -0,0 +1,383 @@ +// Copyright 2021 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package repo + +import ( + "net/http" + + issues_model "code.gitea.io/gitea/models/issues" + repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/convert" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/setting" + api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/web" + "code.gitea.io/gitea/services/attachment" + issue_service "code.gitea.io/gitea/services/issue" +) + +// GetIssueCommentAttachment gets a single attachment of the comment +func GetIssueCommentAttachment(ctx *context.APIContext) { + // swagger:operation GET /repos/{owner}/{repo}/issues/comments/{id}/assets/{attachment_id} issue issueGetIssueCommentAttachment + // --- + // summary: Get a comment attachment + // produces: + // - application/json + // parameters: + // - name: owner + // in: path + // description: owner of the repo + // type: string + // required: true + // - name: repo + // in: path + // description: name of the repo + // type: string + // required: true + // - name: id + // in: path + // description: id of the comment + // type: integer + // format: int64 + // required: true + // - name: attachment_id + // in: path + // description: id of the attachment to get + // type: integer + // format: int64 + // required: true + // responses: + // "200": + // "$ref": "#/responses/Attachment" + // "404": + // "$ref": "#/responses/error" + + comment := getIssueCommentSafe(ctx) + if comment == nil { + return + } + attachment := getIssueCommentAttachmentSafeRead(ctx, comment) + if attachment == nil { + return + } + if attachment.CommentID != comment.ID { + log.Debug("User requested attachment[%d] is not in comment[%d].", attachment.ID, comment.ID) + ctx.NotFound("attachment not in comment") + return + } + + ctx.JSON(http.StatusOK, convert.ToAttachment(attachment)) +} + +// ListIssueCommentAttachments lists all attachments of the comment +func ListIssueCommentAttachments(ctx *context.APIContext) { + // swagger:operation GET /repos/{owner}/{repo}/issues/comments/{id}/assets issue issueListIssueCommentAttachments + // --- + // summary: List comment's attachments + // produces: + // - application/json + // parameters: + // - name: owner + // in: path + // description: owner of the repo + // type: string + // required: true + // - name: repo + // in: path + // description: name of the repo + // type: string + // required: true + // - name: id + // in: path + // description: id of the comment + // type: integer + // format: int64 + // required: true + // responses: + // "200": + // "$ref": "#/responses/AttachmentList" + // "404": + // "$ref": "#/responses/error" + comment := getIssueCommentSafe(ctx) + if comment == nil { + return + } + + if err := comment.LoadAttachments(ctx); err != nil { + ctx.Error(http.StatusInternalServerError, "LoadAttachments", err) + return + } + + ctx.JSON(http.StatusOK, convert.ToAttachments(comment.Attachments)) +} + +// CreateIssueCommentAttachment creates an attachment and saves the given file +func CreateIssueCommentAttachment(ctx *context.APIContext) { + // swagger:operation POST /repos/{owner}/{repo}/issues/comments/{id}/assets issue issueCreateIssueCommentAttachment + // --- + // summary: Create a comment attachment + // produces: + // - application/json + // consumes: + // - multipart/form-data + // parameters: + // - name: owner + // in: path + // description: owner of the repo + // type: string + // required: true + // - name: repo + // in: path + // description: name of the repo + // type: string + // required: true + // - name: id + // in: path + // description: id of the comment + // type: integer + // format: int64 + // required: true + // - name: name + // in: query + // description: name of the attachment + // type: string + // required: false + // - name: attachment + // in: formData + // description: attachment to upload + // type: file + // required: true + // responses: + // "201": + // "$ref": "#/responses/Attachment" + // "400": + // "$ref": "#/responses/error" + // "404": + // "$ref": "#/responses/error" + + // Check if comment exists and load comment + comment := getIssueCommentSafe(ctx) + if comment == nil { + return + } + + if !canUserWriteIssueCommentAttachment(ctx, comment) { + return + } + + // Get uploaded file from request + file, header, err := ctx.Req.FormFile("attachment") + if err != nil { + ctx.Error(http.StatusInternalServerError, "FormFile", err) + return + } + defer file.Close() + + filename := header.Filename + if query := ctx.FormString("name"); query != "" { + filename = query + } + + attachment, err := attachment.UploadAttachment(file, setting.Attachment.AllowedTypes, &repo_model.Attachment{ + Name: filename, + UploaderID: ctx.Doer.ID, + RepoID: ctx.Repo.Repository.ID, + IssueID: comment.IssueID, + CommentID: comment.ID, + }) + if err != nil { + ctx.Error(http.StatusInternalServerError, "UploadAttachment", err) + return + } + if err := comment.LoadAttachments(ctx); err != nil { + ctx.Error(http.StatusInternalServerError, "LoadAttachments", err) + return + } + + if err = issue_service.UpdateComment(ctx, comment, ctx.Doer, comment.Content); err != nil { + ctx.ServerError("UpdateComment", err) + return + } + + ctx.JSON(http.StatusCreated, convert.ToAttachment(attachment)) +} + +// EditIssueCommentAttachment updates the given attachment +func EditIssueCommentAttachment(ctx *context.APIContext) { + // swagger:operation PATCH /repos/{owner}/{repo}/issues/comments/{id}/assets/{attachment_id} issue issueEditIssueCommentAttachment + // --- + // summary: Edit a comment attachment + // produces: + // - application/json + // consumes: + // - application/json + // parameters: + // - name: owner + // in: path + // description: owner of the repo + // type: string + // required: true + // - name: repo + // in: path + // description: name of the repo + // type: string + // required: true + // - name: id + // in: path + // description: id of the comment + // type: integer + // format: int64 + // required: true + // - name: attachment_id + // in: path + // description: id of the attachment to edit + // type: integer + // format: int64 + // required: true + // - name: body + // in: body + // schema: + // "$ref": "#/definitions/EditAttachmentOptions" + // responses: + // "201": + // "$ref": "#/responses/Attachment" + // "404": + // "$ref": "#/responses/error" + + attach := getIssueCommentAttachmentSafeWrite(ctx) + if attach == nil { + return + } + + form := web.GetForm(ctx).(*api.EditAttachmentOptions) + if form.Name != "" { + attach.Name = form.Name + } + + if err := repo_model.UpdateAttachment(ctx, attach); err != nil { + ctx.Error(http.StatusInternalServerError, "UpdateAttachment", attach) + } + ctx.JSON(http.StatusCreated, convert.ToAttachment(attach)) +} + +// DeleteIssueCommentAttachment delete a given attachment +func DeleteIssueCommentAttachment(ctx *context.APIContext) { + // swagger:operation DELETE /repos/{owner}/{repo}/issues/comments/{id}/assets/{attachment_id} issue issueDeleteIssueCommentAttachment + // --- + // summary: Delete a comment attachment + // produces: + // - application/json + // parameters: + // - name: owner + // in: path + // description: owner of the repo + // type: string + // required: true + // - name: repo + // in: path + // description: name of the repo + // type: string + // required: true + // - name: id + // in: path + // description: id of the comment + // type: integer + // format: int64 + // required: true + // - name: attachment_id + // in: path + // description: id of the attachment to delete + // type: integer + // format: int64 + // required: true + // responses: + // "204": + // "$ref": "#/responses/empty" + // "404": + // "$ref": "#/responses/error" + + attach := getIssueCommentAttachmentSafeWrite(ctx) + if attach == nil { + return + } + + if err := repo_model.DeleteAttachment(attach, true); err != nil { + ctx.Error(http.StatusInternalServerError, "DeleteAttachment", err) + return + } + ctx.Status(http.StatusNoContent) +} + +func getIssueCommentSafe(ctx *context.APIContext) *issues_model.Comment { + comment, err := issues_model.GetCommentByID(ctx, ctx.ParamsInt64("id")) + if err != nil { + ctx.NotFoundOrServerError("GetCommentByID", issues_model.IsErrCommentNotExist, err) + return nil + } + if err := comment.LoadIssue(ctx); err != nil { + ctx.Error(http.StatusInternalServerError, "comment.LoadIssue", err) + return nil + } + if comment.Issue == nil || comment.Issue.RepoID != ctx.Repo.Repository.ID { + ctx.Error(http.StatusNotFound, "", "no matching issue comment found") + return nil + } + + comment.Issue.Repo = ctx.Repo.Repository + + return comment +} + +func getIssueCommentAttachmentSafeWrite(ctx *context.APIContext) *repo_model.Attachment { + comment := getIssueCommentSafe(ctx) + if comment == nil { + return nil + } + if !canUserWriteIssueCommentAttachment(ctx, comment) { + return nil + } + return getIssueCommentAttachmentSafeRead(ctx, comment) +} + +func canUserWriteIssueCommentAttachment(ctx *context.APIContext, comment *issues_model.Comment) bool { + canEditComment := ctx.IsSigned && (ctx.Doer.ID == comment.PosterID || ctx.IsUserRepoAdmin() || ctx.IsUserSiteAdmin()) && ctx.Repo.CanWriteIssuesOrPulls(comment.Issue.IsPull) + if !canEditComment { + ctx.Error(http.StatusForbidden, "", "user should have permission to edit comment") + return false + } + + return true +} + +func getIssueCommentAttachmentSafeRead(ctx *context.APIContext, comment *issues_model.Comment) *repo_model.Attachment { + attachment, err := repo_model.GetAttachmentByID(ctx, ctx.ParamsInt64("asset")) + if err != nil { + ctx.NotFoundOrServerError("GetAttachmentByID", repo_model.IsErrAttachmentNotExist, err) + return nil + } + if !attachmentBelongsToRepoOrComment(ctx, attachment, comment) { + return nil + } + return attachment +} + +func attachmentBelongsToRepoOrComment(ctx *context.APIContext, attachment *repo_model.Attachment, comment *issues_model.Comment) bool { + if attachment.RepoID != ctx.Repo.Repository.ID { + log.Debug("Requested attachment[%d] does not belong to repo[%-v].", attachment.ID, ctx.Repo.Repository) + ctx.NotFound("no such attachment in repo") + return false + } + if attachment.IssueID == 0 || attachment.CommentID == 0 { + log.Debug("Requested attachment[%d] is not in a comment.", attachment.ID) + ctx.NotFound("no such attachment in comment") + return false + } + if comment != nil && attachment.CommentID != comment.ID { + log.Debug("Requested attachment[%d] does not belong to comment[%d].", attachment.ID, comment.ID) + ctx.NotFound("no such attachment in comment") + return false + } + return true +} diff --git a/routers/api/v1/repo/issue_tracked_time.go b/routers/api/v1/repo/issue_tracked_time.go index 6786d23a932..ede60a2ed81 100644 --- a/routers/api/v1/repo/issue_tracked_time.go +++ b/routers/api/v1/repo/issue_tracked_time.go @@ -71,7 +71,7 @@ func ListTrackedTimes(ctx *context.APIContext) { // "404": // "$ref": "#/responses/notFound" - if !ctx.Repo.Repository.IsTimetrackerEnabled() { + if !ctx.Repo.Repository.IsTimetrackerEnabled(ctx) { ctx.NotFound("Timetracker is disabled") return } @@ -190,7 +190,7 @@ func AddTime(ctx *context.APIContext) { } if !ctx.Repo.CanUseTimetracker(issue, ctx.Doer) { - if !ctx.Repo.Repository.IsTimetrackerEnabled() { + if !ctx.Repo.Repository.IsTimetrackerEnabled(ctx) { ctx.Error(http.StatusBadRequest, "", "time tracking disabled") return } @@ -271,7 +271,7 @@ func ResetIssueTime(ctx *context.APIContext) { } if !ctx.Repo.CanUseTimetracker(issue, ctx.Doer) { - if !ctx.Repo.Repository.IsTimetrackerEnabled() { + if !ctx.Repo.Repository.IsTimetrackerEnabled(ctx) { ctx.JSON(http.StatusBadRequest, struct{ Message string }{Message: "time tracking disabled"}) return } @@ -342,7 +342,7 @@ func DeleteTime(ctx *context.APIContext) { } if !ctx.Repo.CanUseTimetracker(issue, ctx.Doer) { - if !ctx.Repo.Repository.IsTimetrackerEnabled() { + if !ctx.Repo.Repository.IsTimetrackerEnabled(ctx) { ctx.JSON(http.StatusBadRequest, struct{ Message string }{Message: "time tracking disabled"}) return } @@ -410,7 +410,7 @@ func ListTrackedTimesByUser(ctx *context.APIContext) { // "403": // "$ref": "#/responses/forbidden" - if !ctx.Repo.Repository.IsTimetrackerEnabled() { + if !ctx.Repo.Repository.IsTimetrackerEnabled(ctx) { ctx.Error(http.StatusBadRequest, "", "time tracking disabled") return } @@ -498,7 +498,7 @@ func ListTrackedTimesByRepository(ctx *context.APIContext) { // "403": // "$ref": "#/responses/forbidden" - if !ctx.Repo.Repository.IsTimetrackerEnabled() { + if !ctx.Repo.Repository.IsTimetrackerEnabled(ctx) { ctx.Error(http.StatusBadRequest, "", "time tracking disabled") return } diff --git a/routers/api/v1/repo/release_attachment.go b/routers/api/v1/repo/release_attachment.go index 88632b46377..e7dbb42c741 100644 --- a/routers/api/v1/repo/release_attachment.go +++ b/routers/api/v1/repo/release_attachment.go @@ -68,7 +68,7 @@ func GetReleaseAttachment(ctx *context.APIContext) { return } // FIXME Should prove the existence of the given repo, but results in unnecessary database requests - ctx.JSON(http.StatusOK, convert.ToReleaseAttachment(attach)) + ctx.JSON(http.StatusOK, convert.ToAttachment(attach)) } // ListReleaseAttachments lists all attachments of the release @@ -194,7 +194,12 @@ func CreateReleaseAttachment(ctx *context.APIContext) { } // Create a new attachment and save the file - attach, err := attachment.UploadAttachment(file, ctx.Doer.ID, release.RepoID, releaseID, filename, setting.Repository.Release.AllowedTypes) + attach, err := attachment.UploadAttachment(file, setting.Repository.Release.AllowedTypes, &repo_model.Attachment{ + Name: filename, + UploaderID: ctx.Doer.ID, + RepoID: release.RepoID, + ReleaseID: releaseID, + }) if err != nil { if upload.IsErrFileTypeForbidden(err) { ctx.Error(http.StatusBadRequest, "DetectContentType", err) @@ -204,7 +209,7 @@ func CreateReleaseAttachment(ctx *context.APIContext) { return } - ctx.JSON(http.StatusCreated, convert.ToReleaseAttachment(attach)) + ctx.JSON(http.StatusCreated, convert.ToAttachment(attach)) } // EditReleaseAttachment updates the given attachment @@ -274,7 +279,7 @@ func EditReleaseAttachment(ctx *context.APIContext) { if err := repo_model.UpdateAttachment(ctx, attach); err != nil { ctx.Error(http.StatusInternalServerError, "UpdateAttachment", attach) } - ctx.JSON(http.StatusCreated, convert.ToReleaseAttachment(attach)) + ctx.JSON(http.StatusCreated, convert.ToAttachment(attach)) } // DeleteReleaseAttachment delete a given attachment diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index 2e0a77d77fd..b2177829400 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -736,7 +736,7 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { var units []repo_model.RepoUnit var deleteUnitTypes []unit_model.Type - currHasIssues := repo.UnitEnabledCtx(ctx, unit_model.TypeIssues) + currHasIssues := repo.UnitEnabled(ctx, unit_model.TypeIssues) newHasIssues := currHasIssues if opts.HasIssues != nil { newHasIssues = *opts.HasIssues @@ -776,7 +776,7 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { AllowOnlyContributorsToTrackTime: opts.InternalTracker.AllowOnlyContributorsToTrackTime, EnableDependencies: opts.InternalTracker.EnableIssueDependencies, } - } else if unit, err := repo.GetUnit(unit_model.TypeIssues); err != nil { + } else if unit, err := repo.GetUnit(ctx, unit_model.TypeIssues); err != nil { // Unit type doesn't exist so we make a new config file with default values config = &repo_model.IssuesConfig{ EnableTimetracker: true, @@ -803,7 +803,7 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { } } - currHasWiki := repo.UnitEnabledCtx(ctx, unit_model.TypeWiki) + currHasWiki := repo.UnitEnabled(ctx, unit_model.TypeWiki) newHasWiki := currHasWiki if opts.HasWiki != nil { newHasWiki = *opts.HasWiki @@ -843,7 +843,7 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { } } - currHasPullRequests := repo.UnitEnabledCtx(ctx, unit_model.TypePullRequests) + currHasPullRequests := repo.UnitEnabled(ctx, unit_model.TypePullRequests) newHasPullRequests := currHasPullRequests if opts.HasPullRequests != nil { newHasPullRequests = *opts.HasPullRequests @@ -853,7 +853,7 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { // We do allow setting individual PR settings through the API, so // we get the config settings and then set them // if those settings were provided in the opts. - unit, err := repo.GetUnit(unit_model.TypePullRequests) + unit, err := repo.GetUnit(ctx, unit_model.TypePullRequests) var config *repo_model.PullRequestsConfig if err != nil { // Unit type doesn't exist so we make a new config file with default values diff --git a/routers/api/v1/repo/transfer.go b/routers/api/v1/repo/transfer.go index fb15566a4b5..5154c5afa3c 100644 --- a/routers/api/v1/repo/transfer.go +++ b/routers/api/v1/repo/transfer.go @@ -105,7 +105,7 @@ func Transfer(ctx *context.APIContext) { oldFullname := ctx.Repo.Repository.FullName() - if err := repo_service.StartRepositoryTransfer(ctx.Doer, newOwner, ctx.Repo.Repository, teams); err != nil { + if err := repo_service.StartRepositoryTransfer(ctx, ctx.Doer, newOwner, ctx.Repo.Repository, teams); err != nil { if models.IsErrRepoTransferInProgress(err) { ctx.Error(http.StatusConflict, "StartRepositoryTransfer", err) return @@ -207,7 +207,7 @@ func RejectTransfer(ctx *context.APIContext) { } func acceptOrRejectRepoTransfer(ctx *context.APIContext, accept bool) error { - repoTransfer, err := models.GetPendingRepositoryTransfer(ctx.Repo.Repository) + repoTransfer, err := models.GetPendingRepositoryTransfer(ctx, ctx.Repo.Repository) if err != nil { if models.IsErrNoPendingTransfer(err) { ctx.NotFound() @@ -216,7 +216,7 @@ func acceptOrRejectRepoTransfer(ctx *context.APIContext, accept bool) error { return err } - if err := repoTransfer.LoadAttributes(); err != nil { + if err := repoTransfer.LoadAttributes(ctx); err != nil { return err } @@ -226,7 +226,7 @@ func acceptOrRejectRepoTransfer(ctx *context.APIContext, accept bool) error { } if accept { - return repo_service.TransferOwnership(repoTransfer.Doer, repoTransfer.Recipient, ctx.Repo.Repository, repoTransfer.Teams) + return repo_service.TransferOwnership(ctx, repoTransfer.Doer, repoTransfer.Recipient, ctx.Repo.Repository, repoTransfer.Teams) } return models.CancelRepositoryTransfer(ctx.Repo.Repository) diff --git a/routers/private/internal.go b/routers/private/internal.go index d5c85480a77..306e4ffb004 100644 --- a/routers/private/internal.go +++ b/routers/private/internal.go @@ -6,7 +6,6 @@ package private import ( "net/http" - "reflect" "strings" "code.gitea.io/gitea/modules/context" @@ -39,13 +38,9 @@ func CheckInternalToken(next http.Handler) http.Handler { } // bind binding an obj to a handler -func bind(obj interface{}) http.HandlerFunc { - tp := reflect.TypeOf(obj) - for tp.Kind() == reflect.Ptr { - tp = tp.Elem() - } +func bind[T any](obj T) http.HandlerFunc { return web.Wrap(func(ctx *context.PrivateContext) { - theObj := reflect.New(tp).Interface() // create a new form obj for every request but not use obj directly + theObj := new(T) // create a new form obj for every request but not use obj directly binding.Bind(ctx.Req, theObj) web.SetForm(ctx, theObj) }) diff --git a/routers/private/serv.go b/routers/private/serv.go index a86f6c05792..17f966e3e40 100644 --- a/routers/private/serv.go +++ b/routers/private/serv.go @@ -382,7 +382,7 @@ func ServCommand(ctx *context.PrivateContext) { if results.IsWiki { // Ensure the wiki is enabled before we allow access to it - if _, err := repo.GetUnit(unit.TypeWiki); err != nil { + if _, err := repo.GetUnit(ctx, unit.TypeWiki); err != nil { if repo_model.IsErrUnitTypeNotExist(err) { ctx.JSON(http.StatusForbidden, private.ErrServCommand{ Results: results, diff --git a/routers/web/explore/user.go b/routers/web/explore/user.go index bb6aa3dc77c..e00493c87b7 100644 --- a/routers/web/explore/user.go +++ b/routers/web/explore/user.go @@ -68,6 +68,10 @@ func RenderUserSearch(ctx *context.Context, opts *user_model.SearchUserOptions, orderBy = "`user`.updated_unix ASC" case "reversealphabetically": orderBy = "`user`.name DESC" + case "lastlogin": + orderBy = "`user`.last_login_unix ASC" + case "reverselastlogin": + orderBy = "`user`.last_login_unix DESC" case UserSearchDefaultSortType: // "alphabetically" default: orderBy = "`user`.name ASC" diff --git a/routers/web/repo/attachment.go b/routers/web/repo/attachment.go index 656e40f8781..589632ad6e1 100644 --- a/routers/web/repo/attachment.go +++ b/routers/web/repo/attachment.go @@ -44,7 +44,11 @@ func uploadAttachment(ctx *context.Context, repoID int64, allowedTypes string) { } defer file.Close() - attach, err := attachment.UploadAttachment(file, ctx.Doer.ID, repoID, 0, header.Filename, allowedTypes) + attach, err := attachment.UploadAttachment(file, allowedTypes, &repo_model.Attachment{ + Name: header.Filename, + UploaderID: ctx.Doer.ID, + RepoID: repoID, + }) if err != nil { if upload.IsErrFileTypeForbidden(err) { ctx.Error(http.StatusBadRequest, err.Error()) @@ -82,7 +86,7 @@ func DeleteAttachment(ctx *context.Context) { }) } -// GetAttachment serve attachements +// GetAttachment serve attachments func GetAttachment(ctx *context.Context) { attach, err := repo_model.GetAttachmentByUUID(ctx, ctx.Params(":uuid")) if err != nil { diff --git a/routers/web/repo/cherry_pick.go b/routers/web/repo/cherry_pick.go index 2056ad1b379..48bc6959e07 100644 --- a/routers/web/repo/cherry_pick.go +++ b/routers/web/repo/cherry_pick.go @@ -177,7 +177,7 @@ func CherryPickPost(ctx *context.Context) { } } - if form.CommitChoice == frmCommitChoiceNewBranch && ctx.Repo.Repository.UnitEnabled(unit.TypePullRequests) { + if form.CommitChoice == frmCommitChoiceNewBranch && ctx.Repo.Repository.UnitEnabled(ctx, unit.TypePullRequests) { ctx.Redirect(ctx.Repo.RepoLink + "/compare/" + util.PathEscapeSegments(ctx.Repo.BranchName) + "..." + util.PathEscapeSegments(form.NewBranchName)) } else { ctx.Redirect(ctx.Repo.RepoLink + "/src/branch/" + util.PathEscapeSegments(branchName)) diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index b2d8f8645df..d95eeaecc97 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -577,7 +577,7 @@ func PrepareCompareDiff( if (headCommitID == ci.CompareInfo.MergeBase && !ci.DirectComparison) || headCommitID == ci.CompareInfo.BaseCommitID { ctx.Data["IsNothingToCompare"] = true - if unit, err := repo.GetUnit(unit.TypePullRequests); err == nil { + if unit, err := repo.GetUnit(ctx, unit.TypePullRequests); err == nil { config := unit.PullRequestsConfig() if !config.AutodetectManualMerge { diff --git a/routers/web/repo/editor.go b/routers/web/repo/editor.go index 7b064adfc5e..e5ba4ad2c13 100644 --- a/routers/web/repo/editor.go +++ b/routers/web/repo/editor.go @@ -328,7 +328,7 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b } } - if form.CommitChoice == frmCommitChoiceNewBranch && ctx.Repo.Repository.UnitEnabled(unit.TypePullRequests) { + if form.CommitChoice == frmCommitChoiceNewBranch && ctx.Repo.Repository.UnitEnabled(ctx, unit.TypePullRequests) { ctx.Redirect(ctx.Repo.RepoLink + "/compare/" + util.PathEscapeSegments(ctx.Repo.BranchName) + "..." + util.PathEscapeSegments(form.NewBranchName)) } else { ctx.Redirect(ctx.Repo.RepoLink + "/src/branch/" + util.PathEscapeSegments(branchName) + "/" + util.PathEscapeSegments(form.TreePath)) @@ -514,7 +514,7 @@ func DeleteFilePost(ctx *context.Context) { } ctx.Flash.Success(ctx.Tr("repo.editor.file_delete_success", ctx.Repo.TreePath)) - if form.CommitChoice == frmCommitChoiceNewBranch && ctx.Repo.Repository.UnitEnabled(unit.TypePullRequests) { + if form.CommitChoice == frmCommitChoiceNewBranch && ctx.Repo.Repository.UnitEnabled(ctx, unit.TypePullRequests) { ctx.Redirect(ctx.Repo.RepoLink + "/compare/" + util.PathEscapeSegments(ctx.Repo.BranchName) + "..." + util.PathEscapeSegments(form.NewBranchName)) } else { treePath := path.Dir(ctx.Repo.TreePath) @@ -717,7 +717,7 @@ func UploadFilePost(ctx *context.Context) { return } - if form.CommitChoice == frmCommitChoiceNewBranch && ctx.Repo.Repository.UnitEnabled(unit.TypePullRequests) { + if form.CommitChoice == frmCommitChoiceNewBranch && ctx.Repo.Repository.UnitEnabled(ctx, unit.TypePullRequests) { ctx.Redirect(ctx.Repo.RepoLink + "/compare/" + util.PathEscapeSegments(ctx.Repo.BranchName) + "..." + util.PathEscapeSegments(form.NewBranchName)) } else { ctx.Redirect(ctx.Repo.RepoLink + "/src/branch/" + util.PathEscapeSegments(branchName) + "/" + util.PathEscapeSegments(form.TreePath)) diff --git a/routers/web/repo/http.go b/routers/web/repo/http.go index d2450066e34..b2a49e3e3a1 100644 --- a/routers/web/repo/http.go +++ b/routers/web/repo/http.go @@ -258,7 +258,7 @@ func httpBase(ctx *context.Context) (h *serviceHandler) { if isWiki { // Ensure the wiki is enabled before we allow access to it - if _, err := repo.GetUnit(unit.TypeWiki); err != nil { + if _, err := repo.GetUnit(ctx, unit.TypeWiki); err != nil { if repo_model.IsErrUnitTypeNotExist(err) { ctx.PlainText(http.StatusForbidden, "repository wiki is disabled") return diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index a531e832068..d315525dac8 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -47,7 +47,6 @@ import ( "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/utils" asymkey_service "code.gitea.io/gitea/services/asymkey" - comment_service "code.gitea.io/gitea/services/comments" "code.gitea.io/gitea/services/forms" issue_service "code.gitea.io/gitea/services/issue" pull_service "code.gitea.io/gitea/services/pull" @@ -114,7 +113,7 @@ func MustEnableIssues(ctx *context.Context) { return } - unit, err := ctx.Repo.Repository.GetUnit(unit.TypeExternalTracker) + unit, err := ctx.Repo.Repository.GetUnit(ctx, unit.TypeExternalTracker) if err == nil { ctx.Redirect(unit.ExternalTrackerConfig().ExternalTrackerURL) return @@ -1174,7 +1173,7 @@ func getBranchData(ctx *context.Context, issue *issues_model.Issue) { func ViewIssue(ctx *context.Context) { if ctx.Params(":type") == "issues" { // If issue was requested we check if repo has external tracker and redirect - extIssueUnit, err := ctx.Repo.Repository.GetUnit(unit.TypeExternalTracker) + extIssueUnit, err := ctx.Repo.Repository.GetUnit(ctx, unit.TypeExternalTracker) if err == nil && extIssueUnit != nil { if extIssueUnit.ExternalTrackerConfig().ExternalTrackerStyle == markup.IssueNameStyleNumeric || extIssueUnit.ExternalTrackerConfig().ExternalTrackerStyle == "" { metas := ctx.Repo.Repository.ComposeMetas() @@ -1375,7 +1374,7 @@ func ViewIssue(ctx *context.Context) { comment *issues_model.Comment participants = make([]*user_model.User, 1, 10) ) - if ctx.Repo.Repository.IsTimetrackerEnabled() { + if ctx.Repo.Repository.IsTimetrackerEnabled(ctx) { if ctx.IsSigned { // Deal with the stopwatch ctx.Data["IsStopwatchRunning"] = issues_model.StopwatchExists(ctx.Doer.ID, issue.ID) @@ -1636,7 +1635,7 @@ func ViewIssue(ctx *context.Context) { } } - prUnit, err := repo.GetUnit(unit.TypePullRequests) + prUnit, err := repo.GetUnit(ctx, unit.TypePullRequests) if err != nil { ctx.ServerError("GetUnit", err) return @@ -2709,7 +2708,7 @@ func NewComment(ctx *context.Context) { return } - comment, err := comment_service.CreateIssueComment(ctx, ctx.Doer, ctx.Repo.Repository, issue, form.Content, attachments) + comment, err := issue_service.CreateIssueComment(ctx, ctx.Doer, ctx.Repo.Repository, issue, form.Content, attachments) if err != nil { ctx.ServerError("CreateIssueComment", err) return @@ -2749,7 +2748,7 @@ func UpdateCommentContent(ctx *context.Context) { }) return } - if err = comment_service.UpdateComment(ctx, comment, ctx.Doer, oldContent); err != nil { + if err = issue_service.UpdateComment(ctx, comment, ctx.Doer, oldContent); err != nil { ctx.ServerError("UpdateComment", err) return } @@ -2805,7 +2804,7 @@ func DeleteComment(ctx *context.Context) { return } - if err = comment_service.DeleteComment(ctx, ctx.Doer, comment); err != nil { + if err = issue_service.DeleteComment(ctx, ctx.Doer, comment); err != nil { ctx.ServerError("DeleteComment", err) return } @@ -3050,7 +3049,7 @@ func GetIssueAttachments(ctx *context.Context) { issue := GetActionIssue(ctx) attachments := make([]*api.Attachment, len(issue.Attachments)) for i := 0; i < len(issue.Attachments); i++ { - attachments[i] = convert.ToReleaseAttachment(issue.Attachments[i]) + attachments[i] = convert.ToAttachment(issue.Attachments[i]) } ctx.JSON(http.StatusOK, attachments) } @@ -3069,7 +3068,7 @@ func GetCommentAttachments(ctx *context.Context) { return } for i := 0; i < len(comment.Attachments); i++ { - attachments = append(attachments, convert.ToReleaseAttachment(comment.Attachments[i])) + attachments = append(attachments, convert.ToAttachment(comment.Attachments[i])) } } ctx.JSON(http.StatusOK, attachments) diff --git a/routers/web/repo/milestone.go b/routers/web/repo/milestone.go index 04271563973..d712df1001b 100644 --- a/routers/web/repo/milestone.go +++ b/routers/web/repo/milestone.go @@ -73,7 +73,7 @@ func Milestones(ctx *context.Context) { ctx.ServerError("GetMilestones", err) return } - if ctx.Repo.Repository.IsTimetrackerEnabled() { + if ctx.Repo.Repository.IsTimetrackerEnabled(ctx) { if err := miles.LoadTotalTrackedTimes(); err != nil { ctx.ServerError("LoadTotalTrackedTimes", err) return diff --git a/routers/web/repo/patch.go b/routers/web/repo/patch.go index 27199ba4109..12b26f38e90 100644 --- a/routers/web/repo/patch.go +++ b/routers/web/repo/patch.go @@ -108,7 +108,7 @@ func NewDiffPatchPost(ctx *context.Context) { } } - if form.CommitChoice == frmCommitChoiceNewBranch && ctx.Repo.Repository.UnitEnabled(unit.TypePullRequests) { + if form.CommitChoice == frmCommitChoiceNewBranch && ctx.Repo.Repository.UnitEnabled(ctx, unit.TypePullRequests) { ctx.Redirect(ctx.Repo.RepoLink + "/compare/" + util.PathEscapeSegments(ctx.Repo.BranchName) + "..." + util.PathEscapeSegments(form.NewBranchName)) } else { ctx.Redirect(ctx.Repo.RepoLink + "/src/branch/" + util.PathEscapeSegments(branchName) + "/" + util.PathEscapeSegments(form.TreePath)) diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go index 73f0916346b..75cd290b8f0 100644 --- a/routers/web/repo/projects.go +++ b/routers/web/repo/projects.go @@ -196,7 +196,7 @@ func DeleteProject(ctx *context.Context) { return } - if err := project_model.DeleteProjectByID(p.ID); err != nil { + if err := project_model.DeleteProjectByID(ctx, p.ID); err != nil { ctx.Flash.Error("DeleteProjectByID: " + err.Error()) } else { ctx.Flash.Success(ctx.Tr("repo.projects.deletion_success")) diff --git a/routers/web/repo/release.go b/routers/web/repo/release.go index 0aa1fde8de5..2047a1cfb9c 100644 --- a/routers/web/repo/release.go +++ b/routers/web/repo/release.go @@ -135,7 +135,7 @@ func releasesOrTags(ctx *context.Context, isTagList bool) { return } - count, err := repo_model.GetReleaseCountByRepoID(ctx.Repo.Repository.ID, opts) + count, err := repo_model.GetReleaseCountByRepoID(ctx, ctx.Repo.Repository.ID, opts) if err != nil { ctx.ServerError("GetReleaseCountByRepoID", err) return diff --git a/routers/web/repo/repo.go b/routers/web/repo/repo.go index d57cae9591f..aba293607c1 100644 --- a/routers/web/repo/repo.go +++ b/routers/web/repo/repo.go @@ -315,12 +315,12 @@ func Action(ctx *context.Context) { } func acceptOrRejectRepoTransfer(ctx *context.Context, accept bool) error { - repoTransfer, err := models.GetPendingRepositoryTransfer(ctx.Repo.Repository) + repoTransfer, err := models.GetPendingRepositoryTransfer(ctx, ctx.Repo.Repository) if err != nil { return err } - if err := repoTransfer.LoadAttributes(); err != nil { + if err := repoTransfer.LoadAttributes(ctx); err != nil { return err } @@ -334,7 +334,7 @@ func acceptOrRejectRepoTransfer(ctx *context.Context, accept bool) error { ctx.Repo.GitRepo = nil } - if err := repo_service.TransferOwnership(repoTransfer.Doer, repoTransfer.Recipient, ctx.Repo.Repository, repoTransfer.Teams); err != nil { + if err := repo_service.TransferOwnership(ctx, repoTransfer.Doer, repoTransfer.Recipient, ctx.Repo.Repository, repoTransfer.Teams); err != nil { return err } ctx.Flash.Success(ctx.Tr("repo.settings.transfer.success")) diff --git a/routers/web/repo/setting.go b/routers/web/repo/setting.go index 7168d5c7a5f..62c5d795b56 100644 --- a/routers/web/repo/setting.go +++ b/routers/web/repo/setting.go @@ -412,6 +412,15 @@ func SettingsPost(ctx *context.Context) { repoChanged = true } + if form.EnableCode && !unit_model.TypeCode.UnitGlobalDisabled() { + units = append(units, repo_model.RepoUnit{ + RepoID: repo.ID, + Type: unit_model.TypeCode, + }) + } else if !unit_model.TypeCode.UnitGlobalDisabled() { + deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeCode) + } + if form.EnableWiki && form.EnableExternalWiki && !unit_model.TypeExternalWiki.UnitGlobalDisabled() { if !validation.IsValidExternalURL(form.ExternalWikiURL) { ctx.Flash.Error(ctx.Tr("repo.settings.external_wiki_url_error")) @@ -704,7 +713,7 @@ func SettingsPost(ctx *context.Context) { ctx.Repo.GitRepo = nil } - if err := repo_service.StartRepositoryTransfer(ctx.Doer, newOwner, repo, nil); err != nil { + if err := repo_service.StartRepositoryTransfer(ctx, ctx.Doer, newOwner, repo, nil); err != nil { if repo_model.IsErrRepoAlreadyExist(err) { ctx.RenderWithErr(ctx.Tr("repo.settings.new_owner_has_same_repo"), tplSettingsOptions, nil) } else if models.IsErrRepoTransferInProgress(err) { @@ -726,7 +735,7 @@ func SettingsPost(ctx *context.Context) { return } - repoTransfer, err := models.GetPendingRepositoryTransfer(ctx.Repo.Repository) + repoTransfer, err := models.GetPendingRepositoryTransfer(ctx, ctx.Repo.Repository) if err != nil { if models.IsErrNoPendingTransfer(err) { ctx.Flash.Error("repo.settings.transfer_abort_invalid") @@ -738,7 +747,7 @@ func SettingsPost(ctx *context.Context) { return } - if err := repoTransfer.LoadAttributes(); err != nil { + if err := repoTransfer.LoadAttributes(ctx); err != nil { ctx.ServerError("LoadRecipient", err) return } @@ -944,7 +953,7 @@ func CollaborationPost(ctx *context.Context) { } } - if err = repo_module.AddCollaborator(ctx.Repo.Repository, u); err != nil { + if err = repo_module.AddCollaborator(ctx, ctx.Repo.Repository, u); err != nil { ctx.ServerError("AddCollaborator", err) return } @@ -960,6 +969,7 @@ func CollaborationPost(ctx *context.Context) { // ChangeCollaborationAccessMode response for changing access of a collaboration func ChangeCollaborationAccessMode(ctx *context.Context) { if err := repo_model.ChangeCollaborationAccessMode( + ctx, ctx.Repo.Repository, ctx.FormInt64("uid"), perm.AccessMode(ctx.FormInt("mode"))); err != nil { diff --git a/routers/web/repo/wiki.go b/routers/web/repo/wiki.go index 885f84543c8..b50a4be8020 100644 --- a/routers/web/repo/wiki.go +++ b/routers/web/repo/wiki.go @@ -59,7 +59,7 @@ func MustEnableWiki(ctx *context.Context) { return } - unit, err := ctx.Repo.Repository.GetUnit(unit.TypeExternalWiki) + unit, err := ctx.Repo.Repository.GetUnit(ctx, unit.TypeExternalWiki) if err == nil { ctx.Redirect(unit.ExternalWikiConfig().ExternalWikiURL) return diff --git a/routers/web/user/home.go b/routers/web/user/home.go index fc4c919de35..36d9d4f019f 100644 --- a/routers/web/user/home.go +++ b/routers/web/user/home.go @@ -231,7 +231,7 @@ func Milestones(ctx *context.Context) { return } - if milestones[i].Repo.IsTimetrackerEnabled() { + if milestones[i].Repo.IsTimetrackerEnabled(ctx) { err := milestones[i].LoadTotalTrackedTime() if err != nil { ctx.ServerError("LoadTotalTrackedTime", err) diff --git a/routers/web/user/home_test.go b/routers/web/user/home_test.go index a75b3573a00..534b0b26207 100644 --- a/routers/web/user/home_test.go +++ b/routers/web/user/home_test.go @@ -26,7 +26,7 @@ func TestArchivedIssues(t *testing.T) { // Assume: User 30 has access to two Repos with Issues, one of the Repos being archived. repos, _, _ := repo_model.GetUserRepositories(&repo_model.SearchRepoOptions{Actor: ctx.Doer}) - assert.Len(t, repos, 2) + assert.Len(t, repos, 3) IsArchived := make(map[int64]bool) NumIssues := make(map[int64]int) for _, repo := range repos { diff --git a/routers/web/web.go b/routers/web/web.go index 889a89f0d4a..f9d97758a1b 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -234,8 +234,6 @@ func RegisterRoutes(m *web.Route) { ignExploreSignIn := context.Toggle(&context.ToggleOptions{SignInRequired: setting.Service.RequireSignInView || setting.Service.Explore.RequireSigninView}) ignSignInAndCsrf := context.Toggle(&context.ToggleOptions{DisableCSRF: true}) reqSignOut := context.Toggle(&context.ToggleOptions{SignOutRequired: true}) - - bindIgnErr := web.Bind validation.AddBindingRules() linkAccountEnabled := func(ctx *context.Context) { @@ -356,32 +354,32 @@ func RegisterRoutes(m *web.Route) { // ***** START: User ***** m.Group("/user", func() { m.Get("/login", auth.SignIn) - m.Post("/login", bindIgnErr(forms.SignInForm{}), auth.SignInPost) + m.Post("/login", web.Bind(forms.SignInForm{}), auth.SignInPost) m.Group("", func() { m.Combo("/login/openid"). Get(auth.SignInOpenID). - Post(bindIgnErr(forms.SignInOpenIDForm{}), auth.SignInOpenIDPost) + Post(web.Bind(forms.SignInOpenIDForm{}), auth.SignInOpenIDPost) }, openIDSignInEnabled) m.Group("/openid", func() { m.Combo("/connect"). Get(auth.ConnectOpenID). - Post(bindIgnErr(forms.ConnectOpenIDForm{}), auth.ConnectOpenIDPost) + Post(web.Bind(forms.ConnectOpenIDForm{}), auth.ConnectOpenIDPost) m.Group("/register", func() { m.Combo(""). Get(auth.RegisterOpenID, openIDSignUpEnabled). - Post(bindIgnErr(forms.SignUpOpenIDForm{}), auth.RegisterOpenIDPost) + Post(web.Bind(forms.SignUpOpenIDForm{}), auth.RegisterOpenIDPost) }, openIDSignUpEnabled) }, openIDSignInEnabled) m.Get("/sign_up", auth.SignUp) - m.Post("/sign_up", bindIgnErr(forms.RegisterForm{}), auth.SignUpPost) + m.Post("/sign_up", web.Bind(forms.RegisterForm{}), auth.SignUpPost) m.Get("/link_account", linkAccountEnabled, auth.LinkAccount) - m.Post("/link_account_signin", linkAccountEnabled, bindIgnErr(forms.SignInForm{}), auth.LinkAccountPostSignIn) - m.Post("/link_account_signup", linkAccountEnabled, bindIgnErr(forms.RegisterForm{}), auth.LinkAccountPostRegister) + m.Post("/link_account_signin", linkAccountEnabled, web.Bind(forms.SignInForm{}), auth.LinkAccountPostSignIn) + m.Post("/link_account_signup", linkAccountEnabled, web.Bind(forms.RegisterForm{}), auth.LinkAccountPostRegister) m.Group("/two_factor", func() { m.Get("", auth.TwoFactor) - m.Post("", bindIgnErr(forms.TwoFactorAuthForm{}), auth.TwoFactorPost) + m.Post("", web.Bind(forms.TwoFactorAuthForm{}), auth.TwoFactorPost) m.Get("/scratch", auth.TwoFactorScratch) - m.Post("/scratch", bindIgnErr(forms.TwoFactorScratchAuthForm{}), auth.TwoFactorScratchPost) + m.Post("/scratch", web.Bind(forms.TwoFactorScratchAuthForm{}), auth.TwoFactorScratchPost) }) m.Group("/webauthn", func() { m.Get("", auth.WebAuthn) @@ -393,34 +391,34 @@ func RegisterRoutes(m *web.Route) { m.Any("/user/events", routing.MarkLongPolling, events.Events) m.Group("/login/oauth", func() { - m.Get("/authorize", bindIgnErr(forms.AuthorizationForm{}), auth.AuthorizeOAuth) - m.Post("/grant", bindIgnErr(forms.GrantApplicationForm{}), auth.GrantApplicationOAuth) + m.Get("/authorize", web.Bind(forms.AuthorizationForm{}), auth.AuthorizeOAuth) + m.Post("/grant", web.Bind(forms.GrantApplicationForm{}), auth.GrantApplicationOAuth) // TODO manage redirection - m.Post("/authorize", bindIgnErr(forms.AuthorizationForm{}), auth.AuthorizeOAuth) + m.Post("/authorize", web.Bind(forms.AuthorizationForm{}), auth.AuthorizeOAuth) }, ignSignInAndCsrf, reqSignIn) m.Get("/login/oauth/userinfo", ignSignInAndCsrf, auth.InfoOAuth) - m.Post("/login/oauth/access_token", CorsHandler(), bindIgnErr(forms.AccessTokenForm{}), ignSignInAndCsrf, auth.AccessTokenOAuth) + m.Post("/login/oauth/access_token", CorsHandler(), web.Bind(forms.AccessTokenForm{}), ignSignInAndCsrf, auth.AccessTokenOAuth) m.Get("/login/oauth/keys", ignSignInAndCsrf, auth.OIDCKeys) - m.Post("/login/oauth/introspect", CorsHandler(), bindIgnErr(forms.IntrospectTokenForm{}), ignSignInAndCsrf, auth.IntrospectOAuth) + m.Post("/login/oauth/introspect", CorsHandler(), web.Bind(forms.IntrospectTokenForm{}), ignSignInAndCsrf, auth.IntrospectOAuth) m.Group("/user/settings", func() { m.Get("", user_setting.Profile) - m.Post("", bindIgnErr(forms.UpdateProfileForm{}), user_setting.ProfilePost) + m.Post("", web.Bind(forms.UpdateProfileForm{}), user_setting.ProfilePost) m.Get("/change_password", auth.MustChangePassword) - m.Post("/change_password", bindIgnErr(forms.MustChangePasswordForm{}), auth.MustChangePasswordPost) - m.Post("/avatar", bindIgnErr(forms.AvatarForm{}), user_setting.AvatarPost) + m.Post("/change_password", web.Bind(forms.MustChangePasswordForm{}), auth.MustChangePasswordPost) + m.Post("/avatar", web.Bind(forms.AvatarForm{}), user_setting.AvatarPost) m.Post("/avatar/delete", user_setting.DeleteAvatar) m.Group("/account", func() { - m.Combo("").Get(user_setting.Account).Post(bindIgnErr(forms.ChangePasswordForm{}), user_setting.AccountPost) - m.Post("/email", bindIgnErr(forms.AddEmailForm{}), user_setting.EmailPost) + m.Combo("").Get(user_setting.Account).Post(web.Bind(forms.ChangePasswordForm{}), user_setting.AccountPost) + m.Post("/email", web.Bind(forms.AddEmailForm{}), user_setting.EmailPost) m.Post("/email/delete", user_setting.DeleteEmail) m.Post("/delete", user_setting.DeleteAccount) }) m.Group("/appearance", func() { m.Get("", user_setting.Appearance) - m.Post("/language", bindIgnErr(forms.UpdateLanguageForm{}), user_setting.UpdateUserLang) + m.Post("/language", web.Bind(forms.UpdateLanguageForm{}), user_setting.UpdateUserLang) m.Post("/hidden_comments", user_setting.UpdateUserHiddenComments) - m.Post("/theme", bindIgnErr(forms.UpdateThemeForm{}), user_setting.UpdateUIThemePost) + m.Post("/theme", web.Bind(forms.UpdateThemeForm{}), user_setting.UpdateUIThemePost) }) m.Group("/security", func() { m.Get("", security.Security) @@ -428,15 +426,15 @@ func RegisterRoutes(m *web.Route) { m.Post("/regenerate_scratch", security.RegenerateScratchTwoFactor) m.Post("/disable", security.DisableTwoFactor) m.Get("/enroll", security.EnrollTwoFactor) - m.Post("/enroll", bindIgnErr(forms.TwoFactorAuthForm{}), security.EnrollTwoFactorPost) + m.Post("/enroll", web.Bind(forms.TwoFactorAuthForm{}), security.EnrollTwoFactorPost) }) m.Group("/webauthn", func() { - m.Post("/request_register", bindIgnErr(forms.WebauthnRegistrationForm{}), security.WebAuthnRegister) + m.Post("/request_register", web.Bind(forms.WebauthnRegistrationForm{}), security.WebAuthnRegister) m.Post("/register", security.WebauthnRegisterPost) - m.Post("/delete", bindIgnErr(forms.WebauthnDeleteForm{}), security.WebauthnDelete) + m.Post("/delete", web.Bind(forms.WebauthnDeleteForm{}), security.WebauthnDelete) }) m.Group("/openid", func() { - m.Post("", bindIgnErr(forms.AddOpenIDForm{}), security.OpenIDPost) + m.Post("", web.Bind(forms.AddOpenIDForm{}), security.OpenIDPost) m.Post("/delete", security.DeleteOpenID) m.Post("/toggle_visibility", security.ToggleOpenIDVisibility) }, openIDSignInEnabled) @@ -444,28 +442,28 @@ func RegisterRoutes(m *web.Route) { }) m.Group("/applications/oauth2", func() { m.Get("/{id}", user_setting.OAuth2ApplicationShow) - m.Post("/{id}", bindIgnErr(forms.EditOAuth2ApplicationForm{}), user_setting.OAuthApplicationsEdit) + m.Post("/{id}", web.Bind(forms.EditOAuth2ApplicationForm{}), user_setting.OAuthApplicationsEdit) m.Post("/{id}/regenerate_secret", user_setting.OAuthApplicationsRegenerateSecret) - m.Post("", bindIgnErr(forms.EditOAuth2ApplicationForm{}), user_setting.OAuthApplicationsPost) + m.Post("", web.Bind(forms.EditOAuth2ApplicationForm{}), user_setting.OAuthApplicationsPost) m.Post("/{id}/delete", user_setting.DeleteOAuth2Application) m.Post("/{id}/revoke/{grantId}", user_setting.RevokeOAuth2Grant) }) m.Combo("/applications").Get(user_setting.Applications). - Post(bindIgnErr(forms.NewAccessTokenForm{}), user_setting.ApplicationsPost) + Post(web.Bind(forms.NewAccessTokenForm{}), user_setting.ApplicationsPost) m.Post("/applications/delete", user_setting.DeleteApplication) m.Combo("/keys").Get(user_setting.Keys). - Post(bindIgnErr(forms.AddKeyForm{}), user_setting.KeysPost) + Post(web.Bind(forms.AddKeyForm{}), user_setting.KeysPost) m.Post("/keys/delete", user_setting.DeleteKey) m.Group("/packages", func() { m.Get("", user_setting.Packages) m.Group("/rules", func() { m.Group("/add", func() { m.Get("", user_setting.PackagesRuleAdd) - m.Post("", bindIgnErr(forms.PackageCleanupRuleForm{}), user_setting.PackagesRuleAddPost) + m.Post("", web.Bind(forms.PackageCleanupRuleForm{}), user_setting.PackagesRuleAddPost) }) m.Group("/{id}", func() { m.Get("", user_setting.PackagesRuleEdit) - m.Post("", bindIgnErr(forms.PackageCleanupRuleForm{}), user_setting.PackagesRuleEditPost) + m.Post("", web.Bind(forms.PackageCleanupRuleForm{}), user_setting.PackagesRuleEditPost) m.Get("/preview", user_setting.PackagesRulePreview) }) }) @@ -507,7 +505,7 @@ func RegisterRoutes(m *web.Route) { // ***** START: Admin ***** m.Group("/admin", func() { m.Get("", adminReq, admin.Dashboard) - m.Post("", adminReq, bindIgnErr(forms.AdminDashboardForm{}), admin.DashboardPost) + m.Post("", adminReq, web.Bind(forms.AdminDashboardForm{}), admin.DashboardPost) m.Group("/config", func() { m.Get("", admin.Config) @@ -532,10 +530,10 @@ func RegisterRoutes(m *web.Route) { m.Group("/users", func() { m.Get("", admin.Users) - m.Combo("/new").Get(admin.NewUser).Post(bindIgnErr(forms.AdminCreateUserForm{}), admin.NewUserPost) - m.Combo("/{userid}").Get(admin.EditUser).Post(bindIgnErr(forms.AdminEditUserForm{}), admin.EditUserPost) + m.Combo("/new").Get(admin.NewUser).Post(web.Bind(forms.AdminCreateUserForm{}), admin.NewUserPost) + m.Combo("/{userid}").Get(admin.EditUser).Post(web.Bind(forms.AdminEditUserForm{}), admin.EditUserPost) m.Post("/{userid}/delete", admin.DeleteUser) - m.Post("/{userid}/avatar", bindIgnErr(forms.AvatarForm{}), admin.AvatarPost) + m.Post("/{userid}/avatar", web.Bind(forms.AvatarForm{}), admin.AvatarPost) m.Post("/{userid}/avatar/delete", admin.DeleteAvatar) }) @@ -566,39 +564,39 @@ func RegisterRoutes(m *web.Route) { m.Get("", repo.WebHooksEdit) m.Post("/replay/{uuid}", repo.ReplayWebhook) }) - m.Post("/gitea/{id}", bindIgnErr(forms.NewWebhookForm{}), repo.GiteaHooksEditPost) - m.Post("/gogs/{id}", bindIgnErr(forms.NewGogshookForm{}), repo.GogsHooksEditPost) - m.Post("/slack/{id}", bindIgnErr(forms.NewSlackHookForm{}), repo.SlackHooksEditPost) - m.Post("/discord/{id}", bindIgnErr(forms.NewDiscordHookForm{}), repo.DiscordHooksEditPost) - m.Post("/dingtalk/{id}", bindIgnErr(forms.NewDingtalkHookForm{}), repo.DingtalkHooksEditPost) - m.Post("/telegram/{id}", bindIgnErr(forms.NewTelegramHookForm{}), repo.TelegramHooksEditPost) - m.Post("/matrix/{id}", bindIgnErr(forms.NewMatrixHookForm{}), repo.MatrixHooksEditPost) - m.Post("/msteams/{id}", bindIgnErr(forms.NewMSTeamsHookForm{}), repo.MSTeamsHooksEditPost) - m.Post("/feishu/{id}", bindIgnErr(forms.NewFeishuHookForm{}), repo.FeishuHooksEditPost) - m.Post("/wechatwork/{id}", bindIgnErr(forms.NewWechatWorkHookForm{}), repo.WechatworkHooksEditPost) - m.Post("/packagist/{id}", bindIgnErr(forms.NewPackagistHookForm{}), repo.PackagistHooksEditPost) + m.Post("/gitea/{id}", web.Bind(forms.NewWebhookForm{}), repo.GiteaHooksEditPost) + m.Post("/gogs/{id}", web.Bind(forms.NewGogshookForm{}), repo.GogsHooksEditPost) + m.Post("/slack/{id}", web.Bind(forms.NewSlackHookForm{}), repo.SlackHooksEditPost) + m.Post("/discord/{id}", web.Bind(forms.NewDiscordHookForm{}), repo.DiscordHooksEditPost) + m.Post("/dingtalk/{id}", web.Bind(forms.NewDingtalkHookForm{}), repo.DingtalkHooksEditPost) + m.Post("/telegram/{id}", web.Bind(forms.NewTelegramHookForm{}), repo.TelegramHooksEditPost) + m.Post("/matrix/{id}", web.Bind(forms.NewMatrixHookForm{}), repo.MatrixHooksEditPost) + m.Post("/msteams/{id}", web.Bind(forms.NewMSTeamsHookForm{}), repo.MSTeamsHooksEditPost) + m.Post("/feishu/{id}", web.Bind(forms.NewFeishuHookForm{}), repo.FeishuHooksEditPost) + m.Post("/wechatwork/{id}", web.Bind(forms.NewWechatWorkHookForm{}), repo.WechatworkHooksEditPost) + m.Post("/packagist/{id}", web.Bind(forms.NewPackagistHookForm{}), repo.PackagistHooksEditPost) }, webhooksEnabled) m.Group("/{configType:default-hooks|system-hooks}", func() { m.Get("/{type}/new", repo.WebhooksNew) - m.Post("/gitea/new", bindIgnErr(forms.NewWebhookForm{}), repo.GiteaHooksNewPost) - m.Post("/gogs/new", bindIgnErr(forms.NewGogshookForm{}), repo.GogsHooksNewPost) - m.Post("/slack/new", bindIgnErr(forms.NewSlackHookForm{}), repo.SlackHooksNewPost) - m.Post("/discord/new", bindIgnErr(forms.NewDiscordHookForm{}), repo.DiscordHooksNewPost) - m.Post("/dingtalk/new", bindIgnErr(forms.NewDingtalkHookForm{}), repo.DingtalkHooksNewPost) - m.Post("/telegram/new", bindIgnErr(forms.NewTelegramHookForm{}), repo.TelegramHooksNewPost) - m.Post("/matrix/new", bindIgnErr(forms.NewMatrixHookForm{}), repo.MatrixHooksNewPost) - m.Post("/msteams/new", bindIgnErr(forms.NewMSTeamsHookForm{}), repo.MSTeamsHooksNewPost) - m.Post("/feishu/new", bindIgnErr(forms.NewFeishuHookForm{}), repo.FeishuHooksNewPost) - m.Post("/wechatwork/new", bindIgnErr(forms.NewWechatWorkHookForm{}), repo.WechatworkHooksNewPost) - m.Post("/packagist/new", bindIgnErr(forms.NewPackagistHookForm{}), repo.PackagistHooksNewPost) + m.Post("/gitea/new", web.Bind(forms.NewWebhookForm{}), repo.GiteaHooksNewPost) + m.Post("/gogs/new", web.Bind(forms.NewGogshookForm{}), repo.GogsHooksNewPost) + m.Post("/slack/new", web.Bind(forms.NewSlackHookForm{}), repo.SlackHooksNewPost) + m.Post("/discord/new", web.Bind(forms.NewDiscordHookForm{}), repo.DiscordHooksNewPost) + m.Post("/dingtalk/new", web.Bind(forms.NewDingtalkHookForm{}), repo.DingtalkHooksNewPost) + m.Post("/telegram/new", web.Bind(forms.NewTelegramHookForm{}), repo.TelegramHooksNewPost) + m.Post("/matrix/new", web.Bind(forms.NewMatrixHookForm{}), repo.MatrixHooksNewPost) + m.Post("/msteams/new", web.Bind(forms.NewMSTeamsHookForm{}), repo.MSTeamsHooksNewPost) + m.Post("/feishu/new", web.Bind(forms.NewFeishuHookForm{}), repo.FeishuHooksNewPost) + m.Post("/wechatwork/new", web.Bind(forms.NewWechatWorkHookForm{}), repo.WechatworkHooksNewPost) + m.Post("/packagist/new", web.Bind(forms.NewPackagistHookForm{}), repo.PackagistHooksNewPost) }) m.Group("/auths", func() { m.Get("", admin.Authentications) - m.Combo("/new").Get(admin.NewAuthSource).Post(bindIgnErr(forms.AuthenticationForm{}), admin.NewAuthSourcePost) + m.Combo("/new").Get(admin.NewAuthSource).Post(web.Bind(forms.AuthenticationForm{}), admin.NewAuthSourcePost) m.Combo("/{authid}").Get(admin.EditAuthSource). - Post(bindIgnErr(forms.AuthenticationForm{}), admin.EditAuthSourcePost) + Post(web.Bind(forms.AuthenticationForm{}), admin.EditAuthSourcePost) m.Post("/{authid}/delete", admin.DeleteAuthSource) }) @@ -610,9 +608,9 @@ func RegisterRoutes(m *web.Route) { m.Group("/applications", func() { m.Get("", admin.Applications) - m.Post("/oauth2", bindIgnErr(forms.EditOAuth2ApplicationForm{}), admin.ApplicationsPost) + m.Post("/oauth2", web.Bind(forms.EditOAuth2ApplicationForm{}), admin.ApplicationsPost) m.Group("/oauth2/{id}", func() { - m.Combo("").Get(admin.EditApplication).Post(bindIgnErr(forms.EditOAuth2ApplicationForm{}), admin.EditApplicationPost) + m.Combo("").Get(admin.EditApplication).Post(web.Bind(forms.EditOAuth2ApplicationForm{}), admin.EditApplicationPost) m.Post("/regenerate_secret", admin.ApplicationsRegenerateSecret) m.Post("/delete", admin.DeleteApplication) }) @@ -657,7 +655,6 @@ func RegisterRoutes(m *web.Route) { reqRepoReleaseWriter := context.RequireRepoWriter(unit.TypeReleases) reqRepoReleaseReader := context.RequireRepoReader(unit.TypeReleases) reqRepoWikiWriter := context.RequireRepoWriter(unit.TypeWiki) - reqRepoIssueWriter := context.RequireRepoWriter(unit.TypeIssues) reqRepoIssueReader := context.RequireRepoReader(unit.TypeIssues) reqRepoPullsReader := context.RequireRepoReader(unit.TypePullRequests) reqRepoIssuesOrPullsWriter := context.RequireRepoWriterOr(unit.TypeIssues, unit.TypePullRequests) @@ -683,7 +680,7 @@ func RegisterRoutes(m *web.Route) { m.Group("/org", func() { m.Group("", func() { m.Get("/create", org.Create) - m.Post("/create", bindIgnErr(forms.CreateOrgForm{}), org.CreatePost) + m.Post("/create", web.Bind(forms.CreateOrgForm{}), org.CreatePost) }) m.Group("/invite/{token}", func() { @@ -713,22 +710,22 @@ func RegisterRoutes(m *web.Route) { m.Group("/{org}", func() { m.Get("/teams/new", org.NewTeam) - m.Post("/teams/new", bindIgnErr(forms.CreateTeamForm{}), org.NewTeamPost) + m.Post("/teams/new", web.Bind(forms.CreateTeamForm{}), org.NewTeamPost) m.Get("/teams/-/search", org.SearchTeam) m.Get("/teams/{team}/edit", org.EditTeam) - m.Post("/teams/{team}/edit", bindIgnErr(forms.CreateTeamForm{}), org.EditTeamPost) + m.Post("/teams/{team}/edit", web.Bind(forms.CreateTeamForm{}), org.EditTeamPost) m.Post("/teams/{team}/delete", org.DeleteTeam) m.Group("/settings", func() { m.Combo("").Get(org.Settings). - Post(bindIgnErr(forms.UpdateOrgSettingForm{}), org.SettingsPost) - m.Post("/avatar", bindIgnErr(forms.AvatarForm{}), org.SettingsAvatar) + Post(web.Bind(forms.UpdateOrgSettingForm{}), org.SettingsPost) + m.Post("/avatar", web.Bind(forms.AvatarForm{}), org.SettingsAvatar) m.Post("/avatar/delete", org.SettingsDeleteAvatar) m.Group("/applications", func() { m.Get("", org.Applications) - m.Post("/oauth2", bindIgnErr(forms.EditOAuth2ApplicationForm{}), org.OAuthApplicationsPost) + m.Post("/oauth2", web.Bind(forms.EditOAuth2ApplicationForm{}), org.OAuthApplicationsPost) m.Group("/oauth2/{id}", func() { - m.Combo("").Get(org.OAuth2ApplicationShow).Post(bindIgnErr(forms.EditOAuth2ApplicationForm{}), org.OAuth2ApplicationEdit) + m.Combo("").Get(org.OAuth2ApplicationShow).Post(web.Bind(forms.EditOAuth2ApplicationForm{}), org.OAuth2ApplicationEdit) m.Post("/regenerate_secret", org.OAuthApplicationsRegenerateSecret) m.Post("/delete", org.DeleteOAuth2Application) }) @@ -743,38 +740,38 @@ func RegisterRoutes(m *web.Route) { m.Get("", org.Webhooks) m.Post("/delete", org.DeleteWebhook) m.Get("/{type}/new", repo.WebhooksNew) - m.Post("/gitea/new", bindIgnErr(forms.NewWebhookForm{}), repo.GiteaHooksNewPost) - m.Post("/gogs/new", bindIgnErr(forms.NewGogshookForm{}), repo.GogsHooksNewPost) - m.Post("/slack/new", bindIgnErr(forms.NewSlackHookForm{}), repo.SlackHooksNewPost) - m.Post("/discord/new", bindIgnErr(forms.NewDiscordHookForm{}), repo.DiscordHooksNewPost) - m.Post("/dingtalk/new", bindIgnErr(forms.NewDingtalkHookForm{}), repo.DingtalkHooksNewPost) - m.Post("/telegram/new", bindIgnErr(forms.NewTelegramHookForm{}), repo.TelegramHooksNewPost) - m.Post("/matrix/new", bindIgnErr(forms.NewMatrixHookForm{}), repo.MatrixHooksNewPost) - m.Post("/msteams/new", bindIgnErr(forms.NewMSTeamsHookForm{}), repo.MSTeamsHooksNewPost) - m.Post("/feishu/new", bindIgnErr(forms.NewFeishuHookForm{}), repo.FeishuHooksNewPost) - m.Post("/wechatwork/new", bindIgnErr(forms.NewWechatWorkHookForm{}), repo.WechatworkHooksNewPost) + m.Post("/gitea/new", web.Bind(forms.NewWebhookForm{}), repo.GiteaHooksNewPost) + m.Post("/gogs/new", web.Bind(forms.NewGogshookForm{}), repo.GogsHooksNewPost) + m.Post("/slack/new", web.Bind(forms.NewSlackHookForm{}), repo.SlackHooksNewPost) + m.Post("/discord/new", web.Bind(forms.NewDiscordHookForm{}), repo.DiscordHooksNewPost) + m.Post("/dingtalk/new", web.Bind(forms.NewDingtalkHookForm{}), repo.DingtalkHooksNewPost) + m.Post("/telegram/new", web.Bind(forms.NewTelegramHookForm{}), repo.TelegramHooksNewPost) + m.Post("/matrix/new", web.Bind(forms.NewMatrixHookForm{}), repo.MatrixHooksNewPost) + m.Post("/msteams/new", web.Bind(forms.NewMSTeamsHookForm{}), repo.MSTeamsHooksNewPost) + m.Post("/feishu/new", web.Bind(forms.NewFeishuHookForm{}), repo.FeishuHooksNewPost) + m.Post("/wechatwork/new", web.Bind(forms.NewWechatWorkHookForm{}), repo.WechatworkHooksNewPost) m.Group("/{id}", func() { m.Get("", repo.WebHooksEdit) m.Post("/replay/{uuid}", repo.ReplayWebhook) }) - m.Post("/gitea/{id}", bindIgnErr(forms.NewWebhookForm{}), repo.GiteaHooksEditPost) - m.Post("/gogs/{id}", bindIgnErr(forms.NewGogshookForm{}), repo.GogsHooksEditPost) - m.Post("/slack/{id}", bindIgnErr(forms.NewSlackHookForm{}), repo.SlackHooksEditPost) - m.Post("/discord/{id}", bindIgnErr(forms.NewDiscordHookForm{}), repo.DiscordHooksEditPost) - m.Post("/dingtalk/{id}", bindIgnErr(forms.NewDingtalkHookForm{}), repo.DingtalkHooksEditPost) - m.Post("/telegram/{id}", bindIgnErr(forms.NewTelegramHookForm{}), repo.TelegramHooksEditPost) - m.Post("/matrix/{id}", bindIgnErr(forms.NewMatrixHookForm{}), repo.MatrixHooksEditPost) - m.Post("/msteams/{id}", bindIgnErr(forms.NewMSTeamsHookForm{}), repo.MSTeamsHooksEditPost) - m.Post("/feishu/{id}", bindIgnErr(forms.NewFeishuHookForm{}), repo.FeishuHooksEditPost) - m.Post("/wechatwork/{id}", bindIgnErr(forms.NewWechatWorkHookForm{}), repo.WechatworkHooksEditPost) + m.Post("/gitea/{id}", web.Bind(forms.NewWebhookForm{}), repo.GiteaHooksEditPost) + m.Post("/gogs/{id}", web.Bind(forms.NewGogshookForm{}), repo.GogsHooksEditPost) + m.Post("/slack/{id}", web.Bind(forms.NewSlackHookForm{}), repo.SlackHooksEditPost) + m.Post("/discord/{id}", web.Bind(forms.NewDiscordHookForm{}), repo.DiscordHooksEditPost) + m.Post("/dingtalk/{id}", web.Bind(forms.NewDingtalkHookForm{}), repo.DingtalkHooksEditPost) + m.Post("/telegram/{id}", web.Bind(forms.NewTelegramHookForm{}), repo.TelegramHooksEditPost) + m.Post("/matrix/{id}", web.Bind(forms.NewMatrixHookForm{}), repo.MatrixHooksEditPost) + m.Post("/msteams/{id}", web.Bind(forms.NewMSTeamsHookForm{}), repo.MSTeamsHooksEditPost) + m.Post("/feishu/{id}", web.Bind(forms.NewFeishuHookForm{}), repo.FeishuHooksEditPost) + m.Post("/wechatwork/{id}", web.Bind(forms.NewWechatWorkHookForm{}), repo.WechatworkHooksEditPost) }, webhooksEnabled) m.Group("/labels", func() { m.Get("", org.RetrieveLabels, org.Labels) - m.Post("/new", bindIgnErr(forms.CreateLabelForm{}), org.NewLabel) - m.Post("/edit", bindIgnErr(forms.CreateLabelForm{}), org.UpdateLabel) + m.Post("/new", web.Bind(forms.CreateLabelForm{}), org.NewLabel) + m.Post("/edit", web.Bind(forms.CreateLabelForm{}), org.UpdateLabel) m.Post("/delete", org.DeleteLabel) - m.Post("/initialize", bindIgnErr(forms.InitializeLabelsForm{}), org.InitializeLabels) + m.Post("/initialize", web.Bind(forms.InitializeLabelsForm{}), org.InitializeLabels) }) m.Route("/delete", "GET,POST", org.SettingsDelete) @@ -784,11 +781,11 @@ func RegisterRoutes(m *web.Route) { m.Group("/rules", func() { m.Group("/add", func() { m.Get("", org.PackagesRuleAdd) - m.Post("", bindIgnErr(forms.PackageCleanupRuleForm{}), org.PackagesRuleAddPost) + m.Post("", web.Bind(forms.PackageCleanupRuleForm{}), org.PackagesRuleAddPost) }) m.Group("/{id}", func() { m.Get("", org.PackagesRuleEdit) - m.Post("", bindIgnErr(forms.PackageCleanupRuleForm{}), org.PackagesRuleEditPost) + m.Post("", web.Bind(forms.PackageCleanupRuleForm{}), org.PackagesRuleEditPost) m.Get("/preview", org.PackagesRulePreview) }) }) @@ -804,12 +801,12 @@ func RegisterRoutes(m *web.Route) { // ***** START: Repository ***** m.Group("/repo", func() { m.Get("/create", repo.Create) - m.Post("/create", bindIgnErr(forms.CreateRepoForm{}), repo.CreatePost) + m.Post("/create", web.Bind(forms.CreateRepoForm{}), repo.CreatePost) m.Get("/migrate", repo.Migrate) - m.Post("/migrate", bindIgnErr(forms.MigrateRepoForm{}), repo.MigratePost) + m.Post("/migrate", web.Bind(forms.MigrateRepoForm{}), repo.MigratePost) m.Group("/fork", func() { m.Combo("/{repoid}").Get(repo.Fork). - Post(bindIgnErr(forms.CreateRepoForm{}), repo.ForkPost) + Post(web.Bind(forms.CreateRepoForm{}), repo.ForkPost) }, context.RepoIDAssignment(), context.UnitTypes(), reqRepoCodeReader) m.Get("/search", repo.SearchRepo) }, reqSignIn) @@ -826,7 +823,7 @@ func RegisterRoutes(m *web.Route) { m.Get("/files/{fileid}", user.DownloadPackageFile) m.Group("/settings", func() { m.Get("", user.PackageSettings) - m.Post("", bindIgnErr(forms.PackageSettingForm{}), user.PackageSettingsPost) + m.Post("", web.Bind(forms.PackageSettingForm{}), user.PackageSettingsPost) }, reqPackageAccess(perm.AccessModeWrite)) }) }) @@ -842,9 +839,9 @@ func RegisterRoutes(m *web.Route) { m.Group("/settings", func() { m.Group("", func() { m.Combo("").Get(repo.Settings). - Post(bindIgnErr(forms.RepoSettingForm{}), repo.SettingsPost) + Post(web.Bind(forms.RepoSettingForm{}), repo.SettingsPost) }, repo.SettingsCtxData) - m.Post("/avatar", bindIgnErr(forms.AvatarForm{}), repo.SettingsAvatar) + m.Post("/avatar", web.Bind(forms.AvatarForm{}), repo.SettingsAvatar) m.Post("/avatar/delete", repo.SettingsDeleteAvatar) m.Group("/collaboration", func() { @@ -860,16 +857,16 @@ func RegisterRoutes(m *web.Route) { m.Group("/branches", func() { m.Combo("").Get(repo.ProtectedBranch).Post(repo.ProtectedBranchPost) m.Combo("/*").Get(repo.SettingsProtectedBranch). - Post(bindIgnErr(forms.ProtectBranchForm{}), context.RepoMustNotBeArchived(), repo.SettingsProtectedBranchPost) + Post(web.Bind(forms.ProtectBranchForm{}), context.RepoMustNotBeArchived(), repo.SettingsProtectedBranchPost) }, repo.MustBeNotEmpty) - m.Post("/rename_branch", bindIgnErr(forms.RenameBranchForm{}), context.RepoMustNotBeArchived(), repo.RenameBranchPost) + m.Post("/rename_branch", web.Bind(forms.RenameBranchForm{}), context.RepoMustNotBeArchived(), repo.RenameBranchPost) m.Group("/tags", func() { m.Get("", repo.Tags) - m.Post("", bindIgnErr(forms.ProtectTagForm{}), context.RepoMustNotBeArchived(), repo.NewProtectedTagPost) + m.Post("", web.Bind(forms.ProtectTagForm{}), context.RepoMustNotBeArchived(), repo.NewProtectedTagPost) m.Post("/delete", context.RepoMustNotBeArchived(), repo.DeleteProtectedTagPost) m.Get("/{id}", repo.EditProtectedTag) - m.Post("/{id}", bindIgnErr(forms.ProtectTagForm{}), context.RepoMustNotBeArchived(), repo.EditProtectedTagPost) + m.Post("/{id}", web.Bind(forms.ProtectTagForm{}), context.RepoMustNotBeArchived(), repo.EditProtectedTagPost) }) m.Group("/hooks/git", func() { @@ -882,38 +879,38 @@ func RegisterRoutes(m *web.Route) { m.Get("", repo.Webhooks) m.Post("/delete", repo.DeleteWebhook) m.Get("/{type}/new", repo.WebhooksNew) - m.Post("/gitea/new", bindIgnErr(forms.NewWebhookForm{}), repo.GiteaHooksNewPost) - m.Post("/gogs/new", bindIgnErr(forms.NewGogshookForm{}), repo.GogsHooksNewPost) - m.Post("/slack/new", bindIgnErr(forms.NewSlackHookForm{}), repo.SlackHooksNewPost) - m.Post("/discord/new", bindIgnErr(forms.NewDiscordHookForm{}), repo.DiscordHooksNewPost) - m.Post("/dingtalk/new", bindIgnErr(forms.NewDingtalkHookForm{}), repo.DingtalkHooksNewPost) - m.Post("/telegram/new", bindIgnErr(forms.NewTelegramHookForm{}), repo.TelegramHooksNewPost) - m.Post("/matrix/new", bindIgnErr(forms.NewMatrixHookForm{}), repo.MatrixHooksNewPost) - m.Post("/msteams/new", bindIgnErr(forms.NewMSTeamsHookForm{}), repo.MSTeamsHooksNewPost) - m.Post("/feishu/new", bindIgnErr(forms.NewFeishuHookForm{}), repo.FeishuHooksNewPost) - m.Post("/wechatwork/new", bindIgnErr(forms.NewWechatWorkHookForm{}), repo.WechatworkHooksNewPost) - m.Post("/packagist/new", bindIgnErr(forms.NewPackagistHookForm{}), repo.PackagistHooksNewPost) + m.Post("/gitea/new", web.Bind(forms.NewWebhookForm{}), repo.GiteaHooksNewPost) + m.Post("/gogs/new", web.Bind(forms.NewGogshookForm{}), repo.GogsHooksNewPost) + m.Post("/slack/new", web.Bind(forms.NewSlackHookForm{}), repo.SlackHooksNewPost) + m.Post("/discord/new", web.Bind(forms.NewDiscordHookForm{}), repo.DiscordHooksNewPost) + m.Post("/dingtalk/new", web.Bind(forms.NewDingtalkHookForm{}), repo.DingtalkHooksNewPost) + m.Post("/telegram/new", web.Bind(forms.NewTelegramHookForm{}), repo.TelegramHooksNewPost) + m.Post("/matrix/new", web.Bind(forms.NewMatrixHookForm{}), repo.MatrixHooksNewPost) + m.Post("/msteams/new", web.Bind(forms.NewMSTeamsHookForm{}), repo.MSTeamsHooksNewPost) + m.Post("/feishu/new", web.Bind(forms.NewFeishuHookForm{}), repo.FeishuHooksNewPost) + m.Post("/wechatwork/new", web.Bind(forms.NewWechatWorkHookForm{}), repo.WechatworkHooksNewPost) + m.Post("/packagist/new", web.Bind(forms.NewPackagistHookForm{}), repo.PackagistHooksNewPost) m.Group("/{id}", func() { m.Get("", repo.WebHooksEdit) m.Post("/test", repo.TestWebhook) m.Post("/replay/{uuid}", repo.ReplayWebhook) }) - m.Post("/gitea/{id}", bindIgnErr(forms.NewWebhookForm{}), repo.GiteaHooksEditPost) - m.Post("/gogs/{id}", bindIgnErr(forms.NewGogshookForm{}), repo.GogsHooksEditPost) - m.Post("/slack/{id}", bindIgnErr(forms.NewSlackHookForm{}), repo.SlackHooksEditPost) - m.Post("/discord/{id}", bindIgnErr(forms.NewDiscordHookForm{}), repo.DiscordHooksEditPost) - m.Post("/dingtalk/{id}", bindIgnErr(forms.NewDingtalkHookForm{}), repo.DingtalkHooksEditPost) - m.Post("/telegram/{id}", bindIgnErr(forms.NewTelegramHookForm{}), repo.TelegramHooksEditPost) - m.Post("/matrix/{id}", bindIgnErr(forms.NewMatrixHookForm{}), repo.MatrixHooksEditPost) - m.Post("/msteams/{id}", bindIgnErr(forms.NewMSTeamsHookForm{}), repo.MSTeamsHooksEditPost) - m.Post("/feishu/{id}", bindIgnErr(forms.NewFeishuHookForm{}), repo.FeishuHooksEditPost) - m.Post("/wechatwork/{id}", bindIgnErr(forms.NewWechatWorkHookForm{}), repo.WechatworkHooksEditPost) - m.Post("/packagist/{id}", bindIgnErr(forms.NewPackagistHookForm{}), repo.PackagistHooksEditPost) + m.Post("/gitea/{id}", web.Bind(forms.NewWebhookForm{}), repo.GiteaHooksEditPost) + m.Post("/gogs/{id}", web.Bind(forms.NewGogshookForm{}), repo.GogsHooksEditPost) + m.Post("/slack/{id}", web.Bind(forms.NewSlackHookForm{}), repo.SlackHooksEditPost) + m.Post("/discord/{id}", web.Bind(forms.NewDiscordHookForm{}), repo.DiscordHooksEditPost) + m.Post("/dingtalk/{id}", web.Bind(forms.NewDingtalkHookForm{}), repo.DingtalkHooksEditPost) + m.Post("/telegram/{id}", web.Bind(forms.NewTelegramHookForm{}), repo.TelegramHooksEditPost) + m.Post("/matrix/{id}", web.Bind(forms.NewMatrixHookForm{}), repo.MatrixHooksEditPost) + m.Post("/msteams/{id}", web.Bind(forms.NewMSTeamsHookForm{}), repo.MSTeamsHooksEditPost) + m.Post("/feishu/{id}", web.Bind(forms.NewFeishuHookForm{}), repo.FeishuHooksEditPost) + m.Post("/wechatwork/{id}", web.Bind(forms.NewWechatWorkHookForm{}), repo.WechatworkHooksEditPost) + m.Post("/packagist/{id}", web.Bind(forms.NewPackagistHookForm{}), repo.PackagistHooksEditPost) }, webhooksEnabled) m.Group("/keys", func() { m.Combo("").Get(repo.DeployKeys). - Post(bindIgnErr(forms.AddKeyForm{}), repo.DeployKeysPost) + Post(web.Bind(forms.AddKeyForm{}), repo.DeployKeysPost) m.Post("/delete", repo.DeleteDeployKey) }) @@ -952,7 +949,7 @@ func RegisterRoutes(m *web.Route) { m.Get("/compare", repo.MustBeNotEmpty, reqRepoCodeReader, repo.SetEditorconfigIfExists, ignSignIn, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.CompareDiff) m.Combo("/compare/*", repo.MustBeNotEmpty, reqRepoCodeReader, repo.SetEditorconfigIfExists). Get(ignSignIn, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.CompareDiff). - Post(reqSignIn, context.RepoMustNotBeArchived(), reqRepoPullsReader, repo.MustAllowPulls, bindIgnErr(forms.CreateIssueForm{}), repo.SetWhitespaceBehavior, repo.CompareAndPullRequestPost) + Post(reqSignIn, context.RepoMustNotBeArchived(), reqRepoPullsReader, repo.MustAllowPulls, web.Bind(forms.CreateIssueForm{}), repo.SetWhitespaceBehavior, repo.CompareAndPullRequestPost) m.Group("/{type:issues|pulls}", func() { m.Group("/{index}", func() { m.Get("/info", repo.GetIssueInfo) @@ -965,7 +962,7 @@ func RegisterRoutes(m *web.Route) { m.Group("/issues", func() { m.Group("/new", func() { m.Combo("").Get(context.RepoRef(), repo.NewIssue). - Post(bindIgnErr(forms.CreateIssueForm{}), repo.NewIssuePost) + Post(web.Bind(forms.CreateIssueForm{}), repo.NewIssuePost) m.Get("/choose", context.RepoRef(), repo.NewIssueChooseTemplate) }) m.Get("/search", repo.ListIssues) @@ -976,7 +973,7 @@ func RegisterRoutes(m *web.Route) { m.Group("/{index}", func() { m.Post("/title", repo.UpdateIssueTitle) m.Post("/content", repo.UpdateIssueContent) - m.Post("/deadline", bindIgnErr(structs.EditDeadlineOption{}), repo.UpdateIssueDeadline) + m.Post("/deadline", web.Bind(structs.EditDeadlineOption{}), repo.UpdateIssueDeadline) m.Post("/watch", repo.IssueWatch) m.Post("/ref", repo.UpdateIssueRef) m.Post("/viewed-files", repo.UpdateViewedFiles) @@ -984,18 +981,18 @@ func RegisterRoutes(m *web.Route) { m.Post("/add", repo.AddDependency) m.Post("/delete", repo.RemoveDependency) }) - m.Combo("/comments").Post(repo.MustAllowUserComment, bindIgnErr(forms.CreateCommentForm{}), repo.NewComment) + m.Combo("/comments").Post(repo.MustAllowUserComment, web.Bind(forms.CreateCommentForm{}), repo.NewComment) m.Group("/times", func() { - m.Post("/add", bindIgnErr(forms.AddTimeManuallyForm{}), repo.AddTimeManually) + m.Post("/add", web.Bind(forms.AddTimeManuallyForm{}), repo.AddTimeManually) m.Post("/{timeid}/delete", repo.DeleteTime) m.Group("/stopwatch", func() { m.Post("/toggle", repo.IssueStopwatch) m.Post("/cancel", repo.CancelStopwatch) }) }) - m.Post("/reactions/{action}", bindIgnErr(forms.ReactionForm{}), repo.ChangeIssueReaction) - m.Post("/lock", reqRepoIssueWriter, bindIgnErr(forms.IssueLockForm{}), repo.LockIssue) - m.Post("/unlock", reqRepoIssueWriter, repo.UnlockIssue) + m.Post("/reactions/{action}", web.Bind(forms.ReactionForm{}), repo.ChangeIssueReaction) + m.Post("/lock", reqRepoIssuesOrPullsWriter, web.Bind(forms.IssueLockForm{}), repo.LockIssue) + m.Post("/unlock", reqRepoIssuesOrPullsWriter, repo.UnlockIssue) m.Post("/delete", reqRepoAdmin, repo.DeleteIssue) }, context.RepoMustNotBeArchived()) m.Group("/{index}", func() { @@ -1011,7 +1008,7 @@ func RegisterRoutes(m *web.Route) { m.Post("/projects", reqRepoIssuesOrPullsWriter, reqRepoProjectsReader, repo.UpdateIssueProject) m.Post("/assignee", reqRepoIssuesOrPullsWriter, repo.UpdateIssueAssignee) m.Post("/request_review", reqRepoIssuesOrPullsReader, repo.UpdatePullReviewRequest) - m.Post("/dismiss_review", reqRepoAdmin, bindIgnErr(forms.DismissReviewForm{}), repo.DismissReview) + m.Post("/dismiss_review", reqRepoAdmin, web.Bind(forms.DismissReviewForm{}), repo.DismissReview) m.Post("/status", reqRepoIssuesOrPullsWriter, repo.UpdateIssueStatus) m.Post("/resolve_conversation", reqRepoIssuesOrPullsReader, repo.UpdateResolveConversation) m.Post("/attachments", repo.UploadIssueAttachment) @@ -1020,23 +1017,23 @@ func RegisterRoutes(m *web.Route) { m.Group("/comments/{id}", func() { m.Post("", repo.UpdateCommentContent) m.Post("/delete", repo.DeleteComment) - m.Post("/reactions/{action}", bindIgnErr(forms.ReactionForm{}), repo.ChangeCommentReaction) + m.Post("/reactions/{action}", web.Bind(forms.ReactionForm{}), repo.ChangeCommentReaction) }, context.RepoMustNotBeArchived()) m.Group("/comments/{id}", func() { m.Get("/attachments", repo.GetCommentAttachments) }) - m.Post("/markdown", bindIgnErr(structs.MarkdownOption{}), misc.Markdown) + m.Post("/markdown", web.Bind(structs.MarkdownOption{}), misc.Markdown) m.Group("/labels", func() { - m.Post("/new", bindIgnErr(forms.CreateLabelForm{}), repo.NewLabel) - m.Post("/edit", bindIgnErr(forms.CreateLabelForm{}), repo.UpdateLabel) + m.Post("/new", web.Bind(forms.CreateLabelForm{}), repo.NewLabel) + m.Post("/edit", web.Bind(forms.CreateLabelForm{}), repo.UpdateLabel) m.Post("/delete", repo.DeleteLabel) - m.Post("/initialize", bindIgnErr(forms.InitializeLabelsForm{}), repo.InitializeLabels) + m.Post("/initialize", web.Bind(forms.InitializeLabelsForm{}), repo.InitializeLabels) }, context.RepoMustNotBeArchived(), reqRepoIssuesOrPullsWriter, context.RepoRef()) m.Group("/milestones", func() { m.Combo("/new").Get(repo.NewMilestone). - Post(bindIgnErr(forms.CreateMilestoneForm{}), repo.NewMilestonePost) + Post(web.Bind(forms.CreateMilestoneForm{}), repo.NewMilestonePost) m.Get("/{id}/edit", repo.EditMilestone) - m.Post("/{id}/edit", bindIgnErr(forms.CreateMilestoneForm{}), repo.EditMilestonePost) + m.Post("/{id}/edit", web.Bind(forms.CreateMilestoneForm{}), repo.EditMilestonePost) m.Post("/{id}/{action}", repo.ChangeMilestoneStatus) m.Post("/delete", repo.DeleteMilestone) }, context.RepoMustNotBeArchived(), reqRepoIssuesOrPullsWriter, context.RepoRef()) @@ -1047,23 +1044,23 @@ func RegisterRoutes(m *web.Route) { m.Group("", func() { m.Group("", func() { m.Combo("/_edit/*").Get(repo.EditFile). - Post(bindIgnErr(forms.EditRepoFileForm{}), repo.EditFilePost) + Post(web.Bind(forms.EditRepoFileForm{}), repo.EditFilePost) m.Combo("/_new/*").Get(repo.NewFile). - Post(bindIgnErr(forms.EditRepoFileForm{}), repo.NewFilePost) - m.Post("/_preview/*", bindIgnErr(forms.EditPreviewDiffForm{}), repo.DiffPreviewPost) + Post(web.Bind(forms.EditRepoFileForm{}), repo.NewFilePost) + m.Post("/_preview/*", web.Bind(forms.EditPreviewDiffForm{}), repo.DiffPreviewPost) m.Combo("/_delete/*").Get(repo.DeleteFile). - Post(bindIgnErr(forms.DeleteRepoFileForm{}), repo.DeleteFilePost) + Post(web.Bind(forms.DeleteRepoFileForm{}), repo.DeleteFilePost) m.Combo("/_upload/*", repo.MustBeAbleToUpload). Get(repo.UploadFile). - Post(bindIgnErr(forms.UploadRepoFileForm{}), repo.UploadFilePost) + Post(web.Bind(forms.UploadRepoFileForm{}), repo.UploadFilePost) m.Combo("/_diffpatch/*").Get(repo.NewDiffPatch). - Post(bindIgnErr(forms.EditRepoFileForm{}), repo.NewDiffPatchPost) + Post(web.Bind(forms.EditRepoFileForm{}), repo.NewDiffPatchPost) m.Combo("/_cherrypick/{sha:([a-f0-9]{7,40})}/*").Get(repo.CherryPick). - Post(bindIgnErr(forms.CherryPickForm{}), repo.CherryPickPost) + Post(web.Bind(forms.CherryPickForm{}), repo.CherryPickPost) }, repo.MustBeEditable) m.Group("", func() { m.Post("/upload-file", repo.UploadFileToServer) - m.Post("/upload-remove", bindIgnErr(forms.RemoveUploadFileForm{}), repo.RemoveUploadFileFromServer) + m.Post("/upload-remove", web.Bind(forms.RemoveUploadFileForm{}), repo.RemoveUploadFileFromServer) }, repo.MustBeEditable, repo.MustBeAbleToUpload) }, context.RepoRef(), canEnableEditor, context.RepoMustNotBeArchived(), repo.MustBeNotEmpty) @@ -1072,7 +1069,7 @@ func RegisterRoutes(m *web.Route) { m.Post("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.CreateBranch) m.Post("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.CreateBranch) m.Post("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.CreateBranch) - }, bindIgnErr(forms.NewBranchForm{})) + }, web.Bind(forms.NewBranchForm{})) m.Post("/delete", repo.DeleteBranchPost) m.Post("/restore", repo.RestoreBranchPost) }, context.RepoMustNotBeArchived(), reqRepoCodeWriter, repo.MustBeNotEmpty) @@ -1090,7 +1087,7 @@ func RegisterRoutes(m *web.Route) { m.Get("/releases/attachments/{uuid}", repo.GetAttachment, repo.MustBeNotEmpty, reqRepoReleaseReader) m.Group("/releases", func() { m.Get("/new", repo.NewRelease) - m.Post("/new", bindIgnErr(forms.NewReleaseForm{}), repo.NewReleasePost) + m.Post("/new", web.Bind(forms.NewReleaseForm{}), repo.NewReleasePost) m.Post("/delete", repo.DeleteRelease) m.Post("/attachments", repo.UploadReleaseAttachment) m.Post("/attachments/remove", repo.DeleteAttachment) @@ -1099,7 +1096,7 @@ func RegisterRoutes(m *web.Route) { repo.MustBeNotEmpty, context.RepoMustNotBeArchived(), reqRepoCodeWriter, context.RepoRef()) m.Group("/releases", func() { m.Get("/edit/*", repo.EditRelease) - m.Post("/edit/*", bindIgnErr(forms.EditReleaseForm{}), repo.EditReleasePost) + m.Post("/edit/*", web.Bind(forms.EditReleaseForm{}), repo.EditReleasePost) }, reqSignIn, repo.MustBeNotEmpty, context.RepoMustNotBeArchived(), reqRepoReleaseWriter, func(ctx *context.Context) { var err error ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch) @@ -1148,17 +1145,17 @@ func RegisterRoutes(m *web.Route) { m.Get("/{id}", repo.ViewProject) m.Group("", func() { m.Get("/new", repo.NewProject) - m.Post("/new", bindIgnErr(forms.CreateProjectForm{}), repo.NewProjectPost) + m.Post("/new", web.Bind(forms.CreateProjectForm{}), repo.NewProjectPost) m.Group("/{id}", func() { - m.Post("", bindIgnErr(forms.EditProjectBoardForm{}), repo.AddBoardToProjectPost) + m.Post("", web.Bind(forms.EditProjectBoardForm{}), repo.AddBoardToProjectPost) m.Post("/delete", repo.DeleteProject) m.Get("/edit", repo.EditProject) - m.Post("/edit", bindIgnErr(forms.CreateProjectForm{}), repo.EditProjectPost) + m.Post("/edit", web.Bind(forms.CreateProjectForm{}), repo.EditProjectPost) m.Post("/{action:open|close}", repo.ChangeProjectStatus) m.Group("/{boardID}", func() { - m.Put("", bindIgnErr(forms.EditProjectBoardForm{}), repo.EditProjectBoard) + m.Put("", web.Bind(forms.EditProjectBoardForm{}), repo.EditProjectBoard) m.Delete("", repo.DeleteProjectBoard) m.Post("/default", repo.SetDefaultProjectBoard) @@ -1174,14 +1171,14 @@ func RegisterRoutes(m *web.Route) { Post(context.RepoMustNotBeArchived(), reqSignIn, reqRepoWikiWriter, - bindIgnErr(forms.NewWikiForm{}), + web.Bind(forms.NewWikiForm{}), repo.WikiPost) m.Combo("/*"). Get(repo.Wiki). Post(context.RepoMustNotBeArchived(), reqSignIn, reqRepoWikiWriter, - bindIgnErr(forms.NewWikiForm{}), + web.Bind(forms.NewWikiForm{}), repo.WikiPost) m.Get("/commit/{sha:[a-f0-9]{7,40}}", repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.Diff) m.Get("/commit/{sha:[a-f0-9]{7,40}}.{ext:patch|diff}", repo.RawDiff) @@ -1239,17 +1236,17 @@ func RegisterRoutes(m *web.Route) { m.Get(".diff", repo.DownloadPullDiff) m.Get(".patch", repo.DownloadPullPatch) m.Get("/commits", context.RepoRef(), repo.ViewPullCommits) - m.Post("/merge", context.RepoMustNotBeArchived(), bindIgnErr(forms.MergePullRequestForm{}), repo.MergePullRequest) + m.Post("/merge", context.RepoMustNotBeArchived(), web.Bind(forms.MergePullRequestForm{}), repo.MergePullRequest) m.Post("/cancel_auto_merge", context.RepoMustNotBeArchived(), repo.CancelAutoMergePullRequest) m.Post("/update", repo.UpdatePullRequest) - m.Post("/set_allow_maintainer_edit", bindIgnErr(forms.UpdateAllowEditsForm{}), repo.SetAllowEdits) + m.Post("/set_allow_maintainer_edit", web.Bind(forms.UpdateAllowEditsForm{}), repo.SetAllowEdits) m.Post("/cleanup", context.RepoMustNotBeArchived(), context.RepoRef(), repo.CleanUpPullRequest) m.Group("/files", func() { m.Get("", context.RepoRef(), repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.SetWhitespaceBehavior, repo.ViewPullFiles) m.Group("/reviews", func() { m.Get("/new_comment", repo.RenderNewCodeCommentForm) - m.Post("/comments", bindIgnErr(forms.CodeCommentForm{}), repo.CreateCodeComment) - m.Post("/submit", bindIgnErr(forms.SubmitReviewForm{}), repo.SubmitReview) + m.Post("/comments", web.Bind(forms.CodeCommentForm{}), repo.CreateCodeComment) + m.Post("/submit", web.Bind(forms.SubmitReviewForm{}), repo.SubmitReview) }, context.RepoMustNotBeArchived()) }) }, repo.MustAllowPulls) diff --git a/services/agit/agit.go b/services/agit/agit.go index b4f4438791c..b61cb6f3f56 100644 --- a/services/agit/agit.go +++ b/services/agit/agit.go @@ -206,7 +206,7 @@ func ProcReceive(ctx context.Context, repo *repo_model.Repository, gitRepo *git. if err != nil { return nil, fmt.Errorf("Failed to load pull issue. Error: %w", err) } - comment, err := issues_model.CreatePushPullComment(ctx, pusher, pr, oldCommitID, opts.NewCommitIDs[i]) + comment, err := pull_service.CreatePushPullComment(ctx, pusher, pr, oldCommitID, opts.NewCommitIDs[i]) if err == nil && comment != nil { notification.NotifyPullRequestPushCommits(ctx, pusher, pr, comment) } diff --git a/services/attachment/attachment.go b/services/attachment/attachment.go index 522acd00a32..7fdacc6aae5 100644 --- a/services/attachment/attachment.go +++ b/services/attachment/attachment.go @@ -39,19 +39,14 @@ func NewAttachment(attach *repo_model.Attachment, file io.Reader) (*repo_model.A } // UploadAttachment upload new attachment into storage and update database -func UploadAttachment(file io.Reader, actorID, repoID, releaseID int64, fileName, allowedTypes string) (*repo_model.Attachment, error) { +func UploadAttachment(file io.Reader, allowedTypes string, opts *repo_model.Attachment) (*repo_model.Attachment, error) { buf := make([]byte, 1024) n, _ := util.ReadAtMost(file, buf) buf = buf[:n] - if err := upload.Verify(buf, fileName, allowedTypes); err != nil { + if err := upload.Verify(buf, opts.Name, allowedTypes); err != nil { return nil, err } - return NewAttachment(&repo_model.Attachment{ - RepoID: repoID, - UploaderID: actorID, - ReleaseID: releaseID, - Name: fileName, - }, io.MultiReader(bytes.NewReader(buf), file)) + return NewAttachment(opts, io.MultiReader(bytes.NewReader(buf), file)) } diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go index 7a325951200..28da1a78e06 100644 --- a/services/forms/repo_form.go +++ b/services/forms/repo_form.go @@ -136,6 +136,7 @@ type RepoSettingForm struct { RepoSizeLimit int64 // Advanced settings + EnableCode bool EnableWiki bool EnableExternalWiki bool ExternalWikiURL string diff --git a/services/comments/comments.go b/services/issue/comments.go similarity index 63% rename from services/comments/comments.go rename to services/issue/comments.go index 21640d90b4e..46c0daf70a3 100644 --- a/services/comments/comments.go +++ b/services/issue/comments.go @@ -1,10 +1,11 @@ // Copyright 2019 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package comments +package issue import ( "context" + "fmt" "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" @@ -14,9 +15,58 @@ import ( "code.gitea.io/gitea/modules/timeutil" ) +// CreateComment creates comment of issue or commit. +func CreateComment(opts *issues_model.CreateCommentOptions) (comment *issues_model.Comment, err error) { + ctx, committer, err := db.TxContext(db.DefaultContext) + if err != nil { + return nil, err + } + defer committer.Close() + + comment, err = issues_model.CreateComment(ctx, opts) + if err != nil { + return nil, err + } + + if err = committer.Commit(); err != nil { + return nil, err + } + + return comment, nil +} + +// CreateRefComment creates a commit reference comment to issue. +func CreateRefComment(doer *user_model.User, repo *repo_model.Repository, issue *issues_model.Issue, content, commitSHA string) error { + if len(commitSHA) == 0 { + return fmt.Errorf("cannot create reference with empty commit SHA") + } + + // Check if same reference from same commit has already existed. + has, err := db.GetEngine(db.DefaultContext).Get(&issues_model.Comment{ + Type: issues_model.CommentTypeCommitRef, + IssueID: issue.ID, + CommitSHA: commitSHA, + }) + if err != nil { + return fmt.Errorf("check reference comment: %w", err) + } else if has { + return nil + } + + _, err = CreateComment(&issues_model.CreateCommentOptions{ + Type: issues_model.CommentTypeCommitRef, + Doer: doer, + Repo: repo, + Issue: issue, + CommitSHA: commitSHA, + Content: content, + }) + return err +} + // CreateIssueComment creates a plain issue comment. func CreateIssueComment(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, issue *issues_model.Issue, content string, attachments []string) (*issues_model.Comment, error) { - comment, err := issues_model.CreateComment(&issues_model.CreateCommentOptions{ + comment, err := CreateComment(&issues_model.CreateCommentOptions{ Type: issues_model.CommentTypeComment, Doer: doer, Repo: repo, diff --git a/services/issue/commit.go b/services/issue/commit.go index efbcd639a59..db31fc66bb3 100644 --- a/services/issue/commit.go +++ b/services/issue/commit.go @@ -158,7 +158,7 @@ func UpdateIssuesCommit(doer *user_model.User, repo *repo_model.Repository, comm } message := fmt.Sprintf(`%s`, html.EscapeString(repo.Link()), html.EscapeString(url.PathEscape(c.Sha1)), html.EscapeString(strings.SplitN(c.Message, "\n", 2)[0])) - if err = issues_model.CreateRefComment(doer, refRepo, refIssue, message, c.Sha1); err != nil { + if err = CreateRefComment(doer, refRepo, refIssue, message, c.Sha1); err != nil { return err } diff --git a/services/issue/milestone.go b/services/issue/milestone.go index 6019a28a998..a9be8bd8878 100644 --- a/services/issue/milestone.go +++ b/services/issue/milestone.go @@ -54,7 +54,7 @@ func changeMilestoneAssign(ctx context.Context, doer *user_model.User, issue *is OldMilestoneID: oldMilestoneID, MilestoneID: issue.MilestoneID, } - if _, err := issues_model.CreateCommentCtx(ctx, opts); err != nil { + if _, err := issues_model.CreateComment(ctx, opts); err != nil { return err } } diff --git a/services/pull/check.go b/services/pull/check.go index 4f720f0a0cf..ed4b18107ca 100644 --- a/services/pull/check.go +++ b/services/pull/check.go @@ -236,7 +236,7 @@ func manuallyMerged(ctx context.Context, pr *issues_model.PullRequest) bool { return false } - if unit, err := pr.BaseRepo.GetUnit(unit.TypePullRequests); err == nil { + if unit, err := pr.BaseRepo.GetUnit(ctx, unit.TypePullRequests); err == nil { config := unit.PullRequestsConfig() if !config.AutodetectManualMerge { return false diff --git a/services/pull/comment.go b/services/pull/comment.go new file mode 100644 index 00000000000..068aca6cd12 --- /dev/null +++ b/services/pull/comment.go @@ -0,0 +1,162 @@ +// Copyright 2022 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package pull + +import ( + "context" + + issues_model "code.gitea.io/gitea/models/issues" + repo_model "code.gitea.io/gitea/models/repo" + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/json" + issue_service "code.gitea.io/gitea/services/issue" +) + +type commitBranchCheckItem struct { + Commit *git.Commit + Checked bool +} + +func commitBranchCheck(gitRepo *git.Repository, startCommit *git.Commit, endCommitID, baseBranch string, commitList map[string]*commitBranchCheckItem) error { + if startCommit.ID.String() == endCommitID { + return nil + } + + checkStack := make([]string, 0, 10) + checkStack = append(checkStack, startCommit.ID.String()) + + for len(checkStack) > 0 { + commitID := checkStack[0] + checkStack = checkStack[1:] + + item, ok := commitList[commitID] + if !ok { + continue + } + + if item.Commit.ID.String() == endCommitID { + continue + } + + if err := item.Commit.LoadBranchName(); err != nil { + return err + } + + if item.Commit.Branch == baseBranch { + continue + } + + if item.Checked { + continue + } + + item.Checked = true + + parentNum := item.Commit.ParentCount() + for i := 0; i < parentNum; i++ { + parentCommit, err := item.Commit.Parent(i) + if err != nil { + return err + } + checkStack = append(checkStack, parentCommit.ID.String()) + } + } + return nil +} + +// getCommitIDsFromRepo get commit IDs from repo in between oldCommitID and newCommitID +// isForcePush will be true if oldCommit isn't on the branch +// Commit on baseBranch will skip +func getCommitIDsFromRepo(ctx context.Context, repo *repo_model.Repository, oldCommitID, newCommitID, baseBranch string) (commitIDs []string, isForcePush bool, err error) { + repoPath := repo.RepoPath() + gitRepo, closer, err := git.RepositoryFromContextOrOpen(ctx, repoPath) + if err != nil { + return nil, false, err + } + defer closer.Close() + + oldCommit, err := gitRepo.GetCommit(oldCommitID) + if err != nil { + return nil, false, err + } + + if err = oldCommit.LoadBranchName(); err != nil { + return nil, false, err + } + + if len(oldCommit.Branch) == 0 { + commitIDs = make([]string, 2) + commitIDs[0] = oldCommitID + commitIDs[1] = newCommitID + + return commitIDs, true, err + } + + newCommit, err := gitRepo.GetCommit(newCommitID) + if err != nil { + return nil, false, err + } + + commits, err := newCommit.CommitsBeforeUntil(oldCommitID) + if err != nil { + return nil, false, err + } + + commitIDs = make([]string, 0, len(commits)) + commitChecks := make(map[string]*commitBranchCheckItem) + + for _, commit := range commits { + commitChecks[commit.ID.String()] = &commitBranchCheckItem{ + Commit: commit, + Checked: false, + } + } + + if err = commitBranchCheck(gitRepo, newCommit, oldCommitID, baseBranch, commitChecks); err != nil { + return + } + + for i := len(commits) - 1; i >= 0; i-- { + commitID := commits[i].ID.String() + if item, ok := commitChecks[commitID]; ok && item.Checked { + commitIDs = append(commitIDs, commitID) + } + } + + return commitIDs, isForcePush, err +} + +// CreatePushPullComment create push code to pull base comment +func CreatePushPullComment(ctx context.Context, pusher *user_model.User, pr *issues_model.PullRequest, oldCommitID, newCommitID string) (comment *issues_model.Comment, err error) { + if pr.HasMerged || oldCommitID == "" || newCommitID == "" { + return nil, nil + } + + ops := &issues_model.CreateCommentOptions{ + Type: issues_model.CommentTypePullRequestPush, + Doer: pusher, + Repo: pr.BaseRepo, + } + + var data issues_model.PushActionContent + + data.CommitIDs, data.IsForcePush, err = getCommitIDsFromRepo(ctx, pr.BaseRepo, oldCommitID, newCommitID, pr.BaseBranch) + if err != nil { + return nil, err + } + + ops.Issue = pr.Issue + + dataJSON, err := json.Marshal(data) + if err != nil { + return nil, err + } + + ops.Content = string(dataJSON) + + comment, err = issue_service.CreateComment(ops) + + return comment, err +} diff --git a/services/pull/merge.go b/services/pull/merge.go index 9270fb8cbc2..1c42c1c17b8 100644 --- a/services/pull/merge.go +++ b/services/pull/merge.go @@ -54,7 +54,7 @@ func GetDefaultMergeMessage(ctx context.Context, baseGitRepo *git.Repository, pr return "", err } - isExternalTracker := pr.BaseRepo.UnitEnabled(unit.TypeExternalTracker) + isExternalTracker := pr.BaseRepo.UnitEnabled(ctx, unit.TypeExternalTracker) issueReference := "#" if isExternalTracker { issueReference = "!" @@ -145,11 +145,11 @@ func Merge(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.U defer pullWorkingPool.CheckOut(fmt.Sprint(pr.ID)) // Removing an auto merge pull and ignore if not exist - if err := pull_model.DeleteScheduledAutoMerge(db.DefaultContext, pr.ID); err != nil && !db.IsErrNotExist(err) { + if err := pull_model.DeleteScheduledAutoMerge(ctx, pr.ID); err != nil && !db.IsErrNotExist(err) { return err } - prUnit, err := pr.BaseRepo.GetUnit(unit.TypePullRequests) + prUnit, err := pr.BaseRepo.GetUnit(ctx, unit.TypePullRequests) if err != nil { log.Error("pr.BaseRepo.GetUnit(unit.TypePullRequests): %v", err) return err @@ -828,7 +828,7 @@ func MergedManually(pr *issues_model.PullRequest, doer *user_model.User, baseGit defer pullWorkingPool.CheckOut(fmt.Sprint(pr.ID)) if err := db.WithTx(db.DefaultContext, func(ctx context.Context) error { - prUnit, err := pr.BaseRepo.GetUnitCtx(ctx, unit.TypePullRequests) + prUnit, err := pr.BaseRepo.GetUnit(ctx, unit.TypePullRequests) if err != nil { return err } diff --git a/services/pull/patch.go b/services/pull/patch.go index 2d495a3d72d..809b75e6b45 100644 --- a/services/pull/patch.go +++ b/services/pull/patch.go @@ -354,7 +354,7 @@ func checkConflicts(ctx context.Context, pr *issues_model.PullRequest, gitRepo * } // 5. Now get the pull request configuration to check if we need to ignore whitespace - prUnit, err := pr.BaseRepo.GetUnit(unit.TypePullRequests) + prUnit, err := pr.BaseRepo.GetUnit(ctx, unit.TypePullRequests) if err != nil { return false, err } diff --git a/services/pull/pull.go b/services/pull/pull.go index fd907491d44..afb0fa24424 100644 --- a/services/pull/pull.go +++ b/services/pull/pull.go @@ -123,7 +123,7 @@ func NewPullRequest(ctx context.Context, repo *repo_model.Repository, pull *issu Content: string(dataJSON), } - _, _ = issues_model.CreateComment(ops) + _, _ = issue_service.CreateComment(ops) } return nil @@ -222,7 +222,7 @@ func ChangeTargetBranch(ctx context.Context, pr *issues_model.PullRequest, doer OldRef: oldBranch, NewRef: targetBranch, } - if _, err = issues_model.CreateComment(options); err != nil { + if _, err = issue_service.CreateComment(options); err != nil { return fmt.Errorf("CreateChangeTargetBranchComment: %w", err) } @@ -317,7 +317,7 @@ func AddTestPullRequestTask(doer *user_model.User, repoID int64, branch string, } AddToTaskQueue(pr) - comment, err := issues_model.CreatePushPullComment(ctx, doer, pr, oldCommitID, newCommitID) + comment, err := CreatePushPullComment(ctx, doer, pr, oldCommitID, newCommitID) if err == nil && comment != nil { notification.NotifyPullRequestPushCommits(ctx, doer, pr, comment) } diff --git a/services/pull/review.go b/services/pull/review.go index 1f2bc77e2cd..67a10d7aad6 100644 --- a/services/pull/review.go +++ b/services/pull/review.go @@ -20,6 +20,7 @@ import ( "code.gitea.io/gitea/modules/notification" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" + issue_service "code.gitea.io/gitea/services/issue" ) // CreateCodeComment creates a comment on the code line @@ -202,7 +203,7 @@ func createCodeComment(ctx context.Context, doer *user_model.User, repo *repo_mo return nil, err } } - return issues_model.CreateComment(&issues_model.CreateCommentOptions{ + return issue_service.CreateComment(&issues_model.CreateCommentOptions{ Type: issues_model.CommentTypeCode, Doer: doer, Repo: repo, @@ -322,7 +323,7 @@ func DismissReview(ctx context.Context, reviewID, repoID int64, message string, return } - comment, err = issues_model.CreateComment(&issues_model.CreateCommentOptions{ + comment, err = issue_service.CreateComment(&issues_model.CreateCommentOptions{ Doer: doer, Content: message, Type: issues_model.CommentTypeDismissReview, diff --git a/services/pull/update.go b/services/pull/update.go index bd6a37b277e..e09dbf62445 100644 --- a/services/pull/update.go +++ b/services/pull/update.go @@ -106,7 +106,7 @@ func IsUserAllowedToUpdate(ctx context.Context, pull *issues_model.PullRequest, // can't do rebase on protected branch because need force push if pr.ProtectedBranch == nil { - prUnit, err := pr.BaseRepo.GetUnit(unit.TypePullRequests) + prUnit, err := pr.BaseRepo.GetUnit(ctx, unit.TypePullRequests) if err != nil { log.Error("pr.BaseRepo.GetUnit(unit.TypePullRequests): %v", err) return false, false, err diff --git a/services/release/release.go b/services/release/release.go index 1d599fcda1d..13042cd3ac2 100644 --- a/services/release/release.go +++ b/services/release/release.go @@ -21,6 +21,7 @@ import ( "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/modules/util" ) func createTag(ctx context.Context, gitRepo *git.Repository, rel *repo_model.Release, msg string) (bool, error) { @@ -218,7 +219,10 @@ func UpdateRelease(doer *user_model.User, gitRepo *git.Repository, rel *repo_mod } for _, attach := range attachments { if attach.ReleaseID != rel.ID { - return errors.New("delete attachement of release permission denied") + return util.SilentWrap{ + Message: "delete attachment of release permission denied", + Err: util.ErrPermissionDenied, + } } deletedUUIDs.Add(attach.UUID) } @@ -240,7 +244,10 @@ func UpdateRelease(doer *user_model.User, gitRepo *git.Repository, rel *repo_mod } for _, attach := range attachments { if attach.ReleaseID != rel.ID { - return errors.New("update attachement of release permission denied") + return util.SilentWrap{ + Message: "update attachment of release permission denied", + Err: util.ErrPermissionDenied, + } } } diff --git a/services/repository/repository.go b/services/repository/repository.go index 41fae4b4cf6..3c3e7e82c3f 100644 --- a/services/repository/repository.go +++ b/services/repository/repository.go @@ -58,7 +58,7 @@ func DeleteRepository(ctx context.Context, doer *user_model.User, repo *repo_mod func PushCreateRepo(authUser, owner *user_model.User, repoName string) (*repo_model.Repository, error) { if !authUser.IsAdmin { if owner.IsOrganization() { - if ok, err := organization.CanCreateOrgRepo(owner.ID, authUser.ID); err != nil { + if ok, err := organization.CanCreateOrgRepo(db.DefaultContext, owner.ID, authUser.ID); err != nil { return nil, err } else if !ok { return nil, fmt.Errorf("cannot push-create repository for org") diff --git a/services/repository/transfer.go b/services/repository/transfer.go index 9fba9c44ebc..f4afb7e2dec 100644 --- a/services/repository/transfer.go +++ b/services/repository/transfer.go @@ -4,6 +4,7 @@ package repository import ( + "context" "fmt" "code.gitea.io/gitea/models" @@ -24,8 +25,8 @@ import ( var repoWorkingPool = sync.NewExclusivePool() // TransferOwnership transfers all corresponding setting from old user to new one. -func TransferOwnership(doer, newOwner *user_model.User, repo *repo_model.Repository, teams []*organization.Team) error { - if err := repo.GetOwner(db.DefaultContext); err != nil { +func TransferOwnership(ctx context.Context, doer, newOwner *user_model.User, repo *repo_model.Repository, teams []*organization.Team) error { + if err := repo.GetOwner(ctx); err != nil { return err } for _, team := range teams { @@ -43,18 +44,18 @@ func TransferOwnership(doer, newOwner *user_model.User, repo *repo_model.Reposit } repoWorkingPool.CheckOut(fmt.Sprint(repo.ID)) - newRepo, err := repo_model.GetRepositoryByID(db.DefaultContext, repo.ID) + newRepo, err := repo_model.GetRepositoryByID(ctx, repo.ID) if err != nil { return err } for _, team := range teams { - if err := models.AddRepository(db.DefaultContext, team, newRepo); err != nil { + if err := models.AddRepository(ctx, team, newRepo); err != nil { return err } } - notification.NotifyTransferRepository(db.DefaultContext, doer, repo, oldOwner.Name) + notification.NotifyTransferRepository(ctx, doer, repo, oldOwner.Name) return nil } @@ -84,49 +85,49 @@ func ChangeRepositoryName(doer *user_model.User, repo *repo_model.Repository, ne // StartRepositoryTransfer transfer a repo from one owner to a new one. // it make repository into pending transfer state, if doer can not create repo for new owner. -func StartRepositoryTransfer(doer, newOwner *user_model.User, repo *repo_model.Repository, teams []*organization.Team) error { +func StartRepositoryTransfer(ctx context.Context, doer, newOwner *user_model.User, repo *repo_model.Repository, teams []*organization.Team) error { if err := models.TestRepositoryReadyForTransfer(repo.Status); err != nil { return err } // Admin is always allowed to transfer || user transfer repo back to his account if doer.IsAdmin || doer.ID == newOwner.ID { - return TransferOwnership(doer, newOwner, repo, teams) + return TransferOwnership(ctx, doer, newOwner, repo, teams) } // If new owner is an org and user can create repos he can transfer directly too if newOwner.IsOrganization() { - allowed, err := organization.CanCreateOrgRepo(newOwner.ID, doer.ID) + allowed, err := organization.CanCreateOrgRepo(ctx, newOwner.ID, doer.ID) if err != nil { return err } if allowed { - return TransferOwnership(doer, newOwner, repo, teams) + return TransferOwnership(ctx, doer, newOwner, repo, teams) } } // In case the new owner would not have sufficient access to the repo, give access rights for read - hasAccess, err := access_model.HasAccess(db.DefaultContext, newOwner.ID, repo) + hasAccess, err := access_model.HasAccess(ctx, newOwner.ID, repo) if err != nil { return err } if !hasAccess { - if err := repo_module.AddCollaborator(repo, newOwner); err != nil { + if err := repo_module.AddCollaborator(ctx, repo, newOwner); err != nil { return err } - if err := repo_model.ChangeCollaborationAccessMode(repo, newOwner.ID, perm.AccessModeRead); err != nil { + if err := repo_model.ChangeCollaborationAccessMode(ctx, repo, newOwner.ID, perm.AccessModeRead); err != nil { return err } } // Make repo as pending for transfer repo.Status = repo_model.RepositoryPendingTransfer - if err := models.CreatePendingRepositoryTransfer(doer, newOwner, repo.ID, teams); err != nil { + if err := models.CreatePendingRepositoryTransfer(ctx, doer, newOwner, repo.ID, teams); err != nil { return err } // notify users who are able to accept / reject transfer - notification.NotifyRepoPendingTransfer(db.DefaultContext, doer, newOwner, repo) + notification.NotifyRepoPendingTransfer(ctx, doer, newOwner, repo) return nil } diff --git a/services/repository/transfer_test.go b/services/repository/transfer_test.go index 2bbc5abbd25..1299e66be27 100644 --- a/services/repository/transfer_test.go +++ b/services/repository/transfer_test.go @@ -37,7 +37,7 @@ func TestTransferOwnership(t *testing.T) { doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}) repo.Owner = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) - assert.NoError(t, TransferOwnership(doer, doer, repo, nil)) + assert.NoError(t, TransferOwnership(db.DefaultContext, doer, doer, repo, nil)) transferredRepo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}) assert.EqualValues(t, 2, transferredRepo.OwnerID) @@ -70,7 +70,7 @@ func TestStartRepositoryTransferSetPermission(t *testing.T) { assert.NoError(t, err) assert.False(t, hasAccess) - assert.NoError(t, StartRepositoryTransfer(doer, recipient, repo, nil)) + assert.NoError(t, StartRepositoryTransfer(db.DefaultContext, doer, recipient, repo, nil)) hasAccess, err = access_model.HasAccess(db.DefaultContext, recipient.ID, repo) assert.NoError(t, err) diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index 437bda87aae..25494bde239 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -1,5 +1,5 @@ name: gitea -summary: Gitea - A painless self-hosted Git service +summary: Gitea - A painless self-hosted Git service description: | The goal of this project is to make the easiest, fastest, and most painless way of setting up a self-hosted Git service. With Go, this can be done with @@ -39,7 +39,6 @@ apps: command: usr/bin/sqlite3 parts: - gitea: plugin: make source: . diff --git a/templates/admin/base/search.tmpl b/templates/admin/base/search.tmpl index 28bc478f6a2..ae1a4d2ac56 100644 --- a/templates/admin/base/search.tmpl +++ b/templates/admin/base/search.tmpl @@ -6,12 +6,12 @@ {{svg "octicon-triangle-down" 14 "dropdown icon"}} diff --git a/templates/admin/emails/list.tmpl b/templates/admin/emails/list.tmpl index adf5b9bef7b..f5f6a86dc8a 100644 --- a/templates/admin/emails/list.tmpl +++ b/templates/admin/emails/list.tmpl @@ -15,10 +15,10 @@ {{svg "octicon-triangle-down" 14 "dropdown icon"}} diff --git a/templates/admin/navbar.tmpl b/templates/admin/navbar.tmpl index 1c8b12fc2fa..cfd32b64840 100644 --- a/templates/admin/navbar.tmpl +++ b/templates/admin/navbar.tmpl @@ -1,45 +1,45 @@ diff --git a/templates/admin/user/list.tmpl b/templates/admin/user/list.tmpl index 56f6eaa3ad7..67a726eb3f6 100644 --- a/templates/admin/user/list.tmpl +++ b/templates/admin/user/list.tmpl @@ -76,9 +76,9 @@