diff --git a/models/issue.go b/models/issue.go index 511bfa31c411e..ab9456fcf5d26 100644 --- a/models/issue.go +++ b/models/issue.go @@ -1225,6 +1225,16 @@ func NewIssue(repo *Repository, issue *Issue, labelIDs []int64, assigneeIDs []in go HookQueue.Add(issue.RepoID) } + err = FindAndCreateIssueRef(issue.Poster, repo, issue, issue.Content) + if err != nil { + return err + } + + err = FindAndCreateIssueRef(issue.Poster, repo, issue, issue.Title) + if err != nil { + return err + } + return nil } diff --git a/models/issue_comment.go b/models/issue_comment.go index 2a9e8596cb202..bd40a7177e82e 100644 --- a/models/issue_comment.go +++ b/models/issue_comment.go @@ -8,6 +8,7 @@ package models import ( "fmt" + "html" "strings" "code.gitea.io/gitea/modules/git" @@ -830,6 +831,11 @@ func CreateIssueComment(doer *User, repo *Repository, issue *Issue, content stri return nil, fmt.Errorf("CreateComment: %v", err) } + err = FindAndCreateIssueRef(doer, repo, issue, content) + if err != nil { + return nil, err + } + mode, _ := AccessLevel(doer, repo) if err = PrepareWebhooks(repo, HookEventIssueComment, &api.IssueCommentPayload{ Action: api.HookIssueCommentCreated, @@ -845,6 +851,51 @@ func CreateIssueComment(doer *User, repo *Repository, issue *Issue, content stri return comment, nil } +// FindAndCreateIssueRef Search message for issue ref and create cross ref comment +func FindAndCreateIssueRef(doer *User, repo *Repository, issue *Issue, message string) error { + refMarked := make(map[int64]bool) + var refRepo *Repository + var err error + for _, m := range issueReferenceKeywordsPat.FindAllStringSubmatch(message, -1) { + if len(m[3]) == 0 { + continue + } + ref := m[3] + + // issue is from another repo + if len(m[1]) > 0 && len(m[2]) > 0 { + refRepo, err = GetRepositoryFromMatch(m[1], m[2]) + if err != nil { + continue + } + } else { + refRepo = repo + } + refIssue, err := getIssueFromRef(refRepo, ref) + if err != nil { + return err + } + + if refIssue == nil || refMarked[refIssue.ID] { + continue + } + refMarked[refIssue.ID] = true + + issueType := "issues" + + if issue.IsPull { + issueType = "pull" + } + + message := fmt.Sprintf(`%s`, repo.Link(), issueType, issue.ID, html.EscapeString(issue.Title)) + if err = CreateIssueRefComment(doer, refRepo, refIssue, message); err != nil { + return err + } + } + + return nil +} + // CreateRefComment creates a commit reference comment to issue. func CreateRefComment(doer *User, repo *Repository, issue *Issue, content, commitSHA string) error { if len(commitSHA) == 0 { @@ -874,6 +925,30 @@ func CreateRefComment(doer *User, repo *Repository, issue *Issue, content, commi return err } +// CreateIssueRefComment creates a issue reference comment to issue. +func CreateIssueRefComment(doer *User, repo *Repository, issue *Issue, content string) error { + // Check if same reference from same issue has already existed. + has, err := x.Get(&Comment{ + Type: CommentTypeIssueRef, + IssueID: issue.ID, + Content: content, + }) + if err != nil { + return fmt.Errorf("CreateIssueRefComment (check has): %v", err) + } else if has { + return nil + } + + _, err = CreateComment(&CreateCommentOptions{ + Type: CommentTypeIssueRef, + Doer: doer, + Repo: repo, + Issue: issue, + Content: content, + }) + return err +} + // GetCommentByID returns the comment by given ID. func GetCommentByID(id int64) (*Comment, error) { c := new(Comment) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 36fabb5b4aa78..c4b6200fea13d 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -835,6 +835,7 @@ issues.reopen_comment_issue = Comment and Reopen issues.create_comment = Comment issues.closed_at = `closed %[2]s` issues.reopened_at = `reopened %[2]s` +issues.issue_ref_at = `referenced this issue from %s` issues.commit_ref_at = `referenced this issue from a commit %[2]s` issues.poster = Poster issues.collaborator = Collaborator diff --git a/routers/repo/issue.go b/routers/repo/issue.go index dc09a650fed7f..32d53b68007aa 100644 --- a/routers/repo/issue.go +++ b/routers/repo/issue.go @@ -1042,6 +1042,11 @@ func UpdateIssueTitle(ctx *context.Context) { return } + if err := models.FindAndCreateIssueRef(ctx.User, ctx.Repo.Repository, issue, title); err != nil { + ctx.ServerError("FindAndCreateIssueRef", err) + return + } + notification.NotifyIssueChangeTitle(ctx.User, issue, oldTitle) ctx.JSON(200, map[string]interface{}{ @@ -1067,6 +1072,11 @@ func UpdateIssueContent(ctx *context.Context) { return } + if err := models.FindAndCreateIssueRef(ctx.User, ctx.Repo.Repository, issue, content); err != nil { + ctx.ServerError("FindAndCreateIssueRef", err) + return + } + ctx.JSON(200, map[string]interface{}{ "content": string(markdown.Render([]byte(issue.Content), ctx.Query("context"), ctx.Repo.Repository.ComposeMetas())), }) @@ -1339,6 +1349,11 @@ func UpdateCommentContent(ctx *context.Context) { return } + if err = models.FindAndCreateIssueRef(ctx.User, ctx.Repo.Repository, comment.Issue, comment.Content); err != nil { + ctx.ServerError("FindAndCreateIssueRef", err) + return + } + notification.NotifyUpdateComment(ctx.User, comment, oldContent) ctx.JSON(200, map[string]interface{}{ diff --git a/templates/repo/issue/view_content/comments.tmpl b/templates/repo/issue/view_content/comments.tmpl index 70de314c918dc..d2a9b094ecd9b 100644 --- a/templates/repo/issue/view_content/comments.tmpl +++ b/templates/repo/issue/view_content/comments.tmpl @@ -96,6 +96,17 @@ {{.Poster.GetDisplayName}} {{$.i18n.Tr "repo.issues.closed_at" .EventTag $createdStr | Safe}} + {{else if eq .Type 3}} +