aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--conf/locale/TRANSLATORS1
-rw-r--r--gogs.go2
-rw-r--r--models/action.go95
-rw-r--r--models/issue.go26
-rw-r--r--modules/base/markdown.go8
-rw-r--r--public/ng/css/gogs.css3
-rw-r--r--public/ng/less/gogs/markdown.less167
-rw-r--r--routers/repo/issue.go24
-rw-r--r--routers/repo/view.go12
-rw-r--r--templates/.VERSION2
10 files changed, 188 insertions, 152 deletions
diff --git a/conf/locale/TRANSLATORS b/conf/locale/TRANSLATORS
index 6c72f334..4cd8bf6a 100644
--- a/conf/locale/TRANSLATORS
+++ b/conf/locale/TRANSLATORS
@@ -3,6 +3,7 @@
Akihiro YAGASAKI <yaggytter@momiage.com>
Christoph Kisfeld <christoph.kisfeld@gmail.com>
+Huimin Wang <wanghm2009@hotmail.co.jp>
Thomas Fanninger <gogs.thomas@fanninger.at>
Ɓukasz Jan Niemier <lukasz@niemier.pl>
Lafriks <lafriks@gmail.com>
diff --git a/gogs.go b/gogs.go
index 5e69bd68..ce524af5 100644
--- a/gogs.go
+++ b/gogs.go
@@ -17,7 +17,7 @@ import (
"github.com/gogits/gogs/modules/setting"
)
-const APP_VER = "0.5.12.0204 Beta"
+const APP_VER = "0.5.12.0206 Beta"
func init() {
runtime.GOMAXPROCS(runtime.NumCPU())
diff --git a/models/action.go b/models/action.go
index 318a5f6a..34f543c4 100644
--- a/models/action.go
+++ b/models/action.go
@@ -41,14 +41,21 @@ 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, "|")))
- IssueReferenceKeywordsPat = regexp.MustCompile(fmt.Sprintf(`(?i)(?:) \S+`))
+ IssueCloseKeywordsPat = regexp.MustCompile(assembleKeywordsPattern(IssueCloseKeywords))
+ IssueReopenKeywordsPat = regexp.MustCompile(assembleKeywordsPattern(IssueReopenKeywords))
+ IssueReferenceKeywordsPat = regexp.MustCompile(`(?i)(?:)(^| )\S+`)
}
// Action represents user operation type and other information to repository.,
@@ -112,13 +119,13 @@ 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)
-
- for _, ref := range references {
+ // FIXME: should not be a reference when it comes with action.
+ // e.g. fixes #1 will not have duplicated reference message.
+ 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)
- })
+ return !unicode.IsDigit(c)
+ })
if len(ref) == 0 {
continue
@@ -128,33 +135,29 @@ func updateIssuesCommit(userId, repoId int64, repoUserName, repoName string, com
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
+ // FIXME: We don't support User#ID syntax yet
// return ErrNotImplemented
continue
}
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)
- })
+ return !unicode.IsDigit(c)
+ })
if len(ref) == 0 {
continue
@@ -171,7 +174,6 @@ func updateIssuesCommit(userId, repoId int64, repoUserName, repoName string, com
}
issue, err := GetIssueByRef(ref)
-
if err != nil {
return err
}
@@ -180,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 {
@@ -194,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
}
diff --git a/models/issue.go b/models/issue.go
index c756e497..d9a24063 100644
--- a/models/issue.go
+++ b/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()
diff --git a/modules/base/markdown.go b/modules/base/markdown.go
index d3f3e5fe..b5f397dc 100644
--- a/modules/base/markdown.go
+++ b/modules/base/markdown.go
@@ -106,7 +106,7 @@ func (options *CustomRender) Image(out *bytes.Buffer, link []byte, title []byte,
}
var (
- MentionPattern = regexp.MustCompile(`@[0-9a-zA-Z_]{1,}`)
+ MentionPattern = regexp.MustCompile(`((^|\s)@)[0-9a-zA-Z_]{1,}`)
commitPattern = regexp.MustCompile(`(\s|^)https?.*commit/[0-9a-zA-Z]+(#+[0-9a-zA-Z-]*)?`)
issueFullPattern = regexp.MustCompile(`(\s|^)https?.*issues/[0-9]+(#+[0-9a-zA-Z-]*)?`)
issueIndexPattern = regexp.MustCompile(`( |^)#[0-9]+`)
@@ -129,7 +129,7 @@ func RenderSpecialLink(rawBytes []byte, urlPrefix string) []byte {
ms := MentionPattern.FindAll(line, -1)
for _, m := range ms {
line = bytes.Replace(line, m,
- []byte(fmt.Sprintf(`<a href="%s/user/%s">%s</a>`, setting.AppSubUrl, m[1:], m)), -1)
+ []byte(fmt.Sprintf(`<a href="%s/%s">%s</a>`, setting.AppSubUrl, m[2:], m)), -1)
}
}
@@ -177,8 +177,8 @@ func RenderSha1CurrentPattern(rawBytes []byte, urlPrefix string) []byte {
func RenderIssueIndexPattern(rawBytes []byte, urlPrefix string) []byte {
ms := issueIndexPattern.FindAll(rawBytes, -1)
for _, m := range ms {
- rawBytes = bytes.Replace(rawBytes, m, []byte(fmt.Sprintf(
- `<a href="%s/issues/%s">%s</a>`, urlPrefix, m[1:], m)), -1)
+ rawBytes = bytes.Replace(rawBytes, m, []byte(fmt.Sprintf(`<a href="%s/issues/%s">%s</a>`,
+ urlPrefix, strings.TrimPrefix(string(m[1:]), "#"), m)), -1)
}
return rawBytes
}
diff --git a/public/ng/css/gogs.css b/public/ng/css/gogs.css
index 9a444052..1db8ee69 100644
--- a/public/ng/css/gogs.css
+++ b/public/ng/css/gogs.css
@@ -364,6 +364,9 @@ img.avatar-100 {
.markdown table tr:nth-child(2n) {
background-color: #F8F8F8;
}
+.markdown p {
+ margin: 20px 0;
+}
.markdown a {
color: #428BCA;
}
diff --git a/public/ng/less/gogs/markdown.less b/public/ng/less/gogs/markdown.less
index e3abb480..cd2eef99 100644
--- a/public/ng/less/gogs/markdown.less
+++ b/public/ng/less/gogs/markdown.less
@@ -1,88 +1,91 @@
.markdown {
- background-color: white;
- font-size: 16px;
- line-height: 24px;
- .markdown-body {
- padding-left: 24px;
- padding-right: 16px;
- }
- h5,
- h6 {
- font-size: 1em;
- }
- ul {
- padding: 10px 0 0 15px;
- li {
- list-style: inside;
- }
- }
- ol li {
- list-style: decimal inside;
- }
+ background-color: white;
+ font-size: 16px;
+ line-height: 24px;
+ .markdown-body {
+ padding-left: 24px;
+ padding-right: 16px;
+ }
+ h5,
+ h6 {
+ font-size: 1em;
+ }
+ ul {
+ padding: 10px 0 0 15px;
li {
- line-height: 1.6;
- margin-top: 6px;
- &:first-child {
- margin-top: 0;
- }
- }
+ list-style: inside;
+ }
+ }
+ ol li {
+ list-style: decimal inside;
+ }
+ li {
+ line-height: 1.6;
+ margin-top: 6px;
+ &:first-child {
+ margin-top: 0;
+ }
+ }
+ code {
+ padding: 0.2em 0.5em;
+ margin: 0;
+ background-color: rgba(0,0,0,0.04);
+ border-radius: 3px;
+ }
+ >pre {
+ font-size: 14px;
+ line-height: 1.6;
+ overflow: auto;
+ border: 1px solid #ddd;
+ border-radius: .25em;
+ margin: 5px 0;
+ padding: 10px;
+ background-color: #f8f8f8;
code {
- padding: 0.2em 0.5em;
- margin: 0;
- background-color: rgba(0,0,0,0.04);
- border-radius: 3px;
- }
- >pre {
- font-size: 14px;
- line-height: 1.6;
- overflow: auto;
- border: 1px solid #ddd;
- border-radius: .25em;
- margin: 5px 0;
- padding: 10px;
- background-color: #f8f8f8;
- code {
- padding: 0;
- background-color: inherit;
- }
- }
- img {
- padding: 10px 0;
- max-width: 100%;
- }
- blockquote {
- border-left: 4px solid #ddd;
- margin-bottom: 16px;
- p {
- font-size: 14px;
- padding: 5px 15px;
- color: #777;
- }
- }
- table {
- display: block;
- width: 100%;
- overflow: auto;
- word-break: normal;
- margin: 15px 0;
- border-collapse: collapse;
- border-spacing: 0;
- display: block;
- th {
- font-weight: 700;
- }
- th, td {
- border: 1px solid #DDD;
- padding: 6px 13px !important;
- }
- tr {
- background-color: #FFF;
- border-top: 1px solid #CCC;
- &:nth-child(2n) {
- background-color: #F8F8F8;
- }
- }
- }
+ padding: 0;
+ background-color: inherit;
+ }
+ }
+ img {
+ padding: 10px 0;
+ max-width: 100%;
+ }
+ blockquote {
+ border-left: 4px solid #ddd;
+ margin-bottom: 16px;
+ p {
+ font-size: 14px;
+ padding: 5px 15px;
+ color: #777;
+ }
+ }
+ table {
+ display: block;
+ width: 100%;
+ overflow: auto;
+ word-break: normal;
+ margin: 15px 0;
+ border-collapse: collapse;
+ border-spacing: 0;
+ display: block;
+ th {
+ font-weight: 700;
+ }
+ th, td {
+ border: 1px solid #DDD;
+ padding: 6px 13px !important;
+ }
+ tr {
+ background-color: #FFF;
+ border-top: 1px solid #CCC;
+ &:nth-child(2n) {
+ background-color: #F8F8F8;
+ }
+ }
+ }
+ p {
+ margin: 20px 0;
+ }
}
.markdown a {
color: #428BCA;
diff --git a/routers/repo/issue.go b/routers/repo/issue.go
index 999fd0a8..3e0206da 100644
--- a/routers/repo/issue.go
+++ b/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")
}
diff --git a/routers/repo/view.go b/routers/repo/view.go
index cb689df6..cfe0fa01 100644
--- a/routers/repo/view.go
+++ b/routers/repo/view.go
@@ -127,7 +127,6 @@ func Home(ctx *middleware.Context) {
entries.Sort()
files := make([][]interface{}, 0, len(entries))
-
for _, te := range entries {
if te.Type != git.COMMIT {
c, err := ctx.Repo.Commit.GetCommitOfRelPath(filepath.Join(treePath, te.Name()))
@@ -151,16 +150,6 @@ func Home(ctx *middleware.Context) {
files = append(files, []interface{}{te, git.NewSubModuleFile(c, sm.Url, te.Id.String())})
}
}
-
- // Render issue index links.
- for _, f := range files {
- switch c := f[1].(type) {
- case *git.Commit:
- c.CommitMessage = c.CommitMessage
- case *git.SubModuleFile:
- c.CommitMessage = c.CommitMessage
- }
- }
ctx.Data["Files"] = files
var readmeFile *git.Blob
@@ -208,7 +197,6 @@ func Home(ctx *middleware.Context) {
}
lastCommit := ctx.Repo.Commit
- lastCommit.CommitMessage = string(base.RenderIssueIndexPattern([]byte(lastCommit.CommitMessage), ctx.Repo.RepoLink))
if len(treePath) > 0 {
c, err := ctx.Repo.Commit.GetCommitOfRelPath(treePath)
if err != nil {
diff --git a/templates/.VERSION b/templates/.VERSION
index 40246b9e..99de7de7 100644
--- a/templates/.VERSION
+++ b/templates/.VERSION
@@ -1 +1 @@
-0.5.12.0204 Beta \ No newline at end of file
+0.5.12.0206 Beta \ No newline at end of file