| @@ -1,137 +0,0 @@ | |||
| commit c1a3d4fefbbbf332cd1cedda66e93bf40cc9713d | |||
| Author: Unknown <joe2010xtmf@163.com> | |||
| Date: Tue Mar 25 21:37:18 2014 -0400 | |||
| Add mail notify for creating issue | |||
| diff --git a/gogs.go b/gogs.go | |||
| index b62580f..f5a328a 100644 | |||
| --- a/gogs.go | |||
| +++ b/gogs.go | |||
| @@ -19,7 +19,7 @@ import ( | |||
| // Test that go1.2 tag above is included in builds. main.go refers to this definition. | |||
| const go12tag = true | |||
| -const APP_VER = "0.1.7.0325" | |||
| +const APP_VER = "0.1.8.0325" | |||
| func init() { | |||
| base.AppVer = APP_VER | |||
| diff --git a/models/issue.go b/models/issue.go | |||
| index 2bdd083..2de6568 100644 | |||
| --- a/models/issue.go | |||
| +++ b/models/issue.go | |||
| @@ -59,7 +59,6 @@ func CreateIssue(userId, repoId, milestoneId, assigneeId int64, name, labels, co | |||
| Content: content, | |||
| } | |||
| _, err = orm.Insert(issue) | |||
| - // TODO: newIssueAction | |||
| return issue, err | |||
| } | |||
| diff --git a/modules/mailer/mail.go b/modules/mailer/mail.go | |||
| index 92acd20..d0decbe 100644 | |||
| --- a/modules/mailer/mail.go | |||
| +++ b/modules/mailer/mail.go | |||
| @@ -6,6 +6,7 @@ package mailer | |||
| import ( | |||
| "encoding/hex" | |||
| + "errors" | |||
| "fmt" | |||
| "github.com/gogits/gogs/models" | |||
| @@ -15,12 +16,17 @@ import ( | |||
| ) | |||
| // Create New mail message use MailFrom and MailUser | |||
| -func NewMailMessage(To []string, subject, body string) Message { | |||
| - msg := NewHtmlMessage(To, base.MailService.User, subject, body) | |||
| +func NewMailMessageFrom(To []string, from, subject, body string) Message { | |||
| + msg := NewHtmlMessage(To, from, subject, body) | |||
| msg.User = base.MailService.User | |||
| return msg | |||
| } | |||
| +// Create New mail message use MailFrom and MailUser | |||
| +func NewMailMessage(To []string, subject, body string) Message { | |||
| + return NewMailMessageFrom(To, base.MailService.User, subject, body) | |||
| +} | |||
| + | |||
| func GetMailTmplData(user *models.User) map[interface{}]interface{} { | |||
| data := make(map[interface{}]interface{}, 10) | |||
| data["AppName"] = base.AppName | |||
| @@ -84,3 +90,33 @@ func SendActiveMail(r *middleware.Render, user *models.User) { | |||
| SendAsync(&msg) | |||
| } | |||
| + | |||
| +// SendNotifyMail sends mail notification of all watchers. | |||
| +func SendNotifyMail(userId, repoId int64, userName, repoName, subject, content string) error { | |||
| + watches, err := models.GetWatches(repoId) | |||
| + if err != nil { | |||
| + return errors.New("mail.NotifyWatchers(get watches): " + err.Error()) | |||
| + } | |||
| + | |||
| + tos := make([]string, 0, len(watches)) | |||
| + for i := range watches { | |||
| + uid := watches[i].UserId | |||
| + if userId == uid { | |||
| + continue | |||
| + } | |||
| + u, err := models.GetUserById(uid) | |||
| + if err != nil { | |||
| + return errors.New("mail.NotifyWatchers(get user): " + err.Error()) | |||
| + } | |||
| + tos = append(tos, u.Email) | |||
| + } | |||
| + | |||
| + if len(tos) == 0 { | |||
| + return nil | |||
| + } | |||
| + | |||
| + msg := NewMailMessageFrom(tos, userName, subject, content) | |||
| + msg.Info = fmt.Sprintf("Subject: %s, send notify emails", subject) | |||
| + SendAsync(&msg) | |||
| + return nil | |||
| +} | |||
| diff --git a/modules/mailer/mailer.go b/modules/mailer/mailer.go | |||
| index da63e01..63861d8 100644 | |||
| --- a/modules/mailer/mailer.go | |||
| +++ b/modules/mailer/mailer.go | |||
| @@ -33,7 +33,7 @@ func (m Message) Content() string { | |||
| } | |||
| // create mail content | |||
| - content := "From: " + m.User + "<" + m.From + | |||
| + content := "From: " + m.From + "<" + m.User + | |||
| ">\r\nSubject: " + m.Subject + "\r\nContent-Type: " + contentType + "\r\n\r\n" + m.Body | |||
| return content | |||
| } | |||
| diff --git a/routers/repo/issue.go b/routers/repo/issue.go | |||
| index fc5bb98..242593f 100644 | |||
| --- a/routers/repo/issue.go | |||
| +++ b/routers/repo/issue.go | |||
| @@ -13,6 +13,7 @@ import ( | |||
| "github.com/gogits/gogs/modules/auth" | |||
| "github.com/gogits/gogs/modules/base" | |||
| "github.com/gogits/gogs/modules/log" | |||
| + "github.com/gogits/gogs/modules/mailer" | |||
| "github.com/gogits/gogs/modules/middleware" | |||
| ) | |||
| @@ -86,6 +87,14 @@ func CreateIssue(ctx *middleware.Context, params martini.Params, form auth.Creat | |||
| return | |||
| } | |||
| + // Mail watchers. | |||
| + if base.Service.NotifyMail { | |||
| + if err = mailer.SendNotifyMail(ctx.User.Id, ctx.Repo.Repository.Id, ctx.User.Name, ctx.Repo.Repository.Name, issue.Name, issue.Content); err != nil { | |||
| + ctx.Handle(200, "issue.CreateIssue", err) | |||
| + return | |||
| + } | |||
| + } | |||
| + | |||
| log.Trace("%d Issue created: %d", ctx.Repo.Repository.Id, issue.Id) | |||
| ctx.Redirect(fmt.Sprintf("/%s/%s/issues/%d", params["username"], params["reponame"], issue.Index)) | |||
| } | |||
| @@ -15,6 +15,8 @@ import ( | |||
| "strings" | |||
| "github.com/gogits/git" | |||
| "github.com/gogits/gogs/modules/base" | |||
| ) | |||
| // RepoFile represents a file object in git repository. | |||
| @@ -249,6 +251,10 @@ type DiffLine struct { | |||
| Content string | |||
| } | |||
| func (d DiffLine) GetType() int { | |||
| return d.Type | |||
| } | |||
| type DiffSection struct { | |||
| Name string | |||
| Lines []*DiffLine | |||
| @@ -274,16 +280,20 @@ const DIFF_HEAD = "diff --git " | |||
| func ParsePatch(reader io.Reader) (*Diff, error) { | |||
| scanner := bufio.NewScanner(reader) | |||
| var curFile *DiffFile | |||
| curSection := &DiffSection{ | |||
| Lines: make([]*DiffLine, 0, 10), | |||
| } | |||
| //var leftLine, rightLine int | |||
| var ( | |||
| curFile *DiffFile | |||
| curSection = &DiffSection{ | |||
| Lines: make([]*DiffLine, 0, 10), | |||
| } | |||
| leftLine, rightLine int | |||
| ) | |||
| diff := &Diff{Files: make([]*DiffFile, 0)} | |||
| var i int | |||
| for scanner.Scan() { | |||
| line := scanner.Text() | |||
| fmt.Println(i, line) | |||
| // fmt.Println(i, line) | |||
| if strings.HasPrefix(line, "+++ ") || strings.HasPrefix(line, "--- ") { | |||
| continue | |||
| } | |||
| @@ -293,31 +303,37 @@ func ParsePatch(reader io.Reader) (*Diff, error) { | |||
| continue | |||
| } | |||
| if line[0] == ' ' { | |||
| diffLine := &DiffLine{Type: DIFF_LINE_PLAIN, Content: line} | |||
| diffLine := &DiffLine{Type: DIFF_LINE_PLAIN, Content: line, LeftIdx: leftLine, RightIdx: rightLine} | |||
| leftLine++ | |||
| rightLine++ | |||
| curSection.Lines = append(curSection.Lines, diffLine) | |||
| continue | |||
| } else if line[0] == '@' { | |||
| curSection = &DiffSection{} | |||
| curFile.Sections = append(curFile.Sections, curSection) | |||
| ss := strings.Split(line, "@@") | |||
| diffLine := &DiffLine{Type: DIFF_LINE_SECTION, Content: "@@" + ss[len(ss)-2] + "@@"} | |||
| diffLine := &DiffLine{Type: DIFF_LINE_SECTION, Content: line} | |||
| curSection.Lines = append(curSection.Lines, diffLine) | |||
| if len(ss[len(ss)-1]) > 0 { | |||
| diffLine = &DiffLine{Type: DIFF_LINE_PLAIN, Content: ss[len(ss)-1]} | |||
| curSection.Lines = append(curSection.Lines, diffLine) | |||
| } | |||
| // Parse line number. | |||
| ranges := strings.Split(ss[len(ss)-2][1:], " ") | |||
| leftLine, _ = base.StrTo(strings.Split(ranges[0], ",")[0][1:]).Int() | |||
| rightLine, _ = base.StrTo(strings.Split(ranges[1], ",")[0]).Int() | |||
| continue | |||
| } else if line[0] == '+' { | |||
| curFile.Addition++ | |||
| diff.TotalAddition++ | |||
| diffLine := &DiffLine{Type: DIFF_LINE_ADD, Content: line} | |||
| diffLine := &DiffLine{Type: DIFF_LINE_ADD, Content: line, RightIdx: rightLine} | |||
| rightLine++ | |||
| curSection.Lines = append(curSection.Lines, diffLine) | |||
| continue | |||
| } else if line[0] == '-' { | |||
| curFile.Deletion++ | |||
| diff.TotalDeletion++ | |||
| diffLine := &DiffLine{Type: DIFF_LINE_DEL, Content: line} | |||
| diffLine := &DiffLine{Type: DIFF_LINE_DEL, Content: line, LeftIdx: leftLine} | |||
| if leftLine > 0 { | |||
| leftLine++ | |||
| } | |||
| curSection.Lines = append(curSection.Lines, diffLine) | |||
| continue | |||
| } | |||
| @@ -365,8 +381,20 @@ func GetDiff(repoPath, commitid string) (*Diff, error) { | |||
| return nil, err | |||
| } | |||
| // First commit of repository. | |||
| if commit.ParentCount() == 0 { | |||
| return &Diff{}, err | |||
| rd, wr := io.Pipe() | |||
| go func() { | |||
| cmd := exec.Command("git", "show", commitid) | |||
| cmd.Dir = repoPath | |||
| cmd.Stdout = wr | |||
| cmd.Stdin = os.Stdin | |||
| cmd.Stderr = os.Stderr | |||
| cmd.Run() | |||
| wr.Close() | |||
| }() | |||
| defer rd.Close() | |||
| return ParsePatch(rd) | |||
| } | |||
| rd, wr := io.Pipe() | |||
| @@ -377,47 +405,8 @@ func GetDiff(repoPath, commitid string) (*Diff, error) { | |||
| cmd.Stdin = os.Stdin | |||
| cmd.Stderr = os.Stderr | |||
| cmd.Run() | |||
| //if err != nil { | |||
| // return nil, err | |||
| //} | |||
| wr.Close() | |||
| }() | |||
| defer rd.Close() | |||
| return ParsePatch(rd) | |||
| } | |||
| /*func GetDiff(repoPath, commitid string) (*Diff, error) { | |||
| stdout, _, err := com.ExecCmdDir(repoPath, "git", "show", commitid) | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| // Sperate parts by file. | |||
| startIndex := strings.Index(stdout, "diff --git ") + 12 | |||
| // First part is commit information. | |||
| // Check if it's a merge. | |||
| mergeIndex := strings.Index(stdout[:startIndex], "merge") | |||
| if mergeIndex > -1 { | |||
| mergeCommit := strings.SplitN(strings.Split(stdout[:startIndex], "\n")[1], "", 3)[2] | |||
| return GetDiff(repoPath, mergeCommit) | |||
| } | |||
| parts := strings.Split(stdout[startIndex:], "diff --git ") | |||
| diff := &Diff{NumFiles: len(parts)} | |||
| diff.Files = make([]*DiffFile, 0, diff.NumFiles) | |||
| for _, part := range parts { | |||
| infos := strings.SplitN(part, "\n", 6) | |||
| maxIndex := len(infos) - 1 | |||
| infos[maxIndex] = strings.TrimSuffix(strings.TrimSuffix(infos[maxIndex], "\n"), "\n\\ No newline at end of file") | |||
| file := &DiffFile{ | |||
| Name: strings.TrimPrefix(strings.Split(infos[0], " ")[0], "a/"), | |||
| Content: strings.Split(infos[maxIndex], "\n"), | |||
| } | |||
| diff.Files = append(diff.Files, file) | |||
| } | |||
| return diff, nil | |||
| }*/ | |||
| @@ -173,14 +173,23 @@ type Comment struct { | |||
| // CreateComment creates comment of issue or commit. | |||
| func CreateComment(userId, issueId, commitId, line int64, content string) error { | |||
| _, err := orm.Insert(&Comment{ | |||
| PosterId: userId, | |||
| IssueId: issueId, | |||
| CommitId: commitId, | |||
| Line: line, | |||
| Content: content, | |||
| }) | |||
| return err | |||
| sess := orm.NewSession() | |||
| defer sess.Close() | |||
| sess.Begin() | |||
| if _, err := orm.Insert(&Comment{PosterId: userId, IssueId: issueId, | |||
| CommitId: commitId, Line: line, Content: content, | |||
| }); err != nil { | |||
| sess.Rollback() | |||
| return err | |||
| } | |||
| rawSql := "UPDATE `issue` SET num_comments = num_comments + 1 WHERE id = ?" | |||
| if _, err := sess.Exec(rawSql, issueId); err != nil { | |||
| sess.Rollback() | |||
| return err | |||
| } | |||
| return sess.Commit() | |||
| } | |||
| // GetIssueComments returns list of comment by given issue id. | |||
| @@ -70,5 +70,6 @@ var TemplateFuncs template.FuncMap = map[string]interface{}{ | |||
| "SubStr": func(str string, start, length int) string { | |||
| return str[start : start+length] | |||
| }, | |||
| "DiffTypeToStr": DiffTypeToStr, | |||
| "DiffTypeToStr": DiffTypeToStr, | |||
| "DiffLineTypeToStr": DiffLineTypeToStr, | |||
| } | |||
| @@ -552,3 +552,15 @@ func DiffTypeToStr(diffType int) string { | |||
| return "unknown" | |||
| } | |||
| } | |||
| func DiffLineTypeToStr(diffType int) string { | |||
| switch diffType { | |||
| case 2: | |||
| return "add" | |||
| case 3: | |||
| return "del" | |||
| case 4: | |||
| return "tag" | |||
| } | |||
| return "same" | |||
| } | |||
| @@ -980,6 +980,11 @@ html, body { | |||
| padding: 20px; | |||
| } | |||
| .diff-file-box .code-diff tbody tr.tag-code td, .diff-file-box .code-diff tbody tr.tag-code pre { | |||
| background-color: #E0E0E0 !important; | |||
| border-color: #ADADAD !important; | |||
| } | |||
| .diff-file-box .code-diff tbody tr.add-code td, .diff-file-box .code-diff tbody tr.add-code pre { | |||
| background-color: #d1ffd6 !important; | |||
| border-color: #b4e2b4 !important; | |||
| @@ -71,7 +71,6 @@ func Single(ctx *middleware.Context, params martini.Params) { | |||
| // Branches. | |||
| brs, err := models.GetBranches(params["username"], params["reponame"]) | |||
| if err != nil { | |||
| //log.Error("repo.Single(GetBranches): %v", err) | |||
| ctx.Handle(404, "repo.Single(GetBranches)", err) | |||
| return | |||
| } else if ctx.Repo.Repository.IsBare { | |||
| @@ -79,14 +78,11 @@ func Single(ctx *middleware.Context, params martini.Params) { | |||
| ctx.HTML(200, "repo/single") | |||
| return | |||
| } | |||
| ctx.Data["Branches"] = brs | |||
| repoFile, err := models.GetTargetFile(params["username"], params["reponame"], | |||
| params["branchname"], params["commitid"], treename) | |||
| if err != nil && err != models.ErrRepoFileNotExist { | |||
| //log.Error("repo.Single(GetTargetFile): %v", err) | |||
| ctx.Handle(404, "repo.Single(GetTargetFile)", err) | |||
| return | |||
| } | |||
| @@ -133,7 +129,6 @@ func Single(ctx *middleware.Context, params martini.Params) { | |||
| files, err := models.GetReposFiles(params["username"], params["reponame"], | |||
| params["branchname"], params["commitid"], treename) | |||
| if err != nil { | |||
| //log.Error("repo.Single(GetReposFiles): %v", err) | |||
| ctx.Handle(404, "repo.Single(GetReposFiles)", err) | |||
| return | |||
| } | |||
| @@ -189,7 +184,7 @@ func Single(ctx *middleware.Context, params martini.Params) { | |||
| } | |||
| } | |||
| // Get latest commit according username and repo name | |||
| // Get latest commit according username and repo name. | |||
| commit, err := models.GetCommit(params["username"], params["reponame"], | |||
| params["branchname"], params["commitid"]) | |||
| if err != nil { | |||
| @@ -65,12 +65,12 @@ | |||
| <tbody> | |||
| {{range .Sections}} | |||
| {{range .Lines}} | |||
| <tr class="same-code nl-1 ol-1"> | |||
| <tr class="{{DiffLineTypeToStr .Type}}-code nl-1 ol-1"> | |||
| <td class="lines-num lines-num-old"> | |||
| <span rel="L1"></span> | |||
| <span rel="L1">{{if .LeftIdx}}{{.LeftIdx}}{{end}}</span> | |||
| </td> | |||
| <td class="lines-num lines-num-new"> | |||
| <span rel="L1"></span> | |||
| <span rel="L1">{{if .RightIdx}}{{.RightIdx}}{{end}}</span> | |||
| </td> | |||
| <td class="lines-code"> | |||
| <pre>{{.Content}}</pre> | |||
| @@ -159,7 +159,6 @@ func runWeb(*cli.Context) { | |||
| r.Get("/commits/:branchname", repo.Commits) | |||
| }, ignSignIn, middleware.RepoAssignment(true)) | |||
| // TODO: implement single commit page | |||
| m.Get("/:username/:reponame/commit/:commitid/**", ignSignIn, middleware.RepoAssignment(true), repo.Diff) | |||
| m.Get("/:username/:reponame/commit/:commitid", ignSignIn, middleware.RepoAssignment(true), repo.Diff) | |||