|
|
@ -457,23 +457,6 @@ func (issue *Issue) IsPoster(uid int64) bool { |
|
|
|
return issue.OriginalAuthorID == 0 && issue.PosterID == uid |
|
|
|
return issue.OriginalAuthorID == 0 && issue.PosterID == uid |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (issue *Issue) hasLabel(e db.Engine, labelID int64) bool { |
|
|
|
|
|
|
|
return hasIssueLabel(e, issue.ID, labelID) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// HasLabel returns true if issue has been labeled by given ID.
|
|
|
|
|
|
|
|
func (issue *Issue) HasLabel(labelID int64) bool { |
|
|
|
|
|
|
|
return issue.hasLabel(db.GetEngine(db.DefaultContext), labelID) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (issue *Issue) addLabel(ctx context.Context, label *Label, doer *user_model.User) error { |
|
|
|
|
|
|
|
return newIssueLabel(ctx, issue, label, doer) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (issue *Issue) addLabels(ctx context.Context, labels []*Label, doer *user_model.User) error { |
|
|
|
|
|
|
|
return newIssueLabels(ctx, issue, labels, doer) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (issue *Issue) getLabels(e db.Engine) (err error) { |
|
|
|
func (issue *Issue) getLabels(e db.Engine) (err error) { |
|
|
|
if len(issue.Labels) > 0 { |
|
|
|
if len(issue.Labels) > 0 { |
|
|
|
return nil |
|
|
|
return nil |
|
|
@ -486,17 +469,13 @@ func (issue *Issue) getLabels(e db.Engine) (err error) { |
|
|
|
return nil |
|
|
|
return nil |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (issue *Issue) removeLabel(ctx context.Context, doer *user_model.User, label *Label) error { |
|
|
|
func clearIssueLabels(ctx context.Context, issue *Issue, doer *user_model.User) (err error) { |
|
|
|
return deleteIssueLabel(ctx, issue, label, doer) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (issue *Issue) clearLabels(ctx context.Context, doer *user_model.User) (err error) { |
|
|
|
|
|
|
|
if err = issue.getLabels(db.GetEngine(ctx)); err != nil { |
|
|
|
if err = issue.getLabels(db.GetEngine(ctx)); err != nil { |
|
|
|
return fmt.Errorf("getLabels: %v", err) |
|
|
|
return fmt.Errorf("getLabels: %v", err) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
for i := range issue.Labels { |
|
|
|
for i := range issue.Labels { |
|
|
|
if err = issue.removeLabel(ctx, doer, issue.Labels[i]); err != nil { |
|
|
|
if err = deleteIssueLabel(ctx, issue, issue.Labels[i], doer); err != nil { |
|
|
|
return fmt.Errorf("removeLabel: %v", err) |
|
|
|
return fmt.Errorf("removeLabel: %v", err) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -504,9 +483,9 @@ func (issue *Issue) clearLabels(ctx context.Context, doer *user_model.User) (err |
|
|
|
return nil |
|
|
|
return nil |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// ClearLabels removes all issue labels as the given user.
|
|
|
|
// ClearIssueLabels removes all issue labels as the given user.
|
|
|
|
// Triggers appropriate WebHooks, if any.
|
|
|
|
// Triggers appropriate WebHooks, if any.
|
|
|
|
func (issue *Issue) ClearLabels(doer *user_model.User) (err error) { |
|
|
|
func ClearIssueLabels(issue *Issue, doer *user_model.User) (err error) { |
|
|
|
ctx, committer, err := db.TxContext() |
|
|
|
ctx, committer, err := db.TxContext() |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
return err |
|
|
|
return err |
|
|
@ -527,7 +506,7 @@ func (issue *Issue) ClearLabels(doer *user_model.User) (err error) { |
|
|
|
return ErrRepoLabelNotExist{} |
|
|
|
return ErrRepoLabelNotExist{} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if err = issue.clearLabels(ctx, doer); err != nil { |
|
|
|
if err = clearIssueLabels(ctx, issue, doer); err != nil { |
|
|
|
return err |
|
|
|
return err |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -552,9 +531,9 @@ func (ts labelSorter) Swap(i, j int) { |
|
|
|
[]*Label(ts)[i], []*Label(ts)[j] = []*Label(ts)[j], []*Label(ts)[i] |
|
|
|
[]*Label(ts)[i], []*Label(ts)[j] = []*Label(ts)[j], []*Label(ts)[i] |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// ReplaceLabels removes all current labels and add new labels to the issue.
|
|
|
|
// ReplaceIssueLabels removes all current labels and add new labels to the issue.
|
|
|
|
// Triggers appropriate WebHooks, if any.
|
|
|
|
// Triggers appropriate WebHooks, if any.
|
|
|
|
func (issue *Issue) ReplaceLabels(labels []*Label, doer *user_model.User) (err error) { |
|
|
|
func ReplaceIssueLabels(issue *Issue, labels []*Label, doer *user_model.User) (err error) { |
|
|
|
ctx, committer, err := db.TxContext() |
|
|
|
ctx, committer, err := db.TxContext() |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
return err |
|
|
|
return err |
|
|
@ -601,13 +580,13 @@ func (issue *Issue) ReplaceLabels(labels []*Label, doer *user_model.User) (err e |
|
|
|
toRemove = append(toRemove, issue.Labels[removeIndex:]...) |
|
|
|
toRemove = append(toRemove, issue.Labels[removeIndex:]...) |
|
|
|
|
|
|
|
|
|
|
|
if len(toAdd) > 0 { |
|
|
|
if len(toAdd) > 0 { |
|
|
|
if err = issue.addLabels(ctx, toAdd, doer); err != nil { |
|
|
|
if err = newIssueLabels(ctx, issue, toAdd, doer); err != nil { |
|
|
|
return fmt.Errorf("addLabels: %v", err) |
|
|
|
return fmt.Errorf("addLabels: %v", err) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
for _, l := range toRemove { |
|
|
|
for _, l := range toRemove { |
|
|
|
if err = issue.removeLabel(ctx, doer, l); err != nil { |
|
|
|
if err = deleteIssueLabel(ctx, issue, l, doer); err != nil { |
|
|
|
return fmt.Errorf("removeLabel: %v", err) |
|
|
|
return fmt.Errorf("removeLabel: %v", err) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -636,7 +615,7 @@ func updateIssueCols(ctx context.Context, issue *Issue, cols ...string) error { |
|
|
|
return nil |
|
|
|
return nil |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (issue *Issue) changeStatus(ctx context.Context, doer *user_model.User, isClosed, isMergePull bool) (*Comment, error) { |
|
|
|
func changeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.User, isClosed, isMergePull bool) (*Comment, error) { |
|
|
|
// Reload the issue
|
|
|
|
// Reload the issue
|
|
|
|
currentIssue, err := getIssueByID(db.GetEngine(ctx), issue.ID) |
|
|
|
currentIssue, err := getIssueByID(db.GetEngine(ctx), issue.ID) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
@ -656,10 +635,10 @@ func (issue *Issue) changeStatus(ctx context.Context, doer *user_model.User, isC |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
issue.IsClosed = isClosed |
|
|
|
issue.IsClosed = isClosed |
|
|
|
return issue.doChangeStatus(ctx, doer, isMergePull) |
|
|
|
return doChangeIssueStatus(ctx, issue, doer, isMergePull) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (issue *Issue) doChangeStatus(ctx context.Context, doer *user_model.User, isMergePull bool) (*Comment, error) { |
|
|
|
func doChangeIssueStatus(ctx context.Context, issue *Issue, doer *user_model.User, isMergePull bool) (*Comment, error) { |
|
|
|
e := db.GetEngine(ctx) |
|
|
|
e := db.GetEngine(ctx) |
|
|
|
// Check for open dependencies
|
|
|
|
// Check for open dependencies
|
|
|
|
if issue.IsClosed && issue.Repo.IsDependenciesEnabledCtx(ctx) { |
|
|
|
if issue.IsClosed && issue.Repo.IsDependenciesEnabledCtx(ctx) { |
|
|
@ -701,7 +680,7 @@ func (issue *Issue) doChangeStatus(ctx context.Context, doer *user_model.User, i |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if err := issue.updateClosedNum(ctx); err != nil { |
|
|
|
if err := updateIssueClosedNum(ctx, issue); err != nil { |
|
|
|
return nil, err |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -721,8 +700,8 @@ func (issue *Issue) doChangeStatus(ctx context.Context, doer *user_model.User, i |
|
|
|
}) |
|
|
|
}) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// ChangeStatus changes issue status to open or closed.
|
|
|
|
// ChangeIssueStatus changes issue status to open or closed.
|
|
|
|
func (issue *Issue) ChangeStatus(doer *user_model.User, isClosed bool) (*Comment, error) { |
|
|
|
func ChangeIssueStatus(issue *Issue, doer *user_model.User, isClosed bool) (*Comment, error) { |
|
|
|
ctx, committer, err := db.TxContext() |
|
|
|
ctx, committer, err := db.TxContext() |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
return nil, err |
|
|
@ -736,7 +715,7 @@ func (issue *Issue) ChangeStatus(doer *user_model.User, isClosed bool) (*Comment |
|
|
|
return nil, err |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
comment, err := issue.changeStatus(ctx, doer, isClosed, false) |
|
|
|
comment, err := changeIssueStatus(ctx, issue, doer, isClosed, false) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
return nil, err |
|
|
|
return nil, err |
|
|
|
} |
|
|
|
} |
|
|
@ -748,8 +727,8 @@ func (issue *Issue) ChangeStatus(doer *user_model.User, isClosed bool) (*Comment |
|
|
|
return comment, nil |
|
|
|
return comment, nil |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// ChangeTitle changes the title of this issue, as the given user.
|
|
|
|
// ChangeIssueTitle changes the title of this issue, as the given user.
|
|
|
|
func (issue *Issue) ChangeTitle(doer *user_model.User, oldTitle string) (err error) { |
|
|
|
func ChangeIssueTitle(issue *Issue, doer *user_model.User, oldTitle string) (err error) { |
|
|
|
ctx, committer, err := db.TxContext() |
|
|
|
ctx, committer, err := db.TxContext() |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
return err |
|
|
|
return err |
|
|
@ -782,8 +761,8 @@ func (issue *Issue) ChangeTitle(doer *user_model.User, oldTitle string) (err err |
|
|
|
return committer.Commit() |
|
|
|
return committer.Commit() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// ChangeRef changes the branch of this issue, as the given user.
|
|
|
|
// ChangeIssueRef changes the branch of this issue, as the given user.
|
|
|
|
func (issue *Issue) ChangeRef(doer *user_model.User, oldRef string) (err error) { |
|
|
|
func ChangeIssueRef(issue *Issue, doer *user_model.User, oldRef string) (err error) { |
|
|
|
ctx, committer, err := db.TxContext() |
|
|
|
ctx, committer, err := db.TxContext() |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
return err |
|
|
|
return err |
|
|
@ -840,8 +819,8 @@ func AddDeletePRBranchComment(doer *user_model.User, repo *repo_model.Repository |
|
|
|
return committer.Commit() |
|
|
|
return committer.Commit() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// UpdateAttachments update attachments by UUIDs for the issue
|
|
|
|
// UpdateIssueAttachments update attachments by UUIDs for the issue
|
|
|
|
func (issue *Issue) UpdateAttachments(uuids []string) (err error) { |
|
|
|
func UpdateIssueAttachments(issueID int64, uuids []string) (err error) { |
|
|
|
ctx, committer, err := db.TxContext() |
|
|
|
ctx, committer, err := db.TxContext() |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
return err |
|
|
|
return err |
|
|
@ -852,7 +831,7 @@ func (issue *Issue) UpdateAttachments(uuids []string) (err error) { |
|
|
|
return fmt.Errorf("getAttachmentsByUUIDs [uuids: %v]: %v", uuids, err) |
|
|
|
return fmt.Errorf("getAttachmentsByUUIDs [uuids: %v]: %v", uuids, err) |
|
|
|
} |
|
|
|
} |
|
|
|
for i := 0; i < len(attachments); i++ { |
|
|
|
for i := 0; i < len(attachments); i++ { |
|
|
|
attachments[i].IssueID = issue.ID |
|
|
|
attachments[i].IssueID = issueID |
|
|
|
if err := repo_model.UpdateAttachmentCtx(ctx, attachments[i]); err != nil { |
|
|
|
if err := repo_model.UpdateAttachmentCtx(ctx, attachments[i]); err != nil { |
|
|
|
return fmt.Errorf("update attachment [id: %d]: %v", attachments[i].ID, err) |
|
|
|
return fmt.Errorf("update attachment [id: %d]: %v", attachments[i].ID, err) |
|
|
|
} |
|
|
|
} |
|
|
@ -860,8 +839,8 @@ func (issue *Issue) UpdateAttachments(uuids []string) (err error) { |
|
|
|
return committer.Commit() |
|
|
|
return committer.Commit() |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// ChangeContent changes issue content, as the given user.
|
|
|
|
// ChangeIssueContent changes issue content, as the given user.
|
|
|
|
func (issue *Issue) ChangeContent(doer *user_model.User, content string) (err error) { |
|
|
|
func ChangeIssueContent(issue *Issue, doer *user_model.User, content string) (err error) { |
|
|
|
ctx, committer, err := db.TxContext() |
|
|
|
ctx, committer, err := db.TxContext() |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
return err |
|
|
|
return err |
|
|
@ -1034,7 +1013,7 @@ func newIssue(ctx context.Context, doer *user_model.User, opts NewIssueOptions) |
|
|
|
continue |
|
|
|
continue |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if err = opts.Issue.addLabel(ctx, label, opts.Issue.Poster); err != nil { |
|
|
|
if err = newIssueLabel(ctx, opts.Issue, label, opts.Issue.Poster); err != nil { |
|
|
|
return fmt.Errorf("addLabel [id: %d]: %v", label.ID, err) |
|
|
|
return fmt.Errorf("addLabel [id: %d]: %v", label.ID, err) |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
@ -2011,7 +1990,7 @@ func UpdateIssueByAPI(issue *Issue, doer *user_model.User) (statusChangeComment |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if currentIssue.IsClosed != issue.IsClosed { |
|
|
|
if currentIssue.IsClosed != issue.IsClosed { |
|
|
|
statusChangeComment, err = issue.doChangeStatus(ctx, doer, false) |
|
|
|
statusChangeComment, err = doChangeIssueStatus(ctx, issue, doer, false) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
return nil, false, err |
|
|
|
return nil, false, err |
|
|
|
} |
|
|
|
} |
|
|
@ -2235,7 +2214,7 @@ func (issue *Issue) BlockingDependencies() ([]*DependencyInfo, error) { |
|
|
|
return issue.getBlockingDependencies(db.GetEngine(db.DefaultContext)) |
|
|
|
return issue.getBlockingDependencies(db.GetEngine(db.DefaultContext)) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
func (issue *Issue) updateClosedNum(ctx context.Context) (err error) { |
|
|
|
func updateIssueClosedNum(ctx context.Context, issue *Issue) (err error) { |
|
|
|
if issue.IsPull { |
|
|
|
if issue.IsPull { |
|
|
|
err = repoStatsCorrectNumClosed(ctx, issue.RepoID, true, "num_closed_pulls") |
|
|
|
err = repoStatsCorrectNumClosed(ctx, issue.RepoID, true, "num_closed_pulls") |
|
|
|
} else { |
|
|
|
} else { |
|
|
@ -2245,9 +2224,9 @@ func (issue *Issue) updateClosedNum(ctx context.Context) (err error) { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// FindAndUpdateIssueMentions finds users mentioned in the given content string, and saves them in the database.
|
|
|
|
// FindAndUpdateIssueMentions finds users mentioned in the given content string, and saves them in the database.
|
|
|
|
func (issue *Issue) FindAndUpdateIssueMentions(ctx context.Context, doer *user_model.User, content string) (mentions []*user_model.User, err error) { |
|
|
|
func FindAndUpdateIssueMentions(ctx context.Context, issue *Issue, doer *user_model.User, content string) (mentions []*user_model.User, err error) { |
|
|
|
rawMentions := references.FindAllMentionsMarkdown(content) |
|
|
|
rawMentions := references.FindAllMentionsMarkdown(content) |
|
|
|
mentions, err = issue.ResolveMentionsByVisibility(ctx, doer, rawMentions) |
|
|
|
mentions, err = ResolveIssueMentionsByVisibility(ctx, issue, doer, rawMentions) |
|
|
|
if err != nil { |
|
|
|
if err != nil { |
|
|
|
return nil, fmt.Errorf("UpdateIssueMentions [%d]: %v", issue.ID, err) |
|
|
|
return nil, fmt.Errorf("UpdateIssueMentions [%d]: %v", issue.ID, err) |
|
|
|
} |
|
|
|
} |
|
|
@ -2257,9 +2236,9 @@ func (issue *Issue) FindAndUpdateIssueMentions(ctx context.Context, doer *user_m |
|
|
|
return |
|
|
|
return |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// ResolveMentionsByVisibility returns the users mentioned in an issue, removing those that
|
|
|
|
// ResolveIssueMentionsByVisibility returns the users mentioned in an issue, removing those that
|
|
|
|
// don't have access to reading it. Teams are expanded into their users, but organizations are ignored.
|
|
|
|
// don't have access to reading it. Teams are expanded into their users, but organizations are ignored.
|
|
|
|
func (issue *Issue) ResolveMentionsByVisibility(ctx context.Context, doer *user_model.User, mentions []string) (users []*user_model.User, err error) { |
|
|
|
func ResolveIssueMentionsByVisibility(ctx context.Context, issue *Issue, doer *user_model.User, mentions []string) (users []*user_model.User, err error) { |
|
|
|
if len(mentions) == 0 { |
|
|
|
if len(mentions) == 0 { |
|
|
|
return |
|
|
|
return |
|
|
|
} |
|
|
|
} |
|
|
|