mirror of
https://github.com/go-gitea/gitea.git
synced 2026-06-12 11:32:32 +02:00
Merge remote-tracking branch 'upstream/main' into limit-repo-size
This commit is contained in:
commit
d1368ecfb6
@ -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,
|
||||
|
||||
@ -2147,7 +2147,7 @@ ROUTER = console
|
||||
;[cron.update_checker]
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;ENABLED = false
|
||||
;ENABLED = true
|
||||
;RUN_AT_START = false
|
||||
;ENABLE_SUCCESS_NOTICE = false
|
||||
;SCHEDULE = @every 168h
|
||||
|
||||
@ -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`.
|
||||
|
||||
@ -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`_.
|
||||
|
||||
|
||||
6
go.mod
6
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
|
||||
|
||||
12
go.sum
12
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=
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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}
|
||||
|
||||
@ -544,3 +544,9 @@
|
||||
repo_id: 51
|
||||
type: 2
|
||||
created_unix: 946684810
|
||||
|
||||
-
|
||||
id: 80
|
||||
repo_id: 53
|
||||
type: 1
|
||||
created_unix: 946684810
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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).
|
||||
|
||||
@ -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())
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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})
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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",
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
30
modules/convert/attachment.go
Normal file
30
modules/convert/attachment.go
Normal file
@ -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
|
||||
}
|
||||
@ -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{
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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),
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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 "<html><body>"
|
||||
_, _ = res.WriteString("<html><body>")
|
||||
|
||||
// Strip out nuls - they're always invalid
|
||||
_, _ = res.Write(tagCleaner.ReplaceAll([]byte(nulCleaner.Replace(string(rawHTML))), []byte("<$1")))
|
||||
|
||||
// close the tags
|
||||
_, _ = res.WriteString("</body></html>")
|
||||
|
||||
// parse the HTML
|
||||
node, err := html.Parse(res)
|
||||
node, err := html.Parse(io.MultiReader(
|
||||
// prepend "<html><body>"
|
||||
strings.NewReader("<html><body>"),
|
||||
// Strip out nuls - they're always invalid
|
||||
bytes.NewReader(tagCleaner.ReplaceAll([]byte(nulCleaner.Replace(string(rawHTML))), []byte("<$1"))),
|
||||
// close the tags
|
||||
strings.NewReader("</body></html>"),
|
||||
))
|
||||
if err != nil {
|
||||
return &postProcessError{"invalid HTML", err}
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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"`
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
10
options/license/Symlinks
Normal file
10
options/license/Symlinks
Normal file
@ -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.
|
||||
@ -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() {
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
372
routers/api/v1/repo/issue_attachment.go
Normal file
372
routers/api/v1/repo/issue_attachment.go
Normal file
@ -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
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
|
||||
383
routers/api/v1/repo/issue_comment_attachment.go
Normal file
383
routers/api/v1/repo/issue_comment_attachment.go
Normal file
@ -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
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
})
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -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"))
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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"))
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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))
|
||||
}
|
||||
|
||||
@ -136,6 +136,7 @@ type RepoSettingForm struct {
|
||||
RepoSizeLimit int64
|
||||
|
||||
// Advanced settings
|
||||
EnableCode bool
|
||||
EnableWiki bool
|
||||
EnableExternalWiki bool
|
||||
ExternalWikiURL string
|
||||
|
||||
@ -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,
|
||||
@ -158,7 +158,7 @@ func UpdateIssuesCommit(doer *user_model.User, repo *repo_model.Repository, comm
|
||||
}
|
||||
|
||||
message := fmt.Sprintf(`<a href="%s/commit/%s">%s</a>`, 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
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
162
services/pull/comment.go
Normal file
162
services/pull/comment.go
Normal file
@ -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
|
||||
}
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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: .
|
||||
|
||||
@ -6,12 +6,12 @@
|
||||
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
|
||||
</span>
|
||||
<div class="menu">
|
||||
<a class="{{if or (eq .SortType "oldest") (not .SortType)}}active{{end}} item" href="{{$.Link}}?sort=oldest&q={{$.Keyword}}">{{.locale.Tr "repo.issues.filter_sort.oldest"}}</a>
|
||||
<a class="{{if eq .SortType "newest"}}active{{end}} item" href="{{$.Link}}?sort=newest&q={{$.Keyword}}">{{.locale.Tr "repo.issues.filter_sort.latest"}}</a>
|
||||
<a class="{{if eq .SortType "alphabetically"}}active{{end}} item" href="{{$.Link}}?sort=alphabetically&q={{$.Keyword}}">{{.locale.Tr "repo.issues.label.filter_sort.alphabetically"}}</a>
|
||||
<a class="{{if eq .SortType "reversealphabetically"}}active{{end}} item" href="{{$.Link}}?sort=reversealphabetically&q={{$.Keyword}}">{{.locale.Tr "repo.issues.label.filter_sort.reverse_alphabetically"}}</a>
|
||||
<a class="{{if eq .SortType "recentupdate"}}active{{end}} item" href="{{$.Link}}?sort=recentupdate&q={{$.Keyword}}">{{.locale.Tr "repo.issues.filter_sort.recentupdate"}}</a>
|
||||
<a class="{{if eq .SortType "leastupdate"}}active{{end}} item" href="{{$.Link}}?sort=leastupdate&q={{$.Keyword}}">{{.locale.Tr "repo.issues.filter_sort.leastupdate"}}</a>
|
||||
<a class="{{if or (eq .SortType "oldest") (not .SortType)}}active {{end}}item" href="{{$.Link}}?sort=oldest&q={{$.Keyword}}">{{.locale.Tr "repo.issues.filter_sort.oldest"}}</a>
|
||||
<a class="{{if eq .SortType "newest"}}active {{end}}item" href="{{$.Link}}?sort=newest&q={{$.Keyword}}">{{.locale.Tr "repo.issues.filter_sort.latest"}}</a>
|
||||
<a class="{{if eq .SortType "alphabetically"}}active {{end}}item" href="{{$.Link}}?sort=alphabetically&q={{$.Keyword}}">{{.locale.Tr "repo.issues.label.filter_sort.alphabetically"}}</a>
|
||||
<a class="{{if eq .SortType "reversealphabetically"}}active {{end}}item" href="{{$.Link}}?sort=reversealphabetically&q={{$.Keyword}}">{{.locale.Tr "repo.issues.label.filter_sort.reverse_alphabetically"}}</a>
|
||||
<a class="{{if eq .SortType "recentupdate"}}active {{end}}item" href="{{$.Link}}?sort=recentupdate&q={{$.Keyword}}">{{.locale.Tr "repo.issues.filter_sort.recentupdate"}}</a>
|
||||
<a class="{{if eq .SortType "leastupdate"}}active {{end}}item" href="{{$.Link}}?sort=leastupdate&q={{$.Keyword}}">{{.locale.Tr "repo.issues.filter_sort.leastupdate"}}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user