浏览代码

models/action.go: add action reopen for #462

- models/issue.go: format comment type names
Unknwon 10 年之前
父节点
当前提交
5a99e9a37b
共有 3 个文件被更改,包括 84 次插入45 次删除
  1. 62 17
      models/action.go
  2. 10 16
      models/issue.go
  3. 12 12
      routers/repo/issue.go

+ 62 - 17
models/action.go

@@ -41,13 +41,20 @@ var (
 
 var (
 	// Same as Github. See https://help.github.com/articles/closing-issues-via-commit-messages
-	IssueCloseKeywords        = []string{"close", "closes", "closed", "fix", "fixes", "fixed", "resolve", "resolves", "resolved"}
-	IssueCloseKeywordsPat     *regexp.Regexp
-	IssueReferenceKeywordsPat *regexp.Regexp
+	IssueCloseKeywords  = []string{"close", "closes", "closed", "fix", "fixes", "fixed", "resolve", "resolves", "resolved"}
+	IssueReopenKeywords = []string{"reopen", "reopens", "reopened"}
+
+	IssueCloseKeywordsPat, IssueReopenKeywordsPat *regexp.Regexp
+	IssueReferenceKeywordsPat                     *regexp.Regexp
 )
 
+func assembleKeywordsPattern(words []string) string {
+	return fmt.Sprintf(`(?i)(?:%s) \S+`, strings.Join(words, "|"))
+}
+
 func init() {
-	IssueCloseKeywordsPat = regexp.MustCompile(fmt.Sprintf(`(?i)(?:%s) \S+`, strings.Join(IssueCloseKeywords, "|")))
+	IssueCloseKeywordsPat = regexp.MustCompile(assembleKeywordsPattern(IssueCloseKeywords))
+	IssueReopenKeywordsPat = regexp.MustCompile(assembleKeywordsPattern(IssueReopenKeywords))
 	IssueReferenceKeywordsPat = regexp.MustCompile(`(?i)(?:)(^| )\S+`)
 }
 
@@ -112,11 +119,9 @@ func (a Action) GetIssueInfos() []string {
 
 func updateIssuesCommit(userId, repoId int64, repoUserName, repoName string, commits []*base.PushCommit) error {
 	for _, c := range commits {
-		references := IssueReferenceKeywordsPat.FindAllString(c.Message, -1)
-
 		// FIXME: should not be a reference when it comes with action.
 		// e.g. fixes #1 will not have duplicated reference message.
-		for _, ref := range references {
+		for _, ref := range IssueReferenceKeywordsPat.FindAllString(c.Message, -1) {
 			ref := ref[strings.IndexByte(ref, byte(' '))+1:]
 			ref = strings.TrimRightFunc(ref, func(c rune) bool {
 				return !unicode.IsDigit(c)
@@ -137,22 +142,18 @@ func updateIssuesCommit(userId, repoId int64, repoUserName, repoName string, com
 			}
 
 			issue, err := GetIssueByRef(ref)
-
 			if err != nil {
 				return err
 			}
 
 			url := fmt.Sprintf("%s/%s/%s/commit/%s", setting.AppSubUrl, repoUserName, repoName, c.Sha1)
 			message := fmt.Sprintf(`<a href="%s">%s</a>`, url, c.Message)
-
-			if _, err = CreateComment(userId, issue.RepoId, issue.Id, 0, 0, COMMIT, message, nil); err != nil {
+			if _, err = CreateComment(userId, issue.RepoId, issue.Id, 0, 0, COMMENT_TYPE_COMMIT, message, nil); err != nil {
 				return err
 			}
 		}
 
-		closes := IssueCloseKeywordsPat.FindAllString(c.Message, -1)
-
-		for _, ref := range closes {
+		for _, ref := range IssueCloseKeywordsPat.FindAllString(c.Message, -1) {
 			ref := ref[strings.IndexByte(ref, byte(' '))+1:]
 			ref = strings.TrimRightFunc(ref, func(c rune) bool {
 				return !unicode.IsDigit(c)
@@ -173,7 +174,6 @@ func updateIssuesCommit(userId, repoId int64, repoUserName, repoName string, com
 			}
 
 			issue, err := GetIssueByRef(ref)
-
 			if err != nil {
 				return err
 			}
@@ -182,7 +182,6 @@ func updateIssuesCommit(userId, repoId int64, repoUserName, repoName string, com
 				if issue.IsClosed {
 					continue
 				}
-
 				issue.IsClosed = true
 
 				if err = UpdateIssue(issue); err != nil {
@@ -196,14 +195,60 @@ func updateIssuesCommit(userId, repoId int64, repoUserName, repoName string, com
 				}
 
 				// If commit happened in the referenced repository, it means the issue can be closed.
-				if _, err = CreateComment(userId, repoId, issue.Id, 0, 0, CLOSE, "", nil); err != nil {
+				if _, err = CreateComment(userId, repoId, issue.Id, 0, 0, COMMENT_TYPE_CLOSE, "", nil); err != nil {
 					return err
 				}
 			}
 		}
 
-	}
+		for _, ref := range IssueReopenKeywordsPat.FindAllString(c.Message, -1) {
+			ref := ref[strings.IndexByte(ref, byte(' '))+1:]
+			ref = strings.TrimRightFunc(ref, func(c rune) bool {
+				return !unicode.IsDigit(c)
+			})
 
+			if len(ref) == 0 {
+				continue
+			}
+
+			// Add repo name if missing
+			if ref[0] == '#' {
+				ref = fmt.Sprintf("%s/%s%s", repoUserName, repoName, ref)
+			} else if strings.Contains(ref, "/") == false {
+				// We don't support User#ID syntax yet
+				// return ErrNotImplemented
+
+				continue
+			}
+
+			issue, err := GetIssueByRef(ref)
+			if err != nil {
+				return err
+			}
+
+			if issue.RepoId == repoId {
+				if !issue.IsClosed {
+					continue
+				}
+				issue.IsClosed = false
+
+				if err = UpdateIssue(issue); err != nil {
+					return err
+				} else if err = UpdateIssueUserPairsByStatus(issue.Id, issue.IsClosed); err != nil {
+					return err
+				}
+
+				if err = ChangeMilestoneIssueStats(issue); err != nil {
+					return err
+				}
+
+				// If commit happened in the referenced repository, it means the issue can be closed.
+				if _, err = CreateComment(userId, repoId, issue.Id, 0, 0, COMMENT_TYPE_REOPEN, "", nil); err != nil {
+					return err
+				}
+			}
+		}
+	}
 	return nil
 }
 

+ 10 - 16
models/issue.go

@@ -859,22 +859,16 @@ type CommentType int
 
 const (
 	// Plain comment, can be associated with a commit (CommitId > 0) and a line (Line > 0)
-	COMMENT CommentType = iota
-
-	// Reopen action
-	REOPEN
-
-	// Close action
-	CLOSE
-
-	// Reference from another issue
-	ISSUE
+	COMMENT_TYPE_COMMENT CommentType = iota
+	COMMENT_TYPE_REOPEN
+	COMMENT_TYPE_CLOSE
 
+	// References.
+	COMMENT_TYPE_ISSUE
 	// Reference from some commit (not part of a pull request)
-	COMMIT
-
+	COMMENT_TYPE_COMMIT
 	// Reference from some pull request
-	PULL
+	COMMENT_TYPE_PULL
 )
 
 // Comment represents a comment in commit and issue page.
@@ -908,7 +902,7 @@ func CreateComment(userId, repoId, issueId, commitId, line int64, cmtType Commen
 
 	// Check comment type.
 	switch cmtType {
-	case COMMENT:
+	case COMMENT_TYPE_COMMENT:
 		rawSql := "UPDATE `issue` SET num_comments = num_comments + 1 WHERE id = ?"
 		if _, err := sess.Exec(rawSql, issueId); err != nil {
 			sess.Rollback()
@@ -929,13 +923,13 @@ func CreateComment(userId, repoId, issueId, commitId, line int64, cmtType Commen
 				return nil, err
 			}
 		}
-	case REOPEN:
+	case COMMENT_TYPE_REOPEN:
 		rawSql := "UPDATE `repository` SET num_closed_issues = num_closed_issues - 1 WHERE id = ?"
 		if _, err := sess.Exec(rawSql, repoId); err != nil {
 			sess.Rollback()
 			return nil, err
 		}
-	case CLOSE:
+	case COMMENT_TYPE_CLOSE:
 		rawSql := "UPDATE `repository` SET num_closed_issues = num_closed_issues + 1 WHERE id = ?"
 		if _, err := sess.Exec(rawSql, repoId); err != nil {
 			sess.Rollback()

+ 12 - 12
routers/repo/issue.go

@@ -424,7 +424,7 @@ func ViewIssue(ctx *middleware.Context) {
 		}
 		comments[i].Poster = u
 
-		if comments[i].Type == models.COMMENT {
+		if comments[i].Type == models.COMMENT_TYPE_COMMENT {
 			comments[i].Content = string(base.RenderMarkdown([]byte(comments[i].Content), ctx.Repo.RepoLink))
 		}
 	}
@@ -774,9 +774,9 @@ func Comment(ctx *middleware.Context) {
 				}
 			}
 
-			cmtType := models.CLOSE
+			cmtType := models.COMMENT_TYPE_CLOSE
 			if !issue.IsClosed {
-				cmtType = models.REOPEN
+				cmtType = models.COMMENT_TYPE_REOPEN
 			}
 
 			if _, err = models.CreateComment(ctx.User.Id, ctx.Repo.Repository.Id, issue.Id, 0, 0, cmtType, "", nil); err != nil {
@@ -795,7 +795,7 @@ func Comment(ctx *middleware.Context) {
 	if len(content) > 0 || len(ctx.Req.MultipartForm.File["attachments"]) > 0 {
 		switch ctx.Params(":action") {
 		case "new":
-			if comment, err = models.CreateComment(ctx.User.Id, ctx.Repo.Repository.Id, issue.Id, 0, 0, models.COMMENT, content, nil); err != nil {
+			if comment, err = models.CreateComment(ctx.User.Id, ctx.Repo.Repository.Id, issue.Id, 0, 0, models.COMMENT_TYPE_COMMENT, content, nil); err != nil {
 				send(500, nil, err)
 				return
 			}
@@ -1122,18 +1122,18 @@ func IssueGetAttachment(ctx *middleware.Context) {
 
 // testing route handler for new issue ui page
 // todo : move to Issue() function
-func Issues2(ctx *middleware.Context){
-	ctx.HTML(200,"repo/issue2/list")
+func Issues2(ctx *middleware.Context) {
+	ctx.HTML(200, "repo/issue2/list")
 }
 
-func PullRequest2(ctx *middleware.Context){
-	ctx.HTML(200,"repo/pr2/list")
+func PullRequest2(ctx *middleware.Context) {
+	ctx.HTML(200, "repo/pr2/list")
 }
 
-func Labels2(ctx *middleware.Context){
-	ctx.HTML(200,"repo/issue2/labels")
+func Labels2(ctx *middleware.Context) {
+	ctx.HTML(200, "repo/issue2/labels")
 }
 
-func Milestones2(ctx *middleware.Context){
-	ctx.HTML(200,"repo/milestone2/list")
+func Milestones2(ctx *middleware.Context) {
+	ctx.HTML(200, "repo/milestone2/list")
 }