diff --git a/cmd/web.go b/cmd/web.go index c39675ae1..9979929c2 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -37,6 +37,7 @@ import ( "github.com/gogits/gogs/modules/bindata" "github.com/gogits/gogs/modules/context" "github.com/gogits/gogs/modules/log" + "github.com/gogits/gogs/modules/mailer" "github.com/gogits/gogs/modules/setting" "github.com/gogits/gogs/modules/template" "github.com/gogits/gogs/routers" @@ -140,7 +141,7 @@ func newMacaron() *macaron.Macaron { Funcs: funcMap, IndentJSON: macaron.Env != macaron.PROD, })) - models.InitMailRender(path.Join(setting.StaticRootPath, "templates/mail"), + mailer.InitMailRender(path.Join(setting.StaticRootPath, "templates/mail"), path.Join(setting.CustomPath, "templates/mail"), funcMap) localeNames, err := bindata.AssetDir("conf/locale") diff --git a/gogs.go b/gogs.go index 525f1243a..3eb1731e4 100644 --- a/gogs.go +++ b/gogs.go @@ -16,7 +16,7 @@ import ( "github.com/gogits/gogs/modules/setting" ) -const APP_VER = "0.9.126.0129" +const APP_VER = "0.9.127.0130" func init() { setting.AppVer = APP_VER diff --git a/models/issue_mail.go b/models/issue_mail.go index c10eec46b..bdaec71b2 100644 --- a/models/issue_mail.go +++ b/models/issue_mail.go @@ -10,6 +10,7 @@ import ( "github.com/Unknwon/com" "github.com/gogits/gogs/modules/log" + "github.com/gogits/gogs/modules/mailer" "github.com/gogits/gogs/modules/markdown" "github.com/gogits/gogs/modules/setting" ) @@ -18,6 +19,77 @@ func (issue *Issue) MailSubject() string { return fmt.Sprintf("[%s] %s (#%d)", issue.Repo.Name, issue.Title, issue.Index) } +// mailerUser is a wrapper for satisfying mailer.User interface. +type mailerUser struct { + user *User +} + +func (this mailerUser) ID() int64 { + return this.user.ID +} + +func (this mailerUser) DisplayName() string { + return this.user.DisplayName() +} + +func (this mailerUser) Email() string { + return this.user.Email +} + +func (this mailerUser) GenerateActivateCode() string { + return this.user.GenerateActivateCode() +} + +func (this mailerUser) GenerateEmailActivateCode(email string) string { + return this.user.GenerateEmailActivateCode(email) +} + +func NewMailerUser(u *User) mailer.User { + return mailerUser{u} +} + +// mailerRepo is a wrapper for satisfying mailer.Repository interface. +type mailerRepo struct { + repo *Repository +} + +func (this mailerRepo) FullName() string { + return this.repo.FullName() +} + +func (this mailerRepo) HTMLURL() string { + return this.repo.HTMLURL() +} + +func (this mailerRepo) ComposeMetas() map[string]string { + return this.repo.ComposeMetas() +} + +func NewMailerRepo(repo *Repository) mailer.Repository { + return mailerRepo{repo} +} + +// mailerIssue is a wrapper for satisfying mailer.Issue interface. +type mailerIssue struct { + issue *Issue +} + +func (this mailerIssue) MailSubject() string { + return this.issue.MailSubject() +} + +func (this mailerIssue) Content() string { + return this.issue.Content +} + +func (this mailerIssue) HTMLURL() string { + return this.issue.HTMLURL() +} + +func NewMailerIssue(issue *Issue) mailer.Issue { + return mailerIssue{issue} +} + // mailIssueCommentToParticipants can be used for both new issue creation and comment. func mailIssueCommentToParticipants(issue *Issue, doer *User, mentions []string) error { if !setting.Service.EnableNotifyMail { @@ -48,7 +120,7 @@ func mailIssueCommentToParticipants(issue *Issue, doer *User, mentions []string) tos = append(tos, to.Email) names = append(names, to.Name) } - SendIssueCommentMail(issue, doer, tos) + mailer.SendIssueCommentMail(NewMailerIssue(issue), NewMailerRepo(issue.Repo), NewMailerUser(doer), tos) // Mail mentioned people and exclude watchers. names = append(names, doer.Name) @@ -60,7 +132,7 @@ func mailIssueCommentToParticipants(issue *Issue, doer *User, mentions []string) tos = append(tos, mentions[i]) } - SendIssueMentionMail(issue, doer, GetUserEmailsByNames(tos)) + mailer.SendIssueMentionMail(NewMailerIssue(issue), NewMailerRepo(issue.Repo), NewMailerUser(doer), GetUserEmailsByNames(tos)) return nil } diff --git a/models/mail.go b/modules/mailer/mail.go similarity index 63% rename from models/mail.go rename to modules/mailer/mail.go index 0b3f76f5f..ed2f43736 100644 --- a/models/mail.go +++ b/modules/mailer/mail.go @@ -2,19 +2,17 @@ // Use of this source code is governed by a MIT-style // license that can be found in the LICENSE file. -package models +package mailer import ( "fmt" "html/template" - "path" "gopkg.in/gomail.v2" "gopkg.in/macaron.v1" "github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/log" - "github.com/gogits/gogs/modules/mailer" "github.com/gogits/gogs/modules/markdown" "github.com/gogits/gogs/modules/setting" ) @@ -54,10 +52,34 @@ func InitMailRender(dir, appendDir string, funcMap []template.FuncMap) { } func SendTestMail(email string) error { - return gomail.Send(&mailer.Sender{}, mailer.NewMessage([]string{email}, "Gogs Test Email!", "Gogs Test Email!").Message) + return gomail.Send(&Sender{}, NewMessage([]string{email}, "Gogs Test Email!", "Gogs Test Email!").Message) } -func SendUserMail(c *macaron.Context, u *User, tpl base.TplName, code, subject, info string) { +/* + Setup interfaces of used methods in mail to avoid cycle import. +*/ + +type User interface { + ID() int64 + DisplayName() string + Email() string + GenerateActivateCode() string + GenerateEmailActivateCode(string) string +} + +type Repository interface { + FullName() string + HTMLURL() string + ComposeMetas() map[string]string +} + +type Issue interface { + MailSubject() string + Content() string + HTMLURL() string +} + +func SendUserMail(c *macaron.Context, u User, tpl base.TplName, code, subject, info string) { data := map[string]interface{}{ "Username": u.DisplayName(), "ActiveCodeLives": setting.Service.ActiveCodeLives / 60, @@ -70,27 +92,27 @@ func SendUserMail(c *macaron.Context, u *User, tpl base.TplName, code, subject, return } - msg := mailer.NewMessage([]string{u.Email}, subject, body) - msg.Info = fmt.Sprintf("UID: %d, %s", u.ID, info) + msg := NewMessage([]string{u.Email()}, subject, body) + msg.Info = fmt.Sprintf("UID: %d, %s", u.ID(), info) - mailer.SendAsync(msg) + SendAsync(msg) } -func SendActivateAccountMail(c *macaron.Context, u *User) { +func SendActivateAccountMail(c *macaron.Context, u User) { SendUserMail(c, u, MAIL_AUTH_ACTIVATE, u.GenerateActivateCode(), c.Tr("mail.activate_account"), "activate account") } -func SendResetPasswordMail(c *macaron.Context, u *User) { +func SendResetPasswordMail(c *macaron.Context, u User) { SendUserMail(c, u, MAIL_AUTH_RESET_PASSWORD, u.GenerateActivateCode(), c.Tr("mail.reset_password"), "reset password") } // SendActivateAccountMail sends confirmation email. -func SendActivateEmailMail(c *macaron.Context, u *User, email *EmailAddress) { +func SendActivateEmailMail(c *macaron.Context, u User, email string) { data := map[string]interface{}{ "Username": u.DisplayName(), "ActiveCodeLives": setting.Service.ActiveCodeLives / 60, - "Code": u.GenerateEmailActivateCode(email.Email), - "Email": email.Email, + "Code": u.GenerateEmailActivateCode(email), + "Email": email, } body, err := mailRender.HTMLString(string(MAIL_AUTH_ACTIVATE_EMAIL), data) if err != nil { @@ -98,14 +120,14 @@ func SendActivateEmailMail(c *macaron.Context, u *User, email *EmailAddress) { return } - msg := mailer.NewMessage([]string{email.Email}, c.Tr("mail.activate_email"), body) - msg.Info = fmt.Sprintf("UID: %d, activate email", u.ID) + msg := NewMessage([]string{email}, c.Tr("mail.activate_email"), body) + msg.Info = fmt.Sprintf("UID: %d, activate email", u.ID()) - mailer.SendAsync(msg) + SendAsync(msg) } // SendRegisterNotifyMail triggers a notify e-mail by admin created a account. -func SendRegisterNotifyMail(c *macaron.Context, u *User) { +func SendRegisterNotifyMail(c *macaron.Context, u User) { data := map[string]interface{}{ "Username": u.DisplayName(), } @@ -115,20 +137,19 @@ func SendRegisterNotifyMail(c *macaron.Context, u *User) { return } - msg := mailer.NewMessage([]string{u.Email}, c.Tr("mail.register_notify"), body) - msg.Info = fmt.Sprintf("UID: %d, registration notify", u.ID) + msg := NewMessage([]string{u.Email()}, c.Tr("mail.register_notify"), body) + msg.Info = fmt.Sprintf("UID: %d, registration notify", u.ID()) - mailer.SendAsync(msg) + SendAsync(msg) } // SendCollaboratorMail sends mail notification to new collaborator. -func SendCollaboratorMail(u, doer *User, repo *Repository) { - repoName := path.Join(repo.Owner.Name, repo.Name) - subject := fmt.Sprintf("%s added you to %s", doer.DisplayName(), repoName) +func SendCollaboratorMail(u, doer User, repo Repository) { + subject := fmt.Sprintf("%s added you to %s", doer.DisplayName(), repo.FullName()) data := map[string]interface{}{ "Subject": subject, - "RepoName": repoName, + "RepoName": repo.FullName(), "Link": repo.HTMLURL(), } body, err := mailRender.HTMLString(string(MAIL_NOTIFY_COLLABORATOR), data) @@ -137,10 +158,10 @@ func SendCollaboratorMail(u, doer *User, repo *Repository) { return } - msg := mailer.NewMessage([]string{u.Email}, subject, body) - msg.Info = fmt.Sprintf("UID: %d, add collaborator", u.ID) + msg := NewMessage([]string{u.Email()}, subject, body) + msg.Info = fmt.Sprintf("UID: %d, add collaborator", u.ID()) - mailer.SendAsync(msg) + SendAsync(msg) } func composeTplData(subject, body, link string) map[string]interface{} { @@ -151,9 +172,9 @@ func composeTplData(subject, body, link string) map[string]interface{} { return data } -func composeIssueMessage(issue *Issue, doer *User, tplName base.TplName, tos []string, info string) *mailer.Message { +func composeIssueMessage(issue Issue, repo Repository, doer User, tplName base.TplName, tos []string, info string) *Message { subject := issue.MailSubject() - body := string(markdown.RenderSpecialLink([]byte(issue.Content), issue.Repo.HTMLURL(), issue.Repo.ComposeMetas())) + body := string(markdown.RenderSpecialLink([]byte(issue.Content()), repo.HTMLURL(), repo.ComposeMetas())) data := composeTplData(subject, body, issue.HTMLURL()) data["Doer"] = doer content, err := mailRender.HTMLString(string(tplName), data) @@ -161,24 +182,24 @@ func composeIssueMessage(issue *Issue, doer *User, tplName base.TplName, tos []s log.Error(3, "HTMLString (%s): %v", tplName, err) } from := gomail.NewMessage().FormatAddress(setting.MailService.FromEmail, doer.DisplayName()) - msg := mailer.NewMessageFrom(tos, from, subject, content) + msg := NewMessageFrom(tos, from, subject, content) msg.Info = fmt.Sprintf("Subject: %s, %s", subject, info) return msg } // SendIssueCommentMail composes and sends issue comment emails to target receivers. -func SendIssueCommentMail(issue *Issue, doer *User, tos []string) { +func SendIssueCommentMail(issue Issue, repo Repository, doer User, tos []string) { if len(tos) == 0 { return } - mailer.SendAsync(composeIssueMessage(issue, doer, MAIL_ISSUE_COMMENT, tos, "issue comment")) + SendAsync(composeIssueMessage(issue, repo, doer, MAIL_ISSUE_COMMENT, tos, "issue comment")) } // SendIssueMentionMail composes and sends issue mention emails to target receivers. -func SendIssueMentionMail(issue *Issue, doer *User, tos []string) { +func SendIssueMentionMail(issue Issue, repo Repository, doer User, tos []string) { if len(tos) == 0 { return } - mailer.SendAsync(composeIssueMessage(issue, doer, MAIL_ISSUE_MENTION, tos, "issue mention")) + SendAsync(composeIssueMessage(issue, repo, doer, MAIL_ISSUE_MENTION, tos, "issue mention")) } diff --git a/routers/admin/admin.go b/routers/admin/admin.go index 1b22251e5..e0eb92561 100644 --- a/routers/admin/admin.go +++ b/routers/admin/admin.go @@ -17,6 +17,7 @@ import ( "github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/context" "github.com/gogits/gogs/modules/cron" + "github.com/gogits/gogs/modules/mailer" "github.com/gogits/gogs/modules/process" "github.com/gogits/gogs/modules/setting" ) @@ -177,7 +178,7 @@ func Dashboard(ctx *context.Context) { func SendTestMail(ctx *context.Context) { email := ctx.Query("email") // Send a test email to the user's email address and redirect back to Config - if err := models.SendTestMail(email); err != nil { + if err := mailer.SendTestMail(email); err != nil { ctx.Flash.Error(ctx.Tr("admin.config.test_mail_failed", email, err)) } else { ctx.Flash.Info(ctx.Tr("admin.config.test_mail_sent", email)) diff --git a/routers/admin/users.go b/routers/admin/users.go index 93c29614f..8c90b0050 100644 --- a/routers/admin/users.go +++ b/routers/admin/users.go @@ -14,6 +14,7 @@ import ( "github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/context" "github.com/gogits/gogs/modules/log" + "github.com/gogits/gogs/modules/mailer" "github.com/gogits/gogs/modules/setting" "github.com/gogits/gogs/routers" ) @@ -116,7 +117,7 @@ func NewUserPost(ctx *context.Context, form auth.AdminCrateUserForm) { // Send email notification. if form.SendNotify && setting.MailService != nil { - models.SendRegisterNotifyMail(ctx.Context, u) + mailer.SendRegisterNotifyMail(ctx.Context, models.NewMailerUser(u)) } ctx.Flash.Success(ctx.Tr("admin.users.new_success", u.Name)) diff --git a/routers/api/v1/admin/user.go b/routers/api/v1/admin/user.go index 51e2bfe0c..ff2f2c9c6 100644 --- a/routers/api/v1/admin/user.go +++ b/routers/api/v1/admin/user.go @@ -10,6 +10,7 @@ import ( "github.com/gogits/gogs/models" "github.com/gogits/gogs/modules/context" "github.com/gogits/gogs/modules/log" + "github.com/gogits/gogs/modules/mailer" "github.com/gogits/gogs/modules/setting" "github.com/gogits/gogs/routers/api/v1/user" ) @@ -65,7 +66,7 @@ func CreateUser(ctx *context.APIContext, form api.CreateUserOption) { // Send email notification. if form.SendNotify && setting.MailService != nil { - models.SendRegisterNotifyMail(ctx.Context.Context, u) + mailer.SendRegisterNotifyMail(ctx.Context.Context, models.NewMailerUser(u)) } ctx.JSON(201, u.APIFormat()) diff --git a/routers/repo/setting.go b/routers/repo/setting.go index 80b44ad22..52173827e 100644 --- a/routers/repo/setting.go +++ b/routers/repo/setting.go @@ -15,6 +15,7 @@ import ( "github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/context" "github.com/gogits/gogs/modules/log" + "github.com/gogits/gogs/modules/mailer" "github.com/gogits/gogs/modules/setting" ) @@ -344,7 +345,7 @@ func CollaborationPost(ctx *context.Context) { } if setting.Service.EnableNotifyMail { - models.SendCollaboratorMail(u, ctx.User, ctx.Repo.Repository) + mailer.SendCollaboratorMail(models.NewMailerUser(u), models.NewMailerUser(ctx.User), models.NewMailerRepo(ctx.Repo.Repository)) } ctx.Flash.Success(ctx.Tr("repo.settings.add_collaborator_success")) diff --git a/routers/user/auth.go b/routers/user/auth.go index cd929fa71..fe1c22fe8 100644 --- a/routers/user/auth.go +++ b/routers/user/auth.go @@ -15,6 +15,7 @@ import ( "github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/context" "github.com/gogits/gogs/modules/log" + "github.com/gogits/gogs/modules/mailer" "github.com/gogits/gogs/modules/setting" ) @@ -228,7 +229,7 @@ func SignUpPost(ctx *context.Context, cpt *captcha.Captcha, form auth.RegisterFo // Send confirmation email, no need for social account. if setting.Service.RegisterEmailConfirm && u.ID > 1 { - models.SendActivateAccountMail(ctx.Context, u) + mailer.SendActivateAccountMail(ctx.Context, models.NewMailerUser(u)) ctx.Data["IsSendRegisterMail"] = true ctx.Data["Email"] = u.Email ctx.Data["Hours"] = setting.Service.ActiveCodeLives / 60 @@ -257,7 +258,7 @@ func Activate(ctx *context.Context) { ctx.Data["ResendLimited"] = true } else { ctx.Data["Hours"] = setting.Service.ActiveCodeLives / 60 - models.SendActivateAccountMail(ctx.Context, ctx.User) + mailer.SendActivateAccountMail(ctx.Context, models.NewMailerUser(ctx.User)) if err := ctx.Cache.Put("MailResendLimit_"+ctx.User.LowerName, ctx.User.LowerName, 180); err != nil { log.Error(4, "Set cache(MailResendLimit) fail: %v", err) @@ -367,7 +368,7 @@ func ForgotPasswdPost(ctx *context.Context) { return } - models.SendResetPasswordMail(ctx.Context, u) + mailer.SendResetPasswordMail(ctx.Context, models.NewMailerUser(u)) if err = ctx.Cache.Put("MailResendLimit_"+u.LowerName, u.LowerName, 180); err != nil { log.Error(4, "Set cache(MailResendLimit) fail: %v", err) } diff --git a/routers/user/setting.go b/routers/user/setting.go index ade5967ce..96e7a09cc 100644 --- a/routers/user/setting.go +++ b/routers/user/setting.go @@ -17,6 +17,7 @@ import ( "github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/context" "github.com/gogits/gogs/modules/log" + "github.com/gogits/gogs/modules/mailer" "github.com/gogits/gogs/modules/setting" ) @@ -266,7 +267,7 @@ func SettingsEmailPost(ctx *context.Context, form auth.AddEmailForm) { // Send confirmation email if setting.Service.RegisterEmailConfirm { - models.SendActivateEmailMail(ctx.Context, ctx.User, email) + mailer.SendActivateEmailMail(ctx.Context, models.NewMailerUser(ctx.User), email.Email) if err := ctx.Cache.Put("MailResendLimit_"+ctx.User.LowerName, ctx.User.LowerName, 180); err != nil { log.Error(4, "Set cache(MailResendLimit) fail: %v", err) diff --git a/templates/.VERSION b/templates/.VERSION index a6d787ee3..f82f38aa2 100644 --- a/templates/.VERSION +++ b/templates/.VERSION @@ -1 +1 @@ -0.9.126.0129 \ No newline at end of file +0.9.127.0130 \ No newline at end of file diff --git a/templates/mail/issue/mention.tmpl b/templates/mail/issue/mention.tmpl index e88b9abf0..982bfe137 100644 --- a/templates/mail/issue/mention.tmpl +++ b/templates/mail/issue/mention.tmpl @@ -6,7 +6,7 @@ -

@{{.Doer.Name}} mentioned you:

+

@{{.Doer.DisplayName}} mentioned you:

{{.Body | Str2html}}

---