// 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 "#" | ||||
} | } | ||||
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() | ||||
} | } | ||||
Commits []*PushCommit | Commits []*PushCommit | ||||
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), | |||||
} | } | ||||
} | } | ||||
// 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) | |||||
if err == nil { | |||||
author, ok := pc.emailUsers[commit.AuthorEmail] | |||||
if !ok { | |||||
author, err = GetUserByEmail(commit.AuthorEmail) | |||||
if err == nil { | |||||
authorUsername = author.Name | |||||
pc.emailUsers[commit.AuthorEmail] = author | |||||
} | |||||
} else { | |||||
authorUsername = author.Name | authorUsername = author.Name | ||||
} | } | ||||
committerUsername := "" | committerUsername := "" | ||||
committer, err := GetUserByEmail(commit.CommitterEmail) | |||||
if err == nil { | |||||
// TODO: check errors other than email not found. | |||||
committer, ok := pc.emailUsers[commit.CommitterEmail] | |||||
if !ok { | |||||
committer, err = GetUserByEmail(commit.CommitterEmail) | |||||
if err == nil { | |||||
// TODO: check errors other than email not found. | |||||
committerUsername = committer.Name | |||||
pc.emailUsers[commit.CommitterEmail] = committer | |||||
} | |||||
} else { | |||||
committerUsername = committer.Name | committerUsername = committer.Name | ||||
} | } | ||||
commits[i] = &api.PayloadCommit{ | commits[i] = &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] | ||||
} | } | ||||
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 | ||||
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 | ||||
} | } | ||||
} | } |
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) | ||||
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) | ||||
} | } | ||||
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 | ||||
// 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(), | ||||
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()) | ||||
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, | ||||
} | } | ||||
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 | ||||
} | } | ||||
// 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 | ||||
} | } | ||||
} | } | ||||
// 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 | ||||
} | } | ||||
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 { | ||||
} 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 { | ||||
} 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 | ||||
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) | ||||
} | } | ||||
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, | ||||
return nil | return nil | ||||
} | } | ||||
// GetRawIssueByIndex returns raw issue without loading attributes by index in a repository. | |||||
func GetRawIssueByIndex(repoID, index int64) (*Issue, error) { | |||||
// GetIssueByIndex returns raw issue without loading attributes by index in a repository. | |||||
func GetIssueByIndex(repoID, index int64) (*Issue, error) { | |||||
issue := &Issue{ | issue := &Issue{ | ||||
RepoID: repoID, | RepoID: repoID, | ||||
Index: index, | Index: index, | ||||
return issue, nil | return issue, nil | ||||
} | } | ||||
// GetIssueByIndex returns issue by index in a repository. | |||||
func GetIssueByIndex(repoID, index int64) (*Issue, error) { | |||||
issue, err := GetRawIssueByIndex(repoID, index) | |||||
// GetIssueWithAttrsByIndex returns issue by index in a repository. | |||||
func GetIssueWithAttrsByIndex(repoID, index int64) (*Issue, error) { | |||||
issue, err := GetIssueByIndex(repoID, index) | |||||
if err != nil { | if err != nil { | ||||
return nil, err | return nil, err | ||||
} | } | ||||
} 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. |
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 { | ||||
apiIssue := &api.IssuePayload{ | apiIssue := &api.IssuePayload{ | ||||
Index: issue.Index, | Index: issue.Index, | ||||
Issue: issue.APIFormat(), | |||||
Repository: issue.Repo.APIFormat(mode), | |||||
Issue: issue.apiFormat(sess), | |||||
Repository: issue.Repo.innerAPIFormat(sess, mode, false), | |||||
Sender: doer.APIFormat(), | Sender: doer.APIFormat(), | ||||
} | } | ||||
if removed { | if removed { |
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 |
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 { | ||||
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()) | ||||
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() | ||||
} | } | ||||
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 "" | ||||
} | } | ||||
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 | ||||
} | } | ||||
func (c *Comment) loadReview(e Engine) (err error) { | func (c *Comment) loadReview(e Engine) (err error) { | ||||
if c.Review, err = getReviewByID(e, c.ReviewID); err != nil { | |||||
return err | |||||
if c.Review == nil { | |||||
if c.Review, err = getReviewByID(e, c.ReviewID); err != nil { | |||||
return err | |||||
} | |||||
} | } | ||||
return nil | return nil | ||||
} | } | ||||
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()) | ||||
} | } | ||||
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 | ||||
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, | ||||
}) | }) | ||||
} | } | ||||
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 | ||||
} | } | ||||
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 | ||||
} | } | ||||
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 | ||||
} | } |
// 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 | |||||
} |
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) | ||||
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) |
// 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) | ||||
} | } |
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) | ||||
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, |
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, |
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) |
// 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 | ||||
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 | ||||
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, | ||||
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 { |
} | } | ||||
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 |
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, | ||||
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 | ||||
} | } | ||||
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) | ||||
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 |
} | } | ||||
// 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 | ||||
} | } | ||||
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() | ||||
} | } | ||||
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() |
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) | ||||
// 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) { | ||||
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 |
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 | ||||
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() | ||||
} | } | ||||
// 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())) | ||||
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 | ||||
} | } | ||||
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", | ||||
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) { |
} | } | ||||
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 | ||||
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 | ||||
return | return | ||||
} | } | ||||
pr, err := models.GetPullRequestByIssueID(issue.ID) | |||||
if err != nil { | |||||
if models.IsErrPullRequestNotExist(err) { | |||||
ctx.NotFound("GetPullRequestByIssueID", nil) | |||||
} else { | |||||
ctx.ServerError("GetPullRequestByIssueID", err) | |||||
} | |||||
return | |||||
} | |||||
pr.Issue = issue | |||||
pr := issue.PullRequest | |||||
if !pr.CanAutoMerge() || pr.HasMerged { | if !pr.CanAutoMerge() || pr.HasMerged { | ||||
ctx.NotFound("MergePullRequest", nil) | ctx.NotFound("MergePullRequest", nil) | ||||
return | return | ||||
} | } | ||||
pr, err := models.GetPullRequestByIssueID(issue.ID) | |||||
if err != nil { | |||||
if models.IsErrPullRequestNotExist(err) { | |||||
ctx.NotFound("GetPullRequestByIssueID", nil) | |||||
} else { | |||||
ctx.ServerError("GetPullRequestByIssueID", err) | |||||
} | |||||
return | |||||
} | |||||
pr := issue.PullRequest | |||||
// Allow cleanup only for merged PR | // Allow cleanup only for merged PR | ||||
if !pr.HasMerged { | if !pr.HasMerged { | ||||
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 { | ||||
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 | ||||
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 |
OnlyPerformedBy: false, | OnlyPerformedBy: false, | ||||
IncludeDeleted: false, | IncludeDeleted: false, | ||||
}) | }) | ||||
if ctx.Written() { | if ctx.Written() { | ||||
return | return | ||||
} | } |