Improve performance of dashboard (#4977)
This commit is contained in:
parent
49ea6e0deb
commit
b3b7598ec6
19 changed files with 350 additions and 94 deletions
|
@ -194,6 +194,10 @@ func (a *Action) GetRepoLink() string {
|
||||||
|
|
||||||
// GetCommentLink returns link to action comment.
|
// GetCommentLink returns link to action comment.
|
||||||
func (a *Action) GetCommentLink() string {
|
func (a *Action) GetCommentLink() string {
|
||||||
|
return a.getCommentLink(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Action) getCommentLink(e Engine) string {
|
||||||
if a == nil {
|
if a == nil {
|
||||||
return "#"
|
return "#"
|
||||||
}
|
}
|
||||||
|
@ -213,11 +217,15 @@ func (a *Action) GetCommentLink() string {
|
||||||
return "#"
|
return "#"
|
||||||
}
|
}
|
||||||
|
|
||||||
issue, err := GetIssueByID(issueID)
|
issue, err := getIssueByID(e, issueID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "#"
|
return "#"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err = issue.loadRepo(e); err != nil {
|
||||||
|
return "#"
|
||||||
|
}
|
||||||
|
|
||||||
return issue.HTMLURL()
|
return issue.HTMLURL()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -331,12 +339,14 @@ type PushCommits struct {
|
||||||
CompareURL string
|
CompareURL string
|
||||||
|
|
||||||
avatars map[string]string
|
avatars map[string]string
|
||||||
|
emailUsers map[string]*User
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewPushCommits creates a new PushCommits object.
|
// NewPushCommits creates a new PushCommits object.
|
||||||
func NewPushCommits() *PushCommits {
|
func NewPushCommits() *PushCommits {
|
||||||
return &PushCommits{
|
return &PushCommits{
|
||||||
avatars: make(map[string]string),
|
avatars: make(map[string]string),
|
||||||
|
emailUsers: make(map[string]*User),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -344,17 +354,35 @@ func NewPushCommits() *PushCommits {
|
||||||
// api.PayloadCommit format.
|
// api.PayloadCommit format.
|
||||||
func (pc *PushCommits) ToAPIPayloadCommits(repoLink string) []*api.PayloadCommit {
|
func (pc *PushCommits) ToAPIPayloadCommits(repoLink string) []*api.PayloadCommit {
|
||||||
commits := make([]*api.PayloadCommit, len(pc.Commits))
|
commits := make([]*api.PayloadCommit, len(pc.Commits))
|
||||||
|
|
||||||
|
if pc.emailUsers == nil {
|
||||||
|
pc.emailUsers = make(map[string]*User)
|
||||||
|
}
|
||||||
|
var err error
|
||||||
for i, commit := range pc.Commits {
|
for i, commit := range pc.Commits {
|
||||||
authorUsername := ""
|
authorUsername := ""
|
||||||
author, err := GetUserByEmail(commit.AuthorEmail)
|
author, ok := pc.emailUsers[commit.AuthorEmail]
|
||||||
|
if !ok {
|
||||||
|
author, err = GetUserByEmail(commit.AuthorEmail)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
authorUsername = author.Name
|
authorUsername = author.Name
|
||||||
|
pc.emailUsers[commit.AuthorEmail] = author
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
authorUsername = author.Name
|
||||||
|
}
|
||||||
|
|
||||||
committerUsername := ""
|
committerUsername := ""
|
||||||
committer, err := GetUserByEmail(commit.CommitterEmail)
|
committer, ok := pc.emailUsers[commit.CommitterEmail]
|
||||||
|
if !ok {
|
||||||
|
committer, err = GetUserByEmail(commit.CommitterEmail)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// TODO: check errors other than email not found.
|
// TODO: check errors other than email not found.
|
||||||
committerUsername = committer.Name
|
committerUsername = committer.Name
|
||||||
|
pc.emailUsers[commit.CommitterEmail] = committer
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
committerUsername = committer.Name
|
||||||
}
|
}
|
||||||
commits[i] = &api.PayloadCommit{
|
commits[i] = &api.PayloadCommit{
|
||||||
ID: commit.Sha1,
|
ID: commit.Sha1,
|
||||||
|
@ -379,18 +407,28 @@ func (pc *PushCommits) ToAPIPayloadCommits(repoLink string) []*api.PayloadCommit
|
||||||
// AvatarLink tries to match user in database with e-mail
|
// AvatarLink tries to match user in database with e-mail
|
||||||
// in order to show custom avatar, and falls back to general avatar link.
|
// in order to show custom avatar, and falls back to general avatar link.
|
||||||
func (pc *PushCommits) AvatarLink(email string) string {
|
func (pc *PushCommits) AvatarLink(email string) string {
|
||||||
_, ok := pc.avatars[email]
|
avatar, ok := pc.avatars[email]
|
||||||
|
if ok {
|
||||||
|
return avatar
|
||||||
|
}
|
||||||
|
|
||||||
|
u, ok := pc.emailUsers[email]
|
||||||
if !ok {
|
if !ok {
|
||||||
u, err := GetUserByEmail(email)
|
var err error
|
||||||
|
u, err = GetUserByEmail(email)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
pc.avatars[email] = base.AvatarLink(email)
|
pc.avatars[email] = base.AvatarLink(email)
|
||||||
if !IsErrUserNotExist(err) {
|
if !IsErrUserNotExist(err) {
|
||||||
log.Error(4, "GetUserByEmail: %v", err)
|
log.Error(4, "GetUserByEmail: %v", err)
|
||||||
|
return ""
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
pc.avatars[email] = u.RelAvatarLink()
|
pc.emailUsers[email] = u
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if u != nil {
|
||||||
|
pc.avatars[email] = u.RelAvatarLink()
|
||||||
|
}
|
||||||
|
|
||||||
return pc.avatars[email]
|
return pc.avatars[email]
|
||||||
}
|
}
|
||||||
|
@ -479,7 +517,8 @@ func UpdateIssuesCommit(doer *User, repo *Repository, commits []*PushCommit) err
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = issue.ChangeStatus(doer, repo, true); err != nil {
|
issue.Repo = repo
|
||||||
|
if err = issue.ChangeStatus(doer, true); err != nil {
|
||||||
// Don't return an error when dependencies are open as this would let the push fail
|
// Don't return an error when dependencies are open as this would let the push fail
|
||||||
if IsErrDependenciesLeft(err) {
|
if IsErrDependenciesLeft(err) {
|
||||||
return nil
|
return nil
|
||||||
|
@ -504,7 +543,8 @@ func UpdateIssuesCommit(doer *User, repo *Repository, commits []*PushCommit) err
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = issue.ChangeStatus(doer, repo, false); err != nil {
|
issue.Repo = repo
|
||||||
|
if err = issue.ChangeStatus(doer, false); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -86,6 +86,11 @@ func (issue *Issue) IsOverdue() bool {
|
||||||
return util.TimeStampNow() >= issue.DeadlineUnix
|
return util.TimeStampNow() >= issue.DeadlineUnix
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoadRepo loads issue's repository
|
||||||
|
func (issue *Issue) LoadRepo() error {
|
||||||
|
return issue.loadRepo(x)
|
||||||
|
}
|
||||||
|
|
||||||
func (issue *Issue) loadRepo(e Engine) (err error) {
|
func (issue *Issue) loadRepo(e Engine) (err error) {
|
||||||
if issue.Repo == nil {
|
if issue.Repo == nil {
|
||||||
issue.Repo, err = getRepositoryByID(e, issue.RepoID)
|
issue.Repo, err = getRepositoryByID(e, issue.RepoID)
|
||||||
|
@ -129,6 +134,11 @@ func (issue *Issue) loadLabels(e Engine) (err error) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoadPoster loads poster
|
||||||
|
func (issue *Issue) LoadPoster() error {
|
||||||
|
return issue.loadPoster(x)
|
||||||
|
}
|
||||||
|
|
||||||
func (issue *Issue) loadPoster(e Engine) (err error) {
|
func (issue *Issue) loadPoster(e Engine) (err error) {
|
||||||
if issue.Poster == nil {
|
if issue.Poster == nil {
|
||||||
issue.Poster, err = getUserByID(e, issue.PosterID)
|
issue.Poster, err = getUserByID(e, issue.PosterID)
|
||||||
|
@ -154,10 +164,16 @@ func (issue *Issue) loadPullRequest(e Engine) (err error) {
|
||||||
}
|
}
|
||||||
return fmt.Errorf("getPullRequestByIssueID [%d]: %v", issue.ID, err)
|
return fmt.Errorf("getPullRequestByIssueID [%d]: %v", issue.ID, err)
|
||||||
}
|
}
|
||||||
|
issue.PullRequest.Issue = issue
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoadPullRequest loads pull request info
|
||||||
|
func (issue *Issue) LoadPullRequest() error {
|
||||||
|
return issue.loadPullRequest(x)
|
||||||
|
}
|
||||||
|
|
||||||
func (issue *Issue) loadComments(e Engine) (err error) {
|
func (issue *Issue) loadComments(e Engine) (err error) {
|
||||||
if issue.Comments != nil {
|
if issue.Comments != nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -310,11 +326,18 @@ func (issue *Issue) State() api.StateType {
|
||||||
// Required - Poster, Labels,
|
// Required - Poster, Labels,
|
||||||
// Optional - Milestone, Assignee, PullRequest
|
// Optional - Milestone, Assignee, PullRequest
|
||||||
func (issue *Issue) APIFormat() *api.Issue {
|
func (issue *Issue) APIFormat() *api.Issue {
|
||||||
|
return issue.apiFormat(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (issue *Issue) apiFormat(e Engine) *api.Issue {
|
||||||
|
issue.loadLabels(e)
|
||||||
apiLabels := make([]*api.Label, len(issue.Labels))
|
apiLabels := make([]*api.Label, len(issue.Labels))
|
||||||
for i := range issue.Labels {
|
for i := range issue.Labels {
|
||||||
apiLabels[i] = issue.Labels[i].APIFormat()
|
apiLabels[i] = issue.Labels[i].APIFormat()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
issue.loadPoster(e)
|
||||||
|
issue.loadRepo(e)
|
||||||
apiIssue := &api.Issue{
|
apiIssue := &api.Issue{
|
||||||
ID: issue.ID,
|
ID: issue.ID,
|
||||||
URL: issue.APIURL(),
|
URL: issue.APIURL(),
|
||||||
|
@ -336,6 +359,8 @@ func (issue *Issue) APIFormat() *api.Issue {
|
||||||
if issue.Milestone != nil {
|
if issue.Milestone != nil {
|
||||||
apiIssue.Milestone = issue.Milestone.APIFormat()
|
apiIssue.Milestone = issue.Milestone.APIFormat()
|
||||||
}
|
}
|
||||||
|
issue.loadAssignees(e)
|
||||||
|
|
||||||
if len(issue.Assignees) > 0 {
|
if len(issue.Assignees) > 0 {
|
||||||
for _, assignee := range issue.Assignees {
|
for _, assignee := range issue.Assignees {
|
||||||
apiIssue.Assignees = append(apiIssue.Assignees, assignee.APIFormat())
|
apiIssue.Assignees = append(apiIssue.Assignees, assignee.APIFormat())
|
||||||
|
@ -343,6 +368,7 @@ func (issue *Issue) APIFormat() *api.Issue {
|
||||||
apiIssue.Assignee = issue.Assignees[0].APIFormat() // For compatibility, we're keeping the first assignee as `apiIssue.Assignee`
|
apiIssue.Assignee = issue.Assignees[0].APIFormat() // For compatibility, we're keeping the first assignee as `apiIssue.Assignee`
|
||||||
}
|
}
|
||||||
if issue.IsPull {
|
if issue.IsPull {
|
||||||
|
issue.loadPullRequest(e)
|
||||||
apiIssue.PullRequest = &api.PullRequestMeta{
|
apiIssue.PullRequest = &api.PullRequestMeta{
|
||||||
HasMerged: issue.PullRequest.HasMerged,
|
HasMerged: issue.PullRequest.HasMerged,
|
||||||
}
|
}
|
||||||
|
@ -656,7 +682,7 @@ func UpdateIssueCols(issue *Issue, cols ...string) error {
|
||||||
return updateIssueCols(x, issue, cols...)
|
return updateIssueCols(x, issue, cols...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (issue *Issue) changeStatus(e *xorm.Session, doer *User, repo *Repository, isClosed bool) (err error) {
|
func (issue *Issue) changeStatus(e *xorm.Session, doer *User, isClosed bool) (err error) {
|
||||||
// Nothing should be performed if current status is same as target status
|
// Nothing should be performed if current status is same as target status
|
||||||
if issue.IsClosed == isClosed {
|
if issue.IsClosed == isClosed {
|
||||||
return nil
|
return nil
|
||||||
|
@ -707,7 +733,7 @@ func (issue *Issue) changeStatus(e *xorm.Session, doer *User, repo *Repository,
|
||||||
}
|
}
|
||||||
|
|
||||||
// New action comment
|
// New action comment
|
||||||
if _, err = createStatusComment(e, doer, repo, issue); err != nil {
|
if _, err = createStatusComment(e, doer, issue); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -715,14 +741,21 @@ func (issue *Issue) changeStatus(e *xorm.Session, doer *User, repo *Repository,
|
||||||
}
|
}
|
||||||
|
|
||||||
// ChangeStatus changes issue status to open or closed.
|
// ChangeStatus changes issue status to open or closed.
|
||||||
func (issue *Issue) ChangeStatus(doer *User, repo *Repository, isClosed bool) (err error) {
|
func (issue *Issue) ChangeStatus(doer *User, isClosed bool) (err error) {
|
||||||
sess := x.NewSession()
|
sess := x.NewSession()
|
||||||
defer sess.Close()
|
defer sess.Close()
|
||||||
if err = sess.Begin(); err != nil {
|
if err = sess.Begin(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = issue.changeStatus(sess, doer, repo, isClosed); err != nil {
|
if err = issue.loadRepo(sess); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = issue.loadPoster(sess); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = issue.changeStatus(sess, doer, isClosed); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -733,12 +766,14 @@ func (issue *Issue) ChangeStatus(doer *User, repo *Repository, isClosed bool) (e
|
||||||
|
|
||||||
mode, _ := AccessLevel(issue.Poster, issue.Repo)
|
mode, _ := AccessLevel(issue.Poster, issue.Repo)
|
||||||
if issue.IsPull {
|
if issue.IsPull {
|
||||||
|
if err = issue.loadPullRequest(sess); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
// Merge pull request calls issue.changeStatus so we need to handle separately.
|
// Merge pull request calls issue.changeStatus so we need to handle separately.
|
||||||
issue.PullRequest.Issue = issue
|
|
||||||
apiPullRequest := &api.PullRequestPayload{
|
apiPullRequest := &api.PullRequestPayload{
|
||||||
Index: issue.Index,
|
Index: issue.Index,
|
||||||
PullRequest: issue.PullRequest.APIFormat(),
|
PullRequest: issue.PullRequest.APIFormat(),
|
||||||
Repository: repo.APIFormat(mode),
|
Repository: issue.Repo.APIFormat(mode),
|
||||||
Sender: doer.APIFormat(),
|
Sender: doer.APIFormat(),
|
||||||
}
|
}
|
||||||
if isClosed {
|
if isClosed {
|
||||||
|
@ -746,12 +781,12 @@ func (issue *Issue) ChangeStatus(doer *User, repo *Repository, isClosed bool) (e
|
||||||
} else {
|
} else {
|
||||||
apiPullRequest.Action = api.HookIssueReOpened
|
apiPullRequest.Action = api.HookIssueReOpened
|
||||||
}
|
}
|
||||||
err = PrepareWebhooks(repo, HookEventPullRequest, apiPullRequest)
|
err = PrepareWebhooks(issue.Repo, HookEventPullRequest, apiPullRequest)
|
||||||
} else {
|
} else {
|
||||||
apiIssue := &api.IssuePayload{
|
apiIssue := &api.IssuePayload{
|
||||||
Index: issue.Index,
|
Index: issue.Index,
|
||||||
Issue: issue.APIFormat(),
|
Issue: issue.APIFormat(),
|
||||||
Repository: repo.APIFormat(mode),
|
Repository: issue.Repo.APIFormat(mode),
|
||||||
Sender: doer.APIFormat(),
|
Sender: doer.APIFormat(),
|
||||||
}
|
}
|
||||||
if isClosed {
|
if isClosed {
|
||||||
|
@ -759,12 +794,12 @@ func (issue *Issue) ChangeStatus(doer *User, repo *Repository, isClosed bool) (e
|
||||||
} else {
|
} else {
|
||||||
apiIssue.Action = api.HookIssueReOpened
|
apiIssue.Action = api.HookIssueReOpened
|
||||||
}
|
}
|
||||||
err = PrepareWebhooks(repo, HookEventIssues, apiIssue)
|
err = PrepareWebhooks(issue.Repo, HookEventIssues, apiIssue)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(4, "PrepareWebhooks [is_pull: %v, is_closed: %v]: %v", issue.IsPull, isClosed, err)
|
log.Error(4, "PrepareWebhooks [is_pull: %v, is_closed: %v]: %v", issue.IsPull, isClosed, err)
|
||||||
} else {
|
} else {
|
||||||
go HookQueue.Add(repo.ID)
|
go HookQueue.Add(issue.Repo.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -785,6 +820,10 @@ func (issue *Issue) ChangeTitle(doer *User, title string) (err error) {
|
||||||
return fmt.Errorf("updateIssueCols: %v", err)
|
return fmt.Errorf("updateIssueCols: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err = issue.loadRepo(sess); err != nil {
|
||||||
|
return fmt.Errorf("loadRepo: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
if _, err = createChangeTitleComment(sess, doer, issue.Repo, issue, oldTitle, title); err != nil {
|
if _, err = createChangeTitleComment(sess, doer, issue.Repo, issue, oldTitle, title); err != nil {
|
||||||
return fmt.Errorf("createChangeTitleComment: %v", err)
|
return fmt.Errorf("createChangeTitleComment: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -795,6 +834,9 @@ func (issue *Issue) ChangeTitle(doer *User, title string) (err error) {
|
||||||
|
|
||||||
mode, _ := AccessLevel(issue.Poster, issue.Repo)
|
mode, _ := AccessLevel(issue.Poster, issue.Repo)
|
||||||
if issue.IsPull {
|
if issue.IsPull {
|
||||||
|
if err = issue.loadPullRequest(sess); err != nil {
|
||||||
|
return fmt.Errorf("loadPullRequest: %v", err)
|
||||||
|
}
|
||||||
issue.PullRequest.Issue = issue
|
issue.PullRequest.Issue = issue
|
||||||
err = PrepareWebhooks(issue.Repo, HookEventPullRequest, &api.PullRequestPayload{
|
err = PrepareWebhooks(issue.Repo, HookEventPullRequest, &api.PullRequestPayload{
|
||||||
Action: api.HookIssueEdited,
|
Action: api.HookIssueEdited,
|
||||||
|
@ -1099,8 +1141,8 @@ func NewIssue(repo *Repository, issue *Issue, labelIDs []int64, assigneeIDs []in
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRawIssueByIndex returns raw issue without loading attributes by index in a repository.
|
// GetIssueByIndex returns raw issue without loading attributes by index in a repository.
|
||||||
func GetRawIssueByIndex(repoID, index int64) (*Issue, error) {
|
func GetIssueByIndex(repoID, index int64) (*Issue, error) {
|
||||||
issue := &Issue{
|
issue := &Issue{
|
||||||
RepoID: repoID,
|
RepoID: repoID,
|
||||||
Index: index,
|
Index: index,
|
||||||
|
@ -1114,9 +1156,9 @@ func GetRawIssueByIndex(repoID, index int64) (*Issue, error) {
|
||||||
return issue, nil
|
return issue, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetIssueByIndex returns issue by index in a repository.
|
// GetIssueWithAttrsByIndex returns issue by index in a repository.
|
||||||
func GetIssueByIndex(repoID, index int64) (*Issue, error) {
|
func GetIssueWithAttrsByIndex(repoID, index int64) (*Issue, error) {
|
||||||
issue, err := GetRawIssueByIndex(repoID, index)
|
issue, err := GetIssueByIndex(repoID, index)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -1131,7 +1173,16 @@ func getIssueByID(e Engine, id int64) (*Issue, error) {
|
||||||
} else if !has {
|
} else if !has {
|
||||||
return nil, ErrIssueNotExist{id, 0, 0}
|
return nil, ErrIssueNotExist{id, 0, 0}
|
||||||
}
|
}
|
||||||
return issue, issue.loadAttributes(e)
|
return issue, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetIssueWithAttrsByID returns an issue with attributes by given ID.
|
||||||
|
func GetIssueWithAttrsByID(id int64) (*Issue, error) {
|
||||||
|
issue, err := getIssueByID(x, id)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return issue, issue.loadAttributes(x)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetIssueByID returns an issue by given ID.
|
// GetIssueByID returns an issue by given ID.
|
||||||
|
|
|
@ -174,7 +174,7 @@ func (issue *Issue) changeAssignee(sess *xorm.Session, doer *User, assigneeID in
|
||||||
apiPullRequest := &api.PullRequestPayload{
|
apiPullRequest := &api.PullRequestPayload{
|
||||||
Index: issue.Index,
|
Index: issue.Index,
|
||||||
PullRequest: issue.PullRequest.APIFormat(),
|
PullRequest: issue.PullRequest.APIFormat(),
|
||||||
Repository: issue.Repo.APIFormat(mode),
|
Repository: issue.Repo.innerAPIFormat(sess, mode, false),
|
||||||
Sender: doer.APIFormat(),
|
Sender: doer.APIFormat(),
|
||||||
}
|
}
|
||||||
if removed {
|
if removed {
|
||||||
|
@ -191,8 +191,8 @@ func (issue *Issue) changeAssignee(sess *xorm.Session, doer *User, assigneeID in
|
||||||
|
|
||||||
apiIssue := &api.IssuePayload{
|
apiIssue := &api.IssuePayload{
|
||||||
Index: issue.Index,
|
Index: issue.Index,
|
||||||
Issue: issue.APIFormat(),
|
Issue: issue.apiFormat(sess),
|
||||||
Repository: issue.Repo.APIFormat(mode),
|
Repository: issue.Repo.innerAPIFormat(sess, mode, false),
|
||||||
Sender: doer.APIFormat(),
|
Sender: doer.APIFormat(),
|
||||||
}
|
}
|
||||||
if removed {
|
if removed {
|
||||||
|
|
|
@ -14,7 +14,7 @@ func TestUpdateAssignee(t *testing.T) {
|
||||||
assert.NoError(t, PrepareTestDatabase())
|
assert.NoError(t, PrepareTestDatabase())
|
||||||
|
|
||||||
// Fake issue with assignees
|
// Fake issue with assignees
|
||||||
issue, err := GetIssueByID(1)
|
issue, err := GetIssueWithAttrsByID(1)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
// Assign multiple users
|
// Assign multiple users
|
||||||
|
|
|
@ -150,25 +150,6 @@ func (c *Comment) LoadIssue() (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// AfterLoad is invoked from XORM after setting the values of all fields of this object.
|
|
||||||
func (c *Comment) AfterLoad(session *xorm.Session) {
|
|
||||||
var err error
|
|
||||||
c.Attachments, err = getAttachmentsByCommentID(session, c.ID)
|
|
||||||
if err != nil {
|
|
||||||
log.Error(3, "getAttachmentsByCommentID[%d]: %v", c.ID, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Poster, err = getUserByID(session, c.PosterID)
|
|
||||||
if err != nil {
|
|
||||||
if IsErrUserNotExist(err) {
|
|
||||||
c.PosterID = -1
|
|
||||||
c.Poster = NewGhostUser()
|
|
||||||
} else {
|
|
||||||
log.Error(3, "getUserByID[%d]: %v", c.ID, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AfterDelete is invoked from XORM after the object is deleted.
|
// AfterDelete is invoked from XORM after the object is deleted.
|
||||||
func (c *Comment) AfterDelete() {
|
func (c *Comment) AfterDelete() {
|
||||||
if c.ID <= 0 {
|
if c.ID <= 0 {
|
||||||
|
@ -189,6 +170,11 @@ func (c *Comment) HTMLURL() string {
|
||||||
log.Error(4, "LoadIssue(%d): %v", c.IssueID, err)
|
log.Error(4, "LoadIssue(%d): %v", c.IssueID, err)
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
err = c.Issue.loadRepo(x)
|
||||||
|
if err != nil { // Silently dropping errors :unamused:
|
||||||
|
log.Error(4, "loadRepo(%d): %v", c.Issue.RepoID, err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
if c.Type == CommentTypeCode {
|
if c.Type == CommentTypeCode {
|
||||||
if c.ReviewID == 0 {
|
if c.ReviewID == 0 {
|
||||||
return fmt.Sprintf("%s/files#%s", c.Issue.HTMLURL(), c.HashTag())
|
return fmt.Sprintf("%s/files#%s", c.Issue.HTMLURL(), c.HashTag())
|
||||||
|
@ -217,6 +203,12 @@ func (c *Comment) IssueURL() string {
|
||||||
if c.Issue.IsPull {
|
if c.Issue.IsPull {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = c.Issue.loadRepo(x)
|
||||||
|
if err != nil { // Silently dropping errors :unamused:
|
||||||
|
log.Error(4, "loadRepo(%d): %v", c.Issue.RepoID, err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
return c.Issue.HTMLURL()
|
return c.Issue.HTMLURL()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,6 +220,12 @@ func (c *Comment) PRURL() string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = c.Issue.loadRepo(x)
|
||||||
|
if err != nil { // Silently dropping errors :unamused:
|
||||||
|
log.Error(4, "loadRepo(%d): %v", c.Issue.RepoID, err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
if !c.Issue.IsPull {
|
if !c.Issue.IsPull {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
@ -303,6 +301,39 @@ func (c *Comment) LoadMilestone() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoadPoster loads comment poster
|
||||||
|
func (c *Comment) LoadPoster() error {
|
||||||
|
if c.PosterID <= 0 || c.Poster != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
c.Poster, err = getUserByID(x, c.PosterID)
|
||||||
|
if err != nil {
|
||||||
|
if IsErrUserNotExist(err) {
|
||||||
|
c.PosterID = -1
|
||||||
|
c.Poster = NewGhostUser()
|
||||||
|
} else {
|
||||||
|
log.Error(3, "getUserByID[%d]: %v", c.ID, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadAttachments loads attachments
|
||||||
|
func (c *Comment) LoadAttachments() error {
|
||||||
|
if len(c.Attachments) > 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
c.Attachments, err = getAttachmentsByCommentID(x, c.ID)
|
||||||
|
if err != nil {
|
||||||
|
log.Error(3, "getAttachmentsByCommentID[%d]: %v", c.ID, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// LoadAssigneeUser if comment.Type is CommentTypeAssignees, then load assignees
|
// LoadAssigneeUser if comment.Type is CommentTypeAssignees, then load assignees
|
||||||
func (c *Comment) LoadAssigneeUser() error {
|
func (c *Comment) LoadAssigneeUser() error {
|
||||||
var err error
|
var err error
|
||||||
|
@ -375,9 +406,11 @@ func (c *Comment) LoadReactions() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Comment) loadReview(e Engine) (err error) {
|
func (c *Comment) loadReview(e Engine) (err error) {
|
||||||
|
if c.Review == nil {
|
||||||
if c.Review, err = getReviewByID(e, c.ReviewID); err != nil {
|
if c.Review, err = getReviewByID(e, c.ReviewID); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -454,6 +487,11 @@ func (c *Comment) CodeCommentURL() string {
|
||||||
log.Error(4, "LoadIssue(%d): %v", c.IssueID, err)
|
log.Error(4, "LoadIssue(%d): %v", c.IssueID, err)
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
err = c.Issue.loadRepo(x)
|
||||||
|
if err != nil { // Silently dropping errors :unamused:
|
||||||
|
log.Error(4, "loadRepo(%d): %v", c.Issue.RepoID, err)
|
||||||
|
return ""
|
||||||
|
}
|
||||||
return fmt.Sprintf("%s/files#%s", c.Issue.HTMLURL(), c.HashTag())
|
return fmt.Sprintf("%s/files#%s", c.Issue.HTMLURL(), c.HashTag())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -601,7 +639,7 @@ func sendCreateCommentAction(e *xorm.Session, opts *CreateCommentOptions, commen
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func createStatusComment(e *xorm.Session, doer *User, repo *Repository, issue *Issue) (*Comment, error) {
|
func createStatusComment(e *xorm.Session, doer *User, issue *Issue) (*Comment, error) {
|
||||||
cmtType := CommentTypeClose
|
cmtType := CommentTypeClose
|
||||||
if !issue.IsClosed {
|
if !issue.IsClosed {
|
||||||
cmtType = CommentTypeReopen
|
cmtType = CommentTypeReopen
|
||||||
|
@ -609,7 +647,7 @@ func createStatusComment(e *xorm.Session, doer *User, repo *Repository, issue *I
|
||||||
return createComment(e, &CreateCommentOptions{
|
return createComment(e, &CreateCommentOptions{
|
||||||
Type: cmtType,
|
Type: cmtType,
|
||||||
Doer: doer,
|
Doer: doer,
|
||||||
Repo: repo,
|
Repo: issue.Repo,
|
||||||
Issue: issue,
|
Issue: issue,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -983,6 +1021,9 @@ func UpdateComment(doer *User, c *Comment, oldContent string) error {
|
||||||
UpdateIssueIndexer(c.IssueID)
|
UpdateIssueIndexer(c.IssueID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := c.LoadPoster(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if err := c.LoadIssue(); err != nil {
|
if err := c.LoadIssue(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1040,6 +1081,9 @@ func DeleteComment(doer *User, comment *Comment) error {
|
||||||
UpdateIssueIndexer(comment.IssueID)
|
UpdateIssueIndexer(comment.IssueID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := comment.LoadPoster(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
if err := comment.LoadIssue(); err != nil {
|
if err := comment.LoadIssue(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -1095,6 +1139,10 @@ func fetchCodeCommentsByReview(e Engine, issue *Issue, currentUser *User, review
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := CommentList(comments).loadPosters(e); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
if err := issue.loadRepo(e); err != nil {
|
if err := issue.loadRepo(e); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
58
models/issue_comment_list.go
Normal file
58
models/issue_comment_list.go
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
// Copyright 2018 The Gitea Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a MIT-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package models
|
||||||
|
|
||||||
|
// CommentList defines a list of comments
|
||||||
|
type CommentList []*Comment
|
||||||
|
|
||||||
|
func (comments CommentList) getPosterIDs() []int64 {
|
||||||
|
commentIDs := make(map[int64]struct{}, len(comments))
|
||||||
|
for _, comment := range comments {
|
||||||
|
if _, ok := commentIDs[comment.PosterID]; !ok {
|
||||||
|
commentIDs[comment.PosterID] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return keysInt64(commentIDs)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadPosters loads posters from database
|
||||||
|
func (comments CommentList) LoadPosters() error {
|
||||||
|
return comments.loadPosters(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (comments CommentList) loadPosters(e Engine) error {
|
||||||
|
if len(comments) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
posterIDs := comments.getPosterIDs()
|
||||||
|
posterMaps := make(map[int64]*User, len(posterIDs))
|
||||||
|
var left = len(posterIDs)
|
||||||
|
for left > 0 {
|
||||||
|
var limit = defaultMaxInSize
|
||||||
|
if left < limit {
|
||||||
|
limit = left
|
||||||
|
}
|
||||||
|
err := e.
|
||||||
|
In("id", posterIDs[:limit]).
|
||||||
|
Find(&posterMaps)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
left = left - limit
|
||||||
|
posterIDs = posterIDs[limit:]
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, comment := range comments {
|
||||||
|
if comment.PosterID <= 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
var ok bool
|
||||||
|
if comment.Poster, ok = posterMaps[comment.PosterID]; !ok {
|
||||||
|
comment.Poster = NewGhostUser()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -19,8 +19,11 @@ func TestCreateIssueDependency(t *testing.T) {
|
||||||
|
|
||||||
issue1, err := GetIssueByID(1)
|
issue1, err := GetIssueByID(1)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
issue1.LoadAttributes()
|
||||||
|
|
||||||
issue2, err := GetIssueByID(2)
|
issue2, err := GetIssueByID(2)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
issue2.LoadAttributes()
|
||||||
|
|
||||||
// Create a dependency and check if it was successful
|
// Create a dependency and check if it was successful
|
||||||
err = CreateIssueDependency(user1, issue1, issue2)
|
err = CreateIssueDependency(user1, issue1, issue2)
|
||||||
|
@ -44,7 +47,7 @@ func TestCreateIssueDependency(t *testing.T) {
|
||||||
assert.False(t, left)
|
assert.False(t, left)
|
||||||
|
|
||||||
// Close #2 and check again
|
// Close #2 and check again
|
||||||
err = issue2.ChangeStatus(user1, issue2.Repo, true)
|
err = issue2.ChangeStatus(user1, true)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
left, err = IssueNoDependenciesLeft(issue1)
|
left, err = IssueNoDependenciesLeft(issue1)
|
||||||
|
|
|
@ -39,7 +39,7 @@ func mailIssueCommentToParticipants(e Engine, issue *Issue, doer *User, content
|
||||||
|
|
||||||
// In case the issue poster is not watching the repository and is active,
|
// In case the issue poster is not watching the repository and is active,
|
||||||
// even if we have duplicated in watchers, can be safely filtered out.
|
// even if we have duplicated in watchers, can be safely filtered out.
|
||||||
poster, err := GetUserByID(issue.PosterID)
|
poster, err := getUserByID(e, issue.PosterID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("GetUserByID [%d]: %v", issue.PosterID, err)
|
return fmt.Errorf("GetUserByID [%d]: %v", issue.PosterID, err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,10 @@ func CreateOrStopIssueStopwatch(user *User, issue *Issue) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if err := issue.loadRepo(x); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if exists {
|
if exists {
|
||||||
// Create tracked time out of the time difference between start date and actual date
|
// Create tracked time out of the time difference between start date and actual date
|
||||||
timediff := time.Now().Unix() - int64(sw.CreatedUnix)
|
timediff := time.Now().Unix() - int64(sw.CreatedUnix)
|
||||||
|
@ -112,6 +116,10 @@ func CancelStopwatch(user *User, issue *Issue) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := issue.loadRepo(x); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if _, err := CreateComment(&CreateCommentOptions{
|
if _, err := CreateComment(&CreateCommentOptions{
|
||||||
Doer: user,
|
Doer: user,
|
||||||
Issue: issue,
|
Issue: issue,
|
||||||
|
|
|
@ -90,6 +90,9 @@ func AddTime(user *User, issue *Issue, time int64) (*TrackedTime, error) {
|
||||||
if _, err := x.Insert(tt); err != nil {
|
if _, err := x.Insert(tt); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if err := issue.loadRepo(x); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
if _, err := CreateComment(&CreateCommentOptions{
|
if _, err := CreateComment(&CreateCommentOptions{
|
||||||
Issue: issue,
|
Issue: issue,
|
||||||
Repo: issue.Repo,
|
Repo: issue.Repo,
|
||||||
|
|
|
@ -151,6 +151,7 @@ func composeTplData(subject, body, link string) map[string]interface{} {
|
||||||
|
|
||||||
func composeIssueCommentMessage(issue *Issue, doer *User, content string, comment *Comment, tplName base.TplName, tos []string, info string) *mailer.Message {
|
func composeIssueCommentMessage(issue *Issue, doer *User, content string, comment *Comment, tplName base.TplName, tos []string, info string) *mailer.Message {
|
||||||
subject := issue.mailSubject()
|
subject := issue.mailSubject()
|
||||||
|
issue.LoadRepo()
|
||||||
body := string(markup.RenderByType(markdown.MarkupName, []byte(content), issue.Repo.HTMLURL(), issue.Repo.ComposeMetas()))
|
body := string(markup.RenderByType(markdown.MarkupName, []byte(content), issue.Repo.HTMLURL(), issue.Repo.ComposeMetas()))
|
||||||
|
|
||||||
data := make(map[string]interface{}, 10)
|
data := make(map[string]interface{}, 10)
|
||||||
|
|
|
@ -148,6 +148,10 @@ func (pr *PullRequest) GetGitRefName() string {
|
||||||
// Required - Issue
|
// Required - Issue
|
||||||
// Optional - Merger
|
// Optional - Merger
|
||||||
func (pr *PullRequest) APIFormat() *api.PullRequest {
|
func (pr *PullRequest) APIFormat() *api.PullRequest {
|
||||||
|
return pr.apiFormat(x)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pr *PullRequest) apiFormat(e Engine) *api.PullRequest {
|
||||||
var (
|
var (
|
||||||
baseBranch *Branch
|
baseBranch *Branch
|
||||||
headBranch *Branch
|
headBranch *Branch
|
||||||
|
@ -155,16 +159,20 @@ func (pr *PullRequest) APIFormat() *api.PullRequest {
|
||||||
headCommit *git.Commit
|
headCommit *git.Commit
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
apiIssue := pr.Issue.APIFormat()
|
if err = pr.Issue.loadRepo(e); err != nil {
|
||||||
|
log.Error(log.ERROR, "loadRepo[%d]: %v", pr.ID, err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
apiIssue := pr.Issue.apiFormat(e)
|
||||||
if pr.BaseRepo == nil {
|
if pr.BaseRepo == nil {
|
||||||
pr.BaseRepo, err = GetRepositoryByID(pr.BaseRepoID)
|
pr.BaseRepo, err = getRepositoryByID(e, pr.BaseRepoID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(log.ERROR, "GetRepositoryById[%d]: %v", pr.ID, err)
|
log.Error(log.ERROR, "GetRepositoryById[%d]: %v", pr.ID, err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if pr.HeadRepo == nil {
|
if pr.HeadRepo == nil {
|
||||||
pr.HeadRepo, err = GetRepositoryByID(pr.HeadRepoID)
|
pr.HeadRepo, err = getRepositoryByID(e, pr.HeadRepoID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error(log.ERROR, "GetRepositoryById[%d]: %v", pr.ID, err)
|
log.Error(log.ERROR, "GetRepositoryById[%d]: %v", pr.ID, err)
|
||||||
return nil
|
return nil
|
||||||
|
@ -187,15 +195,18 @@ func (pr *PullRequest) APIFormat() *api.PullRequest {
|
||||||
Ref: pr.BaseBranch,
|
Ref: pr.BaseBranch,
|
||||||
Sha: baseCommit.ID.String(),
|
Sha: baseCommit.ID.String(),
|
||||||
RepoID: pr.BaseRepoID,
|
RepoID: pr.BaseRepoID,
|
||||||
Repository: pr.BaseRepo.APIFormat(AccessModeNone),
|
Repository: pr.BaseRepo.innerAPIFormat(e, AccessModeNone, false),
|
||||||
}
|
}
|
||||||
apiHeadBranchInfo := &api.PRBranchInfo{
|
apiHeadBranchInfo := &api.PRBranchInfo{
|
||||||
Name: pr.HeadBranch,
|
Name: pr.HeadBranch,
|
||||||
Ref: pr.HeadBranch,
|
Ref: pr.HeadBranch,
|
||||||
Sha: headCommit.ID.String(),
|
Sha: headCommit.ID.String(),
|
||||||
RepoID: pr.HeadRepoID,
|
RepoID: pr.HeadRepoID,
|
||||||
Repository: pr.HeadRepo.APIFormat(AccessModeNone),
|
Repository: pr.HeadRepo.innerAPIFormat(e, AccessModeNone, false),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pr.Issue.loadRepo(e)
|
||||||
|
|
||||||
apiPullRequest := &api.PullRequest{
|
apiPullRequest := &api.PullRequest{
|
||||||
ID: pr.ID,
|
ID: pr.ID,
|
||||||
Index: pr.Index,
|
Index: pr.Index,
|
||||||
|
@ -542,7 +553,7 @@ func (pr *PullRequest) setMerged() (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = pr.Issue.changeStatus(sess, pr.Merger, pr.Issue.Repo, true); err != nil {
|
if err = pr.Issue.changeStatus(sess, pr.Merger, true); err != nil {
|
||||||
return fmt.Errorf("Issue.changeStatus: %v", err)
|
return fmt.Errorf("Issue.changeStatus: %v", err)
|
||||||
}
|
}
|
||||||
if _, err = sess.ID(pr.ID).Cols("has_merged, status, merged_commit_id, merger_id, merged_unix").Update(pr); err != nil {
|
if _, err = sess.ID(pr.ID).Cols("has_merged, status, merged_commit_id, merger_id, merged_unix").Update(pr); err != nil {
|
||||||
|
|
|
@ -51,7 +51,7 @@ func ListToPushCommits(l *list.List) *PushCommits {
|
||||||
}
|
}
|
||||||
commits = append(commits, CommitToPushCommit(commit))
|
commits = append(commits, CommitToPushCommit(commit))
|
||||||
}
|
}
|
||||||
return &PushCommits{l.Len(), commits, "", nil}
|
return &PushCommits{l.Len(), commits, "", make(map[string]string), make(map[string]*User)}
|
||||||
}
|
}
|
||||||
|
|
||||||
// PushUpdateOptions defines the push update options
|
// PushUpdateOptions defines the push update options
|
||||||
|
|
|
@ -175,6 +175,7 @@ func CreateIssue(ctx *context.APIContext, form api.CreateIssueOption) {
|
||||||
|
|
||||||
issue := &models.Issue{
|
issue := &models.Issue{
|
||||||
RepoID: ctx.Repo.Repository.ID,
|
RepoID: ctx.Repo.Repository.ID,
|
||||||
|
Repo: ctx.Repo.Repository,
|
||||||
Title: form.Title,
|
Title: form.Title,
|
||||||
PosterID: ctx.User.ID,
|
PosterID: ctx.User.ID,
|
||||||
Poster: ctx.User,
|
Poster: ctx.User,
|
||||||
|
@ -212,7 +213,7 @@ func CreateIssue(ctx *context.APIContext, form api.CreateIssueOption) {
|
||||||
notification.NotifyNewIssue(issue)
|
notification.NotifyNewIssue(issue)
|
||||||
|
|
||||||
if form.Closed {
|
if form.Closed {
|
||||||
if err := issue.ChangeStatus(ctx.User, ctx.Repo.Repository, true); err != nil {
|
if err := issue.ChangeStatus(ctx.User, true); err != nil {
|
||||||
if models.IsErrDependenciesLeft(err) {
|
if models.IsErrDependenciesLeft(err) {
|
||||||
ctx.Error(http.StatusPreconditionFailed, "DependenciesLeft", "cannot close this issue because it still has open dependencies")
|
ctx.Error(http.StatusPreconditionFailed, "DependenciesLeft", "cannot close this issue because it still has open dependencies")
|
||||||
return
|
return
|
||||||
|
@ -273,6 +274,7 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) {
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
issue.Repo = ctx.Repo.Repository
|
||||||
|
|
||||||
if !issue.IsPoster(ctx.User.ID) && !ctx.Repo.CanWrite(models.UnitTypeIssues) {
|
if !issue.IsPoster(ctx.User.ID) && !ctx.Repo.CanWrite(models.UnitTypeIssues) {
|
||||||
ctx.Status(403)
|
ctx.Status(403)
|
||||||
|
@ -333,7 +335,7 @@ func EditIssue(ctx *context.APIContext, form api.EditIssueOption) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if form.State != nil {
|
if form.State != nil {
|
||||||
if err = issue.ChangeStatus(ctx.User, ctx.Repo.Repository, api.StateClosed == api.StateType(*form.State)); err != nil {
|
if err = issue.ChangeStatus(ctx.User, api.StateClosed == api.StateType(*form.State)); err != nil {
|
||||||
if models.IsErrDependenciesLeft(err) {
|
if models.IsErrDependenciesLeft(err) {
|
||||||
ctx.Error(http.StatusPreconditionFailed, "DependenciesLeft", "cannot close this issue because it still has open dependencies")
|
ctx.Error(http.StatusPreconditionFailed, "DependenciesLeft", "cannot close this issue because it still has open dependencies")
|
||||||
return
|
return
|
||||||
|
|
|
@ -51,7 +51,7 @@ func ListIssueComments(ctx *context.APIContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// comments,err:=models.GetCommentsByIssueIDSince(, since)
|
// comments,err:=models.GetCommentsByIssueIDSince(, since)
|
||||||
issue, err := models.GetRawIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
|
issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Error(500, "GetRawIssueByIndex", err)
|
ctx.Error(500, "GetRawIssueByIndex", err)
|
||||||
return
|
return
|
||||||
|
@ -68,6 +68,10 @@ func ListIssueComments(ctx *context.APIContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
apiComments := make([]*api.Comment, len(comments))
|
apiComments := make([]*api.Comment, len(comments))
|
||||||
|
if err = models.CommentList(comments).LoadPosters(); err != nil {
|
||||||
|
ctx.Error(500, "LoadPosters", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
for i := range comments {
|
for i := range comments {
|
||||||
apiComments[i] = comments[i].APIFormat()
|
apiComments[i] = comments[i].APIFormat()
|
||||||
}
|
}
|
||||||
|
@ -114,6 +118,11 @@ func ListRepoIssueComments(ctx *context.APIContext) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err = models.CommentList(comments).LoadPosters(); err != nil {
|
||||||
|
ctx.Error(500, "LoadPosters", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
apiComments := make([]*api.Comment, len(comments))
|
apiComments := make([]*api.Comment, len(comments))
|
||||||
for i := range comments {
|
for i := range comments {
|
||||||
apiComments[i] = comments[i].APIFormat()
|
apiComments[i] = comments[i].APIFormat()
|
||||||
|
|
|
@ -350,6 +350,7 @@ func EditPullRequest(ctx *context.APIContext, form api.EditPullRequestOption) {
|
||||||
|
|
||||||
pr.LoadIssue()
|
pr.LoadIssue()
|
||||||
issue := pr.Issue
|
issue := pr.Issue
|
||||||
|
issue.Repo = ctx.Repo.Repository
|
||||||
|
|
||||||
if !issue.IsPoster(ctx.User.ID) && !ctx.Repo.CanWrite(models.UnitTypePullRequests) {
|
if !issue.IsPoster(ctx.User.ID) && !ctx.Repo.CanWrite(models.UnitTypePullRequests) {
|
||||||
ctx.Status(403)
|
ctx.Status(403)
|
||||||
|
@ -383,7 +384,6 @@ func EditPullRequest(ctx *context.APIContext, form api.EditPullRequestOption) {
|
||||||
// Send an empty array ([]) to clear all assignees from the Issue.
|
// Send an empty array ([]) to clear all assignees from the Issue.
|
||||||
|
|
||||||
if ctx.Repo.CanWrite(models.UnitTypePullRequests) && (form.Assignees != nil || len(form.Assignee) > 0) {
|
if ctx.Repo.CanWrite(models.UnitTypePullRequests) && (form.Assignees != nil || len(form.Assignee) > 0) {
|
||||||
|
|
||||||
err = models.UpdateAPIAssignee(issue, form.Assignee, form.Assignees, ctx.User)
|
err = models.UpdateAPIAssignee(issue, form.Assignee, form.Assignees, ctx.User)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if models.IsErrUserNotExist(err) {
|
if models.IsErrUserNotExist(err) {
|
||||||
|
@ -422,7 +422,7 @@ func EditPullRequest(ctx *context.APIContext, form api.EditPullRequestOption) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if form.State != nil {
|
if form.State != nil {
|
||||||
if err = issue.ChangeStatus(ctx.User, ctx.Repo.Repository, api.StateClosed == api.StateType(*form.State)); err != nil {
|
if err = issue.ChangeStatus(ctx.User, api.StateClosed == api.StateType(*form.State)); err != nil {
|
||||||
if models.IsErrDependenciesLeft(err) {
|
if models.IsErrDependenciesLeft(err) {
|
||||||
ctx.Error(http.StatusPreconditionFailed, "DependenciesLeft", "cannot close this pull request because it still has open dependencies")
|
ctx.Error(http.StatusPreconditionFailed, "DependenciesLeft", "cannot close this pull request because it still has open dependencies")
|
||||||
return
|
return
|
||||||
|
|
|
@ -576,6 +576,12 @@ func ViewIssue(ctx *context.Context) {
|
||||||
ctx.Data["RequireTribute"] = true
|
ctx.Data["RequireTribute"] = true
|
||||||
renderAttachmentSettings(ctx)
|
renderAttachmentSettings(ctx)
|
||||||
|
|
||||||
|
err = issue.LoadAttributes()
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("GetIssueByIndex", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
ctx.Data["Title"] = fmt.Sprintf("#%d - %s", issue.Index, issue.Title)
|
ctx.Data["Title"] = fmt.Sprintf("#%d - %s", issue.Index, issue.Title)
|
||||||
|
|
||||||
var iw *models.IssueWatch
|
var iw *models.IssueWatch
|
||||||
|
@ -677,6 +683,10 @@ func ViewIssue(ctx *context.Context) {
|
||||||
ctx.ServerError("GetIssueByID", err)
|
ctx.ServerError("GetIssueByID", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if err = otherIssue.LoadRepo(); err != nil {
|
||||||
|
ctx.ServerError("LoadRepo", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
// Add link to the issue of the already running stopwatch
|
// Add link to the issue of the already running stopwatch
|
||||||
ctx.Data["OtherStopwatchURL"] = otherIssue.HTMLURL()
|
ctx.Data["OtherStopwatchURL"] = otherIssue.HTMLURL()
|
||||||
}
|
}
|
||||||
|
@ -697,7 +707,17 @@ func ViewIssue(ctx *context.Context) {
|
||||||
// Render comments and and fetch participants.
|
// Render comments and and fetch participants.
|
||||||
participants[0] = issue.Poster
|
participants[0] = issue.Poster
|
||||||
for _, comment = range issue.Comments {
|
for _, comment = range issue.Comments {
|
||||||
|
if err := comment.LoadPoster(); err != nil {
|
||||||
|
ctx.ServerError("LoadPoster", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if comment.Type == models.CommentTypeComment {
|
if comment.Type == models.CommentTypeComment {
|
||||||
|
if err := comment.LoadAttachments(); err != nil {
|
||||||
|
ctx.ServerError("LoadAttachments", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
comment.RenderedContent = string(markdown.Render([]byte(comment.Content), ctx.Repo.RepoLink,
|
comment.RenderedContent = string(markdown.Render([]byte(comment.Content), ctx.Repo.RepoLink,
|
||||||
ctx.Repo.Repository.ComposeMetas()))
|
ctx.Repo.Repository.ComposeMetas()))
|
||||||
|
|
||||||
|
@ -868,6 +888,7 @@ func GetActionIssue(ctx *context.Context) *models.Issue {
|
||||||
ctx.NotFoundOrServerError("GetIssueByIndex", models.IsErrIssueNotExist, err)
|
ctx.NotFoundOrServerError("GetIssueByIndex", models.IsErrIssueNotExist, err)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
issue.Repo = ctx.Repo.Repository
|
||||||
checkIssueRights(ctx, issue)
|
checkIssueRights(ctx, issue)
|
||||||
if ctx.Written() {
|
if ctx.Written() {
|
||||||
return nil
|
return nil
|
||||||
|
@ -1049,7 +1070,7 @@ func UpdateIssueStatus(ctx *context.Context) {
|
||||||
}
|
}
|
||||||
for _, issue := range issues {
|
for _, issue := range issues {
|
||||||
if issue.IsClosed != isClosed {
|
if issue.IsClosed != isClosed {
|
||||||
if err := issue.ChangeStatus(ctx.User, issue.Repo, isClosed); err != nil {
|
if err := issue.ChangeStatus(ctx.User, isClosed); err != nil {
|
||||||
if models.IsErrDependenciesLeft(err) {
|
if models.IsErrDependenciesLeft(err) {
|
||||||
ctx.JSON(http.StatusPreconditionFailed, map[string]interface{}{
|
ctx.JSON(http.StatusPreconditionFailed, map[string]interface{}{
|
||||||
"error": "cannot close this issue because it still has open dependencies",
|
"error": "cannot close this issue because it still has open dependencies",
|
||||||
|
@ -1126,7 +1147,7 @@ func NewComment(ctx *context.Context, form auth.CreateCommentForm) {
|
||||||
ctx.Flash.Info(ctx.Tr("repo.pulls.open_unmerged_pull_exists", pr.Index))
|
ctx.Flash.Info(ctx.Tr("repo.pulls.open_unmerged_pull_exists", pr.Index))
|
||||||
} else {
|
} else {
|
||||||
isClosed := form.Status == "close"
|
isClosed := form.Status == "close"
|
||||||
if err := issue.ChangeStatus(ctx.User, ctx.Repo.Repository, isClosed); err != nil {
|
if err := issue.ChangeStatus(ctx.User, isClosed); err != nil {
|
||||||
log.Error(4, "ChangeStatus: %v", err)
|
log.Error(4, "ChangeStatus: %v", err)
|
||||||
|
|
||||||
if models.IsErrDependenciesLeft(err) {
|
if models.IsErrDependenciesLeft(err) {
|
||||||
|
|
|
@ -223,6 +223,10 @@ func checkPullInfo(ctx *context.Context) *models.Issue {
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
if err = issue.LoadPoster(); err != nil {
|
||||||
|
ctx.ServerError("LoadPoster", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
ctx.Data["Title"] = fmt.Sprintf("#%d - %s", issue.Index, issue.Title)
|
ctx.Data["Title"] = fmt.Sprintf("#%d - %s", issue.Index, issue.Title)
|
||||||
ctx.Data["Issue"] = issue
|
ctx.Data["Issue"] = issue
|
||||||
|
|
||||||
|
@ -231,6 +235,11 @@ func checkPullInfo(ctx *context.Context) *models.Issue {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err = issue.LoadPullRequest(); err != nil {
|
||||||
|
ctx.ServerError("LoadPullRequest", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
if err = issue.PullRequest.GetHeadRepo(); err != nil {
|
if err = issue.PullRequest.GetHeadRepo(); err != nil {
|
||||||
ctx.ServerError("GetHeadRepo", err)
|
ctx.ServerError("GetHeadRepo", err)
|
||||||
return nil
|
return nil
|
||||||
|
@ -519,16 +528,7 @@ func MergePullRequest(ctx *context.Context, form auth.MergePullRequestForm) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
pr, err := models.GetPullRequestByIssueID(issue.ID)
|
pr := issue.PullRequest
|
||||||
if err != nil {
|
|
||||||
if models.IsErrPullRequestNotExist(err) {
|
|
||||||
ctx.NotFound("GetPullRequestByIssueID", nil)
|
|
||||||
} else {
|
|
||||||
ctx.ServerError("GetPullRequestByIssueID", err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
pr.Issue = issue
|
|
||||||
|
|
||||||
if !pr.CanAutoMerge() || pr.HasMerged {
|
if !pr.CanAutoMerge() || pr.HasMerged {
|
||||||
ctx.NotFound("MergePullRequest", nil)
|
ctx.NotFound("MergePullRequest", nil)
|
||||||
|
@ -949,15 +949,7 @@ func CleanUpPullRequest(ctx *context.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
pr, err := models.GetPullRequestByIssueID(issue.ID)
|
pr := issue.PullRequest
|
||||||
if err != nil {
|
|
||||||
if models.IsErrPullRequestNotExist(err) {
|
|
||||||
ctx.NotFound("GetPullRequestByIssueID", nil)
|
|
||||||
} else {
|
|
||||||
ctx.ServerError("GetPullRequestByIssueID", err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allow cleanup only for merged PR
|
// Allow cleanup only for merged PR
|
||||||
if !pr.HasMerged {
|
if !pr.HasMerged {
|
||||||
|
@ -965,7 +957,7 @@ func CleanUpPullRequest(ctx *context.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = pr.GetHeadRepo(); err != nil {
|
if err := pr.GetHeadRepo(); err != nil {
|
||||||
ctx.ServerError("GetHeadRepo", err)
|
ctx.ServerError("GetHeadRepo", err)
|
||||||
return
|
return
|
||||||
} else if pr.HeadRepo == nil {
|
} else if pr.HeadRepo == nil {
|
||||||
|
@ -1077,8 +1069,12 @@ func DownloadPullDiff(ctx *context.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
pr := issue.PullRequest
|
if err = issue.LoadPullRequest(); err != nil {
|
||||||
|
ctx.ServerError("LoadPullRequest", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pr := issue.PullRequest
|
||||||
if err = pr.GetBaseRepo(); err != nil {
|
if err = pr.GetBaseRepo(); err != nil {
|
||||||
ctx.ServerError("GetBaseRepo", err)
|
ctx.ServerError("GetBaseRepo", err)
|
||||||
return
|
return
|
||||||
|
@ -1111,8 +1107,12 @@ func DownloadPullPatch(ctx *context.Context) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
pr := issue.PullRequest
|
if err = issue.LoadPullRequest(); err != nil {
|
||||||
|
ctx.ServerError("LoadPullRequest", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pr := issue.PullRequest
|
||||||
if err = pr.GetHeadRepo(); err != nil {
|
if err = pr.GetHeadRepo(); err != nil {
|
||||||
ctx.ServerError("GetHeadRepo", err)
|
ctx.ServerError("GetHeadRepo", err)
|
||||||
return
|
return
|
||||||
|
|
|
@ -138,6 +138,7 @@ func Dashboard(ctx *context.Context) {
|
||||||
OnlyPerformedBy: false,
|
OnlyPerformedBy: false,
|
||||||
IncludeDeleted: false,
|
IncludeDeleted: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
if ctx.Written() {
|
if ctx.Written() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue