// Check dependency version. | // Check dependency version. | ||||
checkers := []VerChecker{ | checkers := []VerChecker{ | ||||
{"github.com/go-xorm/xorm", func() string { return xorm.Version }, "0.5.5.0711"}, | |||||
{"github.com/go-xorm/xorm", func() string { return xorm.Version }, "0.5.5"}, | |||||
{"github.com/go-macaron/binding", binding.Version, "0.3.2"}, | {"github.com/go-macaron/binding", binding.Version, "0.3.2"}, | ||||
{"github.com/go-macaron/cache", cache.Version, "0.1.2"}, | {"github.com/go-macaron/cache", cache.Version, "0.1.2"}, | ||||
{"github.com/go-macaron/csrf", csrf.Version, "0.1.0"}, | {"github.com/go-macaron/csrf", csrf.Version, "0.1.0"}, | ||||
{"github.com/go-macaron/session", session.Version, "0.1.6"}, | {"github.com/go-macaron/session", session.Version, "0.1.6"}, | ||||
{"github.com/go-macaron/toolbox", toolbox.Version, "0.1.0"}, | {"github.com/go-macaron/toolbox", toolbox.Version, "0.1.0"}, | ||||
{"gopkg.in/ini.v1", ini.Version, "1.8.4"}, | {"gopkg.in/ini.v1", ini.Version, "1.8.4"}, | ||||
{"gopkg.in/macaron.v1", macaron.Version, "1.1.2"}, | |||||
{"gopkg.in/macaron.v1", macaron.Version, "1.1.4"}, | |||||
{"github.com/gogits/git-module", git.Version, "0.3.2"}, | {"github.com/gogits/git-module", git.Version, "0.3.2"}, | ||||
{"github.com/gogits/go-gogs-client", gogs.Version, "0.7.4"}, | {"github.com/gogits/go-gogs-client", gogs.Version, "0.7.4"}, | ||||
} | } | ||||
SkipLogging: setting.DisableRouterLog, | SkipLogging: setting.DisableRouterLog, | ||||
}, | }, | ||||
)) | )) | ||||
funcMap := template.NewFuncMap() | |||||
m.Use(macaron.Renderer(macaron.RenderOptions{ | m.Use(macaron.Renderer(macaron.RenderOptions{ | ||||
Directory: path.Join(setting.StaticRootPath, "templates"), | Directory: path.Join(setting.StaticRootPath, "templates"), | ||||
AppendDirectories: []string{path.Join(setting.CustomPath, "templates")}, | AppendDirectories: []string{path.Join(setting.CustomPath, "templates")}, | ||||
Funcs: template.NewFuncMap(), | |||||
Funcs: funcMap, | |||||
IndentJSON: macaron.Env != macaron.PROD, | IndentJSON: macaron.Env != macaron.PROD, | ||||
})) | })) | ||||
models.InitMailRender(path.Join(setting.StaticRootPath, "templates/mail"), | |||||
path.Join(setting.CustomPath, "templates/mail"), funcMap) | |||||
localeNames, err := bindata.AssetDir("conf/locale") | localeNames, err := bindata.AssetDir("conf/locale") | ||||
if err != nil { | if err != nil { |
i.DeadlineUnix = i.Deadline.UTC().Unix() | i.DeadlineUnix = i.Deadline.UTC().Unix() | ||||
} | } | ||||
func (issue *Issue) loadAttributes() (err error) { | |||||
issue.Repo, err = GetRepositoryByID(issue.RepoID) | |||||
if err != nil { | |||||
return fmt.Errorf("GetRepositoryByID: %v", err) | |||||
} | |||||
return nil | |||||
} | |||||
func (i *Issue) AfterSet(colName string, _ xorm.Cell) { | func (i *Issue) AfterSet(colName string, _ xorm.Cell) { | ||||
var err error | var err error | ||||
switch colName { | switch colName { | ||||
return "open" | return "open" | ||||
} | } | ||||
func (issue *Issue) FullLink() string { | |||||
return fmt.Sprintf("%s/issues/%d", issue.Repo.FullLink(), issue.Index) | |||||
} | |||||
// IsPoster returns true if given user by ID is the poster. | // IsPoster returns true if given user by ID is the poster. | ||||
func (i *Issue) IsPoster(uid int64) bool { | func (i *Issue) IsPoster(uid int64) bool { | ||||
return i.PosterID == uid | return i.PosterID == uid | ||||
} | } | ||||
} | } | ||||
return nil | |||||
return issue.loadAttributes() | |||||
} | } | ||||
// NewIssue creates new issue with labels for repository. | // NewIssue creates new issue with labels for repository. | ||||
IsPrivate: repo.IsPrivate, | IsPrivate: repo.IsPrivate, | ||||
} | } | ||||
if err = NotifyWatchers(act); err != nil { | if err = NotifyWatchers(act); err != nil { | ||||
log.Error(4, "notifyWatchers: %v", err) | |||||
log.Error(4, "NotifyWatchers: %v", err) | |||||
} else if err = issue.MailParticipants(); err != nil { | |||||
log.Error(4, "MailParticipants: %v", err) | |||||
} | } | ||||
return nil | return nil | ||||
return nil, err | return nil, err | ||||
} | } | ||||
issue.Repo = repo | |||||
return issue, nil | |||||
return issue, issue.loadAttributes() | |||||
} | } | ||||
// GetIssueByIndex returns issue by given index in repository. | // GetIssueByIndex returns issue by given index in repository. | ||||
} else if !has { | } else if !has { | ||||
return nil, ErrIssueNotExist{0, repoID, index} | return nil, ErrIssueNotExist{0, repoID, index} | ||||
} | } | ||||
return issue, nil | |||||
return issue, issue.loadAttributes() | |||||
} | } | ||||
// GetIssueByID returns an issue by given ID. | // GetIssueByID returns an issue by given ID. | ||||
} else if !has { | } else if !has { | ||||
return nil, ErrIssueNotExist{id, 0, 0} | return nil, ErrIssueNotExist{id, 0, 0} | ||||
} | } | ||||
return issue, nil | |||||
return issue, issue.loadAttributes() | |||||
} | } | ||||
type IssuesOptions struct { | type IssuesOptions struct { | ||||
return ius, err | return ius, err | ||||
} | } | ||||
func UpdateMentions(userNames []string, issueId int64) error { | |||||
for i := range userNames { | |||||
userNames[i] = strings.ToLower(userNames[i]) | |||||
// UpdateIssueMentions extracts mentioned people from content and | |||||
// updates issue-user relations for them. | |||||
func UpdateIssueMentions(issueID int64, mentions []string) error { | |||||
if len(mentions) == 0 { | |||||
return nil | |||||
} | } | ||||
users := make([]*User, 0, len(userNames)) | |||||
if err := x.Where("lower_name IN (?)", strings.Join(userNames, "\",\"")).OrderBy("lower_name ASC").Find(&users); err != nil { | |||||
return err | |||||
for i := range mentions { | |||||
mentions[i] = strings.ToLower(mentions[i]) | |||||
} | |||||
users := make([]*User, 0, len(mentions)) | |||||
if err := x.In("lower_name", mentions).Asc("lower_name").Find(&users); err != nil { | |||||
return fmt.Errorf("find mentioned users: %v", err) | |||||
} | } | ||||
ids := make([]int64, 0, len(userNames)) | |||||
ids := make([]int64, 0, len(mentions)) | |||||
for _, user := range users { | for _, user := range users { | ||||
ids = append(ids, user.Id) | ids = append(ids, user.Id) | ||||
if !user.IsOrganization() { | |||||
if !user.IsOrganization() || user.NumMembers == 0 { | |||||
continue | continue | ||||
} | } | ||||
if user.NumMembers == 0 { | |||||
continue | |||||
} | |||||
tempIds := make([]int64, 0, user.NumMembers) | |||||
orgUsers, err := GetOrgUsersByOrgId(user.Id) | |||||
memberIDs := make([]int64, 0, user.NumMembers) | |||||
orgUsers, err := GetOrgUsersByOrgID(user.Id) | |||||
if err != nil { | if err != nil { | ||||
return err | |||||
return fmt.Errorf("GetOrgUsersByOrgID [%d]: %v", user.Id, err) | |||||
} | } | ||||
for _, orgUser := range orgUsers { | for _, orgUser := range orgUsers { | ||||
tempIds = append(tempIds, orgUser.ID) | |||||
memberIDs = append(memberIDs, orgUser.ID) | |||||
} | } | ||||
ids = append(ids, tempIds...) | |||||
ids = append(ids, memberIDs...) | |||||
} | } | ||||
if err := UpdateIssueUsersByMentions(ids, issueId); err != nil { | |||||
return err | |||||
if err := UpdateIssueUsersByMentions(issueID, ids); err != nil { | |||||
return fmt.Errorf("UpdateIssueUsersByMentions: %v", err) | |||||
} | } | ||||
return nil | return nil | ||||
} | } | ||||
// UpdateIssueUsersByMentions updates issue-user pairs by mentioning. | // UpdateIssueUsersByMentions updates issue-user pairs by mentioning. | ||||
func UpdateIssueUsersByMentions(uids []int64, iid int64) error { | |||||
func UpdateIssueUsersByMentions(issueID int64, uids []int64) error { | |||||
for _, uid := range uids { | for _, uid := range uids { | ||||
iu := &IssueUser{UID: uid, IssueID: iid} | |||||
iu := &IssueUser{ | |||||
UID: uid, | |||||
IssueID: issueID, | |||||
} | |||||
has, err := x.Get(iu) | has, err := x.Get(iu) | ||||
if err != nil { | if err != nil { | ||||
return err | return err |
"github.com/go-xorm/xorm" | "github.com/go-xorm/xorm" | ||||
"github.com/gogits/gogs/modules/log" | "github.com/gogits/gogs/modules/log" | ||||
"github.com/gogits/gogs/modules/markdown" | |||||
) | ) | ||||
// CommentType defines whether a comment is just a simple comment, an action (like close) or a reference. | // CommentType defines whether a comment is just a simple comment, an action (like close) or a reference. | ||||
return "event-" + com.ToStr(c.ID) | return "event-" + com.ToStr(c.ID) | ||||
} | } | ||||
// MailParticipants sends new comment emails to repository watchers | |||||
// and mentioned people. | |||||
func (cmt *Comment) MailParticipants(opType ActionType, issue *Issue) (err error) { | |||||
mentions := markdown.FindAllMentions(cmt.Content) | |||||
if err = UpdateIssueMentions(cmt.IssueID, mentions); err != nil { | |||||
return fmt.Errorf("UpdateIssueMentions [%d]: %v", cmt.IssueID, err) | |||||
} | |||||
switch opType { | |||||
case ACTION_COMMENT_ISSUE: | |||||
issue.Content = cmt.Content | |||||
case ACTION_CLOSE_ISSUE: | |||||
issue.Content = fmt.Sprintf("Closed #%d", issue.Index) | |||||
case ACTION_REOPEN_ISSUE: | |||||
issue.Content = fmt.Sprintf("Reopened #%d", issue.Index) | |||||
} | |||||
if err = mailIssueCommentToParticipants(issue, cmt.Poster, mentions); err != nil { | |||||
log.Error(4, "mailIssueCommentToParticipants: %v", err) | |||||
} | |||||
return nil | |||||
} | |||||
func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err error) { | func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err error) { | ||||
comment := &Comment{ | comment := &Comment{ | ||||
Type: opts.Type, | Type: opts.Type, | ||||
PosterID: opts.Doer.Id, | PosterID: opts.Doer.Id, | ||||
Poster: opts.Doer, | |||||
IssueID: opts.Issue.ID, | IssueID: opts.Issue.ID, | ||||
CommitID: opts.CommitID, | CommitID: opts.CommitID, | ||||
CommitSHA: opts.CommitSHA, | CommitSHA: opts.CommitSHA, | ||||
if IsErrAttachmentNotExist(err) { | if IsErrAttachmentNotExist(err) { | ||||
continue | continue | ||||
} | } | ||||
return nil, fmt.Errorf("getAttachmentByUUID[%s]: %v", uuid, err) | |||||
return nil, fmt.Errorf("getAttachmentByUUID [%s]: %v", uuid, err) | |||||
} | } | ||||
attachments = append(attachments, attach) | attachments = append(attachments, attach) | ||||
} | } | ||||
attachments[i].CommentID = comment.ID | attachments[i].CommentID = comment.ID | ||||
// No assign value could be 0, so ignore AllCols(). | // No assign value could be 0, so ignore AllCols(). | ||||
if _, err = e.Id(attachments[i].ID).Update(attachments[i]); err != nil { | if _, err = e.Id(attachments[i].ID).Update(attachments[i]); err != nil { | ||||
return nil, fmt.Errorf("update attachment[%d]: %v", attachments[i].ID, err) | |||||
return nil, fmt.Errorf("update attachment [%d]: %v", attachments[i].ID, err) | |||||
} | } | ||||
} | } | ||||
if err != nil { | if err != nil { | ||||
return nil, err | return nil, err | ||||
} | } | ||||
} | } | ||||
// Notify watchers for whatever action comes in, ignore if no action type | |||||
// Notify watchers for whatever action comes in, ignore if no action type. | |||||
if act.OpType > 0 { | if act.OpType > 0 { | ||||
if err = notifyWatchers(e, act); err != nil { | if err = notifyWatchers(e, act); err != nil { | ||||
return nil, fmt.Errorf("notifyWatchers: %v", err) | |||||
log.Error(4, "notifyWatchers: %v", err) | |||||
} | } | ||||
comment.MailParticipants(act.OpType, opts.Issue) | |||||
} | } | ||||
return comment, nil | return comment, nil |
// Copyright 2016 The Gogs Authors. All rights reserved. | |||||
// Use of this source code is governed by a MIT-style | |||||
// license that can be found in the LICENSE file. | |||||
package models | |||||
import ( | |||||
"fmt" | |||||
"github.com/Unknwon/com" | |||||
"github.com/gogits/gogs/modules/log" | |||||
"github.com/gogits/gogs/modules/markdown" | |||||
"github.com/gogits/gogs/modules/setting" | |||||
) | |||||
func (issue *Issue) MailSubject() string { | |||||
return fmt.Sprintf("[%s] %s (#%d)", issue.Repo.Name, issue.Name, issue.Index) | |||||
} | |||||
// mailIssueCommentToParticipants can be used for both new issue creation and comment. | |||||
func mailIssueCommentToParticipants(issue *Issue, doer *User, mentions []string) error { | |||||
if !setting.Service.EnableNotifyMail { | |||||
return nil | |||||
} | |||||
// Mail wahtcers. | |||||
watchers, err := GetWatchers(issue.RepoID) | |||||
if err != nil { | |||||
return fmt.Errorf("GetWatchers [%d]: %v", issue.RepoID, err) | |||||
} | |||||
tos := make([]string, 0, len(watchers)) // List of email addresses. | |||||
names := make([]string, 0, len(watchers)) | |||||
for i := range watchers { | |||||
if watchers[i].UserID == doer.Id { | |||||
continue | |||||
} | |||||
to, err := GetUserByID(watchers[i].UserID) | |||||
if err != nil { | |||||
return fmt.Errorf("GetUserByID [%d]: %v", watchers[i].UserID, err) | |||||
} | |||||
if to.IsOrganization() { | |||||
continue | |||||
} | |||||
tos = append(tos, to.Email) | |||||
names = append(names, to.Name) | |||||
} | |||||
SendIssueCommentMail(issue, doer, tos) | |||||
// Mail mentioned people and exclude watchers. | |||||
names = append(names, doer.Name) | |||||
tos = make([]string, 0, len(mentions)) // list of user names. | |||||
for i := range mentions { | |||||
if com.IsSliceContainsStr(names, mentions[i]) { | |||||
continue | |||||
} | |||||
tos = append(tos, mentions[i]) | |||||
} | |||||
SendIssueMentionMail(issue, doer, GetUserEmailsByNames(tos)) | |||||
return nil | |||||
} | |||||
// MailParticipants sends new issue thread created emails to repository watchers | |||||
// and mentioned people. | |||||
func (issue *Issue) MailParticipants() (err error) { | |||||
mentions := markdown.FindAllMentions(issue.Content) | |||||
if err = UpdateIssueMentions(issue.ID, mentions); err != nil { | |||||
return fmt.Errorf("UpdateIssueMentions [%d]: %v", issue.ID, err) | |||||
} | |||||
if err = mailIssueCommentToParticipants(issue, issue.Poster, mentions); err != nil { | |||||
log.Error(4, "mailIssueCommentToParticipants: %v", err) | |||||
} | |||||
return nil | |||||
} |
// Copyright 2016 The Gogs Authors. All rights reserved. | |||||
// Use of this source code is governed by a MIT-style | |||||
// license that can be found in the LICENSE file. | |||||
package models | |||||
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" | |||||
) | |||||
const ( | |||||
MAIL_AUTH_ACTIVATE base.TplName = "auth/activate" | |||||
MAIL_AUTH_ACTIVATE_EMAIL base.TplName = "auth/activate_email" | |||||
MAIL_AUTH_RESET_PASSWORD base.TplName = "auth/reset_passwd" | |||||
MAIL_AUTH_REGISTER_NOTIFY base.TplName = "auth/register_notify" | |||||
MAIL_ISSUE_COMMENT base.TplName = "issue/comment" | |||||
MAIL_ISSUE_MENTION base.TplName = "issue/mention" | |||||
MAIL_NOTIFY_COLLABORATOR base.TplName = "notify/collaborator" | |||||
) | |||||
type MailRender interface { | |||||
HTMLString(string, interface{}, ...macaron.HTMLOptions) (string, error) | |||||
} | |||||
var mailRender MailRender | |||||
func InitMailRender(dir, appendDir string, funcMap []template.FuncMap) { | |||||
opt := &macaron.RenderOptions{ | |||||
Directory: dir, | |||||
AppendDirectories: []string{appendDir}, | |||||
Funcs: funcMap, | |||||
Extensions: []string{".tmpl", ".html"}, | |||||
} | |||||
ts := macaron.NewTemplateSet() | |||||
ts.Set(macaron.DEFAULT_TPL_SET_NAME, opt) | |||||
mailRender = &macaron.TplRender{ | |||||
TemplateSet: ts, | |||||
Opt: opt, | |||||
} | |||||
} | |||||
func SendTestMail(email string) error { | |||||
return gomail.Send(&mailer.Sender{}, mailer.NewMessage([]string{email}, "Gogs Test Email!", "Gogs Test Email!").Message) | |||||
} | |||||
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, | |||||
"ResetPwdCodeLives": setting.Service.ResetPwdCodeLives / 60, | |||||
"Code": code, | |||||
} | |||||
body, err := mailRender.HTMLString(string(tpl), data) | |||||
if err != nil { | |||||
log.Error(3, "HTMLString: %v", err) | |||||
return | |||||
} | |||||
msg := mailer.NewMessage([]string{u.Email}, subject, body) | |||||
msg.Info = fmt.Sprintf("UID: %d, %s", u.Id, info) | |||||
mailer.SendAsync(msg) | |||||
} | |||||
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) { | |||||
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) { | |||||
data := map[string]interface{}{ | |||||
"Username": u.DisplayName(), | |||||
"ActiveCodeLives": setting.Service.ActiveCodeLives / 60, | |||||
"Code": u.GenerateEmailActivateCode(email.Email), | |||||
"Email": email.Email, | |||||
} | |||||
body, err := mailRender.HTMLString(string(MAIL_AUTH_ACTIVATE_EMAIL), data) | |||||
if err != nil { | |||||
log.Error(3, "HTMLString: %v", err) | |||||
return | |||||
} | |||||
msg := mailer.NewMessage([]string{email.Email}, c.Tr("mail.activate_email"), body) | |||||
msg.Info = fmt.Sprintf("UID: %d, activate email", u.Id) | |||||
mailer.SendAsync(msg) | |||||
} | |||||
// SendRegisterNotifyMail triggers a notify e-mail by admin created a account. | |||||
func SendRegisterNotifyMail(c *macaron.Context, u *User) { | |||||
data := map[string]interface{}{ | |||||
"Username": u.DisplayName(), | |||||
} | |||||
body, err := mailRender.HTMLString(string(MAIL_AUTH_REGISTER_NOTIFY), data) | |||||
if err != nil { | |||||
log.Error(3, "HTMLString: %v", err) | |||||
return | |||||
} | |||||
msg := mailer.NewMessage([]string{u.Email}, c.Tr("mail.register_notify"), body) | |||||
msg.Info = fmt.Sprintf("UID: %d, registration notify", u.Id) | |||||
mailer.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) | |||||
data := map[string]interface{}{ | |||||
"Subject": subject, | |||||
"RepoName": repoName, | |||||
"Link": repo.FullLink(), | |||||
} | |||||
body, err := mailRender.HTMLString(string(MAIL_NOTIFY_COLLABORATOR), data) | |||||
if err != nil { | |||||
log.Error(3, "HTMLString: %v", err) | |||||
return | |||||
} | |||||
msg := mailer.NewMessage([]string{u.Email}, subject, body) | |||||
msg.Info = fmt.Sprintf("UID: %d, add collaborator", u.Id) | |||||
mailer.SendAsync(msg) | |||||
} | |||||
func composeTplData(subject, body, link string) map[string]interface{} { | |||||
data := make(map[string]interface{}, 10) | |||||
data["Subject"] = subject | |||||
data["Body"] = body | |||||
data["Link"] = link | |||||
return data | |||||
} | |||||
func composeIssueMessage(issue *Issue, doer *User, tplName base.TplName, tos []string, info string) *mailer.Message { | |||||
subject := issue.MailSubject() | |||||
body := string(markdown.RenderSpecialLink([]byte(issue.Content), issue.Repo.FullLink(), issue.Repo.ComposeMetas())) | |||||
data := composeTplData(subject, body, issue.FullLink()) | |||||
data["Doer"] = doer | |||||
content, err := mailRender.HTMLString(string(tplName), data) | |||||
if err != nil { | |||||
log.Error(3, "HTMLString (%s): %v", tplName, err) | |||||
} | |||||
msg := mailer.NewMessage(tos, 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) { | |||||
if len(tos) == 0 { | |||||
return | |||||
} | |||||
mailer.SendAsync(composeIssueMessage(issue, 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) { | |||||
if len(tos) == 0 { | |||||
return | |||||
} | |||||
mailer.SendAsync(composeIssueMessage(issue, doer, MAIL_ISSUE_MENTION, tos, "issue mention")) | |||||
} |
// GetMembers returns all members of organization. | // GetMembers returns all members of organization. | ||||
func (org *User) GetMembers() error { | func (org *User) GetMembers() error { | ||||
ous, err := GetOrgUsersByOrgId(org.Id) | |||||
ous, err := GetOrgUsersByOrgID(org.Id) | |||||
if err != nil { | if err != nil { | ||||
return err | return err | ||||
} | } | ||||
return ous, err | return ous, err | ||||
} | } | ||||
// GetOrgUsersByOrgId returns all organization-user relations by organization ID. | |||||
func GetOrgUsersByOrgId(orgId int64) ([]*OrgUser, error) { | |||||
// GetOrgUsersByOrgID returns all organization-user relations by organization ID. | |||||
func GetOrgUsersByOrgID(orgID int64) ([]*OrgUser, error) { | |||||
ous := make([]*OrgUser, 0, 10) | ous := make([]*OrgUser, 0, 10) | ||||
err := x.Where("org_id=?", orgId).Find(&ous) | |||||
err := x.Where("org_id=?", orgID).Find(&ous) | |||||
return ous, err | return ous, err | ||||
} | } | ||||
RepoName: repo.Name, | RepoName: repo.Name, | ||||
IsPrivate: repo.IsPrivate, | IsPrivate: repo.IsPrivate, | ||||
} | } | ||||
if err = notifyWatchers(sess, act); err != nil { | |||||
return err | |||||
} | |||||
pr.Index = pull.Index | pr.Index = pull.Index | ||||
if err = repo.SavePatch(pr.Index, patch); err != nil { | if err = repo.SavePatch(pr.Index, patch); err != nil { | ||||
return fmt.Errorf("insert pull repo: %v", err) | return fmt.Errorf("insert pull repo: %v", err) | ||||
} | } | ||||
return sess.Commit() | |||||
if err = sess.Commit(); err != nil { | |||||
return fmt.Errorf("Commit: %v", err) | |||||
} | |||||
if err = NotifyWatchers(act); err != nil { | |||||
log.Error(4, "NotifyWatchers: %v", err) | |||||
} else if err = pull.MailParticipants(); err != nil { | |||||
log.Error(4, "MailParticipants: %v", err) | |||||
} | |||||
return nil | |||||
} | } | ||||
// GetUnmergedPullRequest returnss a pull request that is open and has not been merged | // GetUnmergedPullRequest returnss a pull request that is open and has not been merged |
// Copyright 2014 The Gogs Authors. All rights reserved. | |||||
// Use of this source code is governed by a MIT-style | |||||
// license that can be found in the LICENSE file. | |||||
package mailer | |||||
import ( | |||||
"fmt" | |||||
"path" | |||||
"strings" | |||||
"gopkg.in/gomail.v2" | |||||
"gopkg.in/macaron.v1" | |||||
"github.com/gogits/gogs/models" | |||||
"github.com/gogits/gogs/modules/base" | |||||
"github.com/gogits/gogs/modules/log" | |||||
"github.com/gogits/gogs/modules/markdown" | |||||
"github.com/gogits/gogs/modules/setting" | |||||
) | |||||
const ( | |||||
AUTH_ACTIVATE base.TplName = "mail/auth/activate" | |||||
AUTH_ACTIVATE_EMAIL base.TplName = "mail/auth/activate_email" | |||||
AUTH_REGISTER_NOTIFY base.TplName = "mail/auth/register_notify" | |||||
AUTH_RESET_PASSWORD base.TplName = "mail/auth/reset_passwd" | |||||
NOTIFY_COLLABORATOR base.TplName = "mail/notify/collaborator" | |||||
NOTIFY_MENTION base.TplName = "mail/notify/mention" | |||||
) | |||||
func ComposeTplData(u *models.User) map[interface{}]interface{} { | |||||
data := make(map[interface{}]interface{}, 10) | |||||
data["AppName"] = setting.AppName | |||||
data["AppVer"] = setting.AppVer | |||||
data["AppUrl"] = setting.AppUrl | |||||
data["ActiveCodeLives"] = setting.Service.ActiveCodeLives / 60 | |||||
data["ResetPwdCodeLives"] = setting.Service.ResetPwdCodeLives / 60 | |||||
if u != nil { | |||||
data["User"] = u | |||||
} | |||||
return data | |||||
} | |||||
func SendUserMail(c *macaron.Context, u *models.User, tpl base.TplName, code, subject, info string) { | |||||
data := ComposeTplData(u) | |||||
data["Code"] = code | |||||
body, err := c.HTMLString(string(tpl), data) | |||||
if err != nil { | |||||
log.Error(4, "HTMLString: %v", err) | |||||
return | |||||
} | |||||
msg := NewMessage([]string{u.Email}, subject, body) | |||||
msg.Info = fmt.Sprintf("UID: %d, %s", u.Id, info) | |||||
SendAsync(msg) | |||||
} | |||||
func SendActivateAccountMail(c *macaron.Context, u *models.User) { | |||||
SendUserMail(c, u, AUTH_ACTIVATE, u.GenerateActivateCode(), c.Tr("mail.activate_account"), "activate account") | |||||
} | |||||
// SendResetPasswordMail sends reset password e-mail. | |||||
func SendResetPasswordMail(c *macaron.Context, u *models.User) { | |||||
SendUserMail(c, u, AUTH_RESET_PASSWORD, u.GenerateActivateCode(), c.Tr("mail.reset_password"), "reset password") | |||||
} | |||||
// SendRegisterNotifyMail triggers a notify e-mail by admin created a account. | |||||
func SendRegisterNotifyMail(c *macaron.Context, u *models.User) { | |||||
body, err := c.HTMLString(string(AUTH_REGISTER_NOTIFY), ComposeTplData(u)) | |||||
if err != nil { | |||||
log.Error(4, "HTMLString: %v", err) | |||||
return | |||||
} | |||||
msg := NewMessage([]string{u.Email}, c.Tr("mail.register_notify"), body) | |||||
msg.Info = fmt.Sprintf("UID: %d, registration notify", u.Id) | |||||
SendAsync(msg) | |||||
} | |||||
// SendActivateAccountMail sends confirmation e-mail. | |||||
func SendActivateEmailMail(c *macaron.Context, u *models.User, email *models.EmailAddress) { | |||||
data := ComposeTplData(u) | |||||
data["Code"] = u.GenerateEmailActivateCode(email.Email) | |||||
data["Email"] = email.Email | |||||
body, err := c.HTMLString(string(AUTH_ACTIVATE_EMAIL), data) | |||||
if err != nil { | |||||
log.Error(4, "HTMLString: %v", err) | |||||
return | |||||
} | |||||
msg := NewMessage([]string{email.Email}, c.Tr("mail.activate_email"), body) | |||||
msg.Info = fmt.Sprintf("UID: %d, activate email", u.Id) | |||||
SendAsync(msg) | |||||
} | |||||
// SendIssueNotifyMail sends mail notification of all watchers of repository. | |||||
func SendIssueNotifyMail(u, owner *models.User, repo *models.Repository, issue *models.Issue) ([]string, error) { | |||||
ws, err := models.GetWatchers(repo.ID) | |||||
if err != nil { | |||||
return nil, fmt.Errorf("GetWatchers[%d]: %v", repo.ID, err) | |||||
} | |||||
tos := make([]string, 0, len(ws)) | |||||
for i := range ws { | |||||
uid := ws[i].UserID | |||||
if u.Id == uid { | |||||
continue | |||||
} | |||||
to, err := models.GetUserByID(uid) | |||||
if err != nil { | |||||
return nil, fmt.Errorf("GetUserByID: %v", err) | |||||
} | |||||
if to.IsOrganization() { | |||||
continue | |||||
} | |||||
tos = append(tos, to.Email) | |||||
} | |||||
if len(tos) == 0 { | |||||
return tos, nil | |||||
} | |||||
subject := fmt.Sprintf("[%s] %s (#%d)", repo.Name, issue.Name, issue.Index) | |||||
content := fmt.Sprintf("%s<br>-<br> <a href=\"%s%s/%s/issues/%d\">View it on Gogs</a>.", | |||||
markdown.RenderSpecialLink([]byte(strings.Replace(issue.Content, "\n", "<br>", -1)), owner.Name+"/"+repo.Name, repo.ComposeMetas()), | |||||
setting.AppUrl, owner.Name, repo.Name, issue.Index) | |||||
msg := NewMessage(tos, subject, content) | |||||
msg.Info = fmt.Sprintf("Subject: %s, issue notify", subject) | |||||
SendAsync(msg) | |||||
return tos, nil | |||||
} | |||||
// SendIssueMentionMail sends mail notification for who are mentioned in issue. | |||||
func SendIssueMentionMail(r macaron.Render, u, owner *models.User, | |||||
repo *models.Repository, issue *models.Issue, tos []string) error { | |||||
if len(tos) == 0 { | |||||
return nil | |||||
} | |||||
subject := fmt.Sprintf("[%s] %s (#%d)", repo.Name, issue.Name, issue.Index) | |||||
data := ComposeTplData(nil) | |||||
data["IssueLink"] = fmt.Sprintf("%s/%s/issues/%d", owner.Name, repo.Name, issue.Index) | |||||
data["Subject"] = subject | |||||
data["ActUserName"] = u.DisplayName() | |||||
data["Content"] = string(markdown.RenderSpecialLink([]byte(issue.Content), owner.Name+"/"+repo.Name, repo.ComposeMetas())) | |||||
body, err := r.HTMLString(string(NOTIFY_MENTION), data) | |||||
if err != nil { | |||||
return fmt.Errorf("HTMLString: %v", err) | |||||
} | |||||
msg := NewMessage(tos, subject, body) | |||||
msg.Info = fmt.Sprintf("Subject: %s, issue mention", subject) | |||||
SendAsync(msg) | |||||
return nil | |||||
} | |||||
// SendCollaboratorMail sends mail notification to new collaborator. | |||||
func SendCollaboratorMail(r macaron.Render, u, doer *models.User, repo *models.Repository) error { | |||||
subject := fmt.Sprintf("%s added you to %s/%s", doer.Name, repo.Owner.Name, repo.Name) | |||||
data := ComposeTplData(nil) | |||||
data["RepoLink"] = path.Join(repo.Owner.Name, repo.Name) | |||||
data["Subject"] = subject | |||||
body, err := r.HTMLString(string(NOTIFY_COLLABORATOR), data) | |||||
if err != nil { | |||||
return fmt.Errorf("HTMLString: %v", err) | |||||
} | |||||
msg := NewMessage([]string{u.Email}, subject, body) | |||||
msg.Info = fmt.Sprintf("UID: %d, add collaborator", u.Id) | |||||
SendAsync(msg) | |||||
return nil | |||||
} | |||||
func SendTestMail(email string) error { | |||||
return gomail.Send(&Sender{}, NewMessage([]string{email}, "Gogs Test Email!", "Gogs Test Email!").Message) | |||||
} |
// NewMessageFrom creates new mail message object with custom From header. | // NewMessageFrom creates new mail message object with custom From header. | ||||
func NewMessageFrom(to []string, from, subject, htmlBody string) *Message { | func NewMessageFrom(to []string, from, subject, htmlBody string) *Message { | ||||
log.Trace("NewMessageFrom (htmlBody):\n%s", htmlBody) | |||||
msg := gomail.NewMessage() | msg := gomail.NewMessage() | ||||
msg.SetHeader("From", from) | msg.SetHeader("From", from) | ||||
msg.SetHeader("To", to...) | msg.SetHeader("To", to...) | ||||
case msg := <-mailQueue: | case msg := <-mailQueue: | ||||
log.Trace("New e-mail sending request %s: %s", msg.GetHeader("To"), msg.Info) | log.Trace("New e-mail sending request %s: %s", msg.GetHeader("To"), msg.Info) | ||||
if err := gomail.Send(sender, msg.Message); err != nil { | if err := gomail.Send(sender, msg.Message); err != nil { | ||||
log.Error(4, "Fail to send e-mails %s: %s - %v", msg.GetHeader("To"), msg.Info, err) | |||||
log.Error(3, "Fail to send emails %s: %s - %v", msg.GetHeader("To"), msg.Info, err) | |||||
} else { | } else { | ||||
log.Trace("E-mails sent %s: %s", msg.GetHeader("To"), msg.Info) | log.Trace("E-mails sent %s: %s", msg.GetHeader("To"), msg.Info) | ||||
} | } |
Sha1CurrentPattern = regexp.MustCompile(`\b[0-9a-f]{40}\b`) | Sha1CurrentPattern = regexp.MustCompile(`\b[0-9a-f]{40}\b`) | ||||
) | ) | ||||
// FindAllMentions matches mention patterns in given content | |||||
// and returns a list of found user names without @ prefix. | |||||
func FindAllMentions(content string) []string { | |||||
mentions := MentionPattern.FindAllString(content, -1) | |||||
for i := range mentions { | |||||
mentions[i] = strings.TrimSpace(mentions[i])[1:] // Strip @ character | |||||
} | |||||
return mentions | |||||
} | |||||
// Renderer is a extended version of underlying render object. | // Renderer is a extended version of underlying render object. | ||||
type Renderer struct { | type Renderer struct { | ||||
blackfriday.Renderer | blackfriday.Renderer | ||||
// cutoutVerbosePrefix cutouts URL prefix including sub-path to | // cutoutVerbosePrefix cutouts URL prefix including sub-path to | ||||
// return a clean unified string of request URL path. | // return a clean unified string of request URL path. | ||||
func cutoutVerbosePrefix(prefix string) string { | func cutoutVerbosePrefix(prefix string) string { | ||||
if len(prefix) == 0 || prefix[0] != '/' { | |||||
return prefix | |||||
} | |||||
count := 0 | count := 0 | ||||
for i := 0; i < len(prefix); i++ { | for i := 0; i < len(prefix); i++ { | ||||
if prefix[i] == '/' { | if prefix[i] == '/' { |
"github.com/gogits/gogs/modules/base" | "github.com/gogits/gogs/modules/base" | ||||
"github.com/gogits/gogs/modules/context" | "github.com/gogits/gogs/modules/context" | ||||
"github.com/gogits/gogs/modules/cron" | "github.com/gogits/gogs/modules/cron" | ||||
"github.com/gogits/gogs/modules/mailer" | |||||
"github.com/gogits/gogs/modules/process" | "github.com/gogits/gogs/modules/process" | ||||
"github.com/gogits/gogs/modules/setting" | "github.com/gogits/gogs/modules/setting" | ||||
) | ) | ||||
func SendTestMail(ctx *context.Context) { | func SendTestMail(ctx *context.Context) { | ||||
email := ctx.Query("email") | email := ctx.Query("email") | ||||
// Send a test email to the user's email address and redirect back to Config | // Send a test email to the user's email address and redirect back to Config | ||||
if err := mailer.SendTestMail(email); err != nil { | |||||
if err := models.SendTestMail(email); err != nil { | |||||
ctx.Flash.Error(ctx.Tr("admin.config.test_mail_failed", email, err)) | ctx.Flash.Error(ctx.Tr("admin.config.test_mail_failed", email, err)) | ||||
} else { | } else { | ||||
ctx.Flash.Info(ctx.Tr("admin.config.test_mail_sent", email)) | ctx.Flash.Info(ctx.Tr("admin.config.test_mail_sent", email)) |
"github.com/gogits/gogs/modules/base" | "github.com/gogits/gogs/modules/base" | ||||
"github.com/gogits/gogs/modules/context" | "github.com/gogits/gogs/modules/context" | ||||
"github.com/gogits/gogs/modules/log" | "github.com/gogits/gogs/modules/log" | ||||
"github.com/gogits/gogs/modules/mailer" | |||||
"github.com/gogits/gogs/modules/setting" | "github.com/gogits/gogs/modules/setting" | ||||
"github.com/gogits/gogs/routers" | "github.com/gogits/gogs/routers" | ||||
) | ) | ||||
} | } | ||||
log.Trace("Account created by admin (%s): %s", ctx.User.Name, u.Name) | log.Trace("Account created by admin (%s): %s", ctx.User.Name, u.Name) | ||||
// Send e-mail notification. | |||||
// Send email notification. | |||||
if form.SendNotify && setting.MailService != nil { | if form.SendNotify && setting.MailService != nil { | ||||
mailer.SendRegisterNotifyMail(ctx.Context, u) | |||||
models.SendRegisterNotifyMail(ctx.Context, u) | |||||
} | } | ||||
ctx.Flash.Success(ctx.Tr("admin.users.new_success", u.Name)) | ctx.Flash.Success(ctx.Tr("admin.users.new_success", u.Name)) |
"github.com/gogits/gogs/models" | "github.com/gogits/gogs/models" | ||||
"github.com/gogits/gogs/modules/context" | "github.com/gogits/gogs/modules/context" | ||||
"github.com/gogits/gogs/modules/log" | "github.com/gogits/gogs/modules/log" | ||||
"github.com/gogits/gogs/modules/mailer" | |||||
"github.com/gogits/gogs/modules/setting" | "github.com/gogits/gogs/modules/setting" | ||||
"github.com/gogits/gogs/routers/api/v1/convert" | "github.com/gogits/gogs/routers/api/v1/convert" | ||||
"github.com/gogits/gogs/routers/api/v1/user" | "github.com/gogits/gogs/routers/api/v1/user" | ||||
} | } | ||||
log.Trace("Account created by admin (%s): %s", ctx.User.Name, u.Name) | log.Trace("Account created by admin (%s): %s", ctx.User.Name, u.Name) | ||||
// Send e-mail notification. | |||||
// Send email notification. | |||||
if form.SendNotify && setting.MailService != nil { | if form.SendNotify && setting.MailService != nil { | ||||
mailer.SendRegisterNotifyMail(ctx.Context.Context, u) | |||||
models.SendRegisterNotifyMail(ctx.Context.Context, u) | |||||
} | } | ||||
ctx.JSON(201, convert.ToUser(u)) | ctx.JSON(201, convert.ToUser(u)) |
"github.com/gogits/gogs/modules/context" | "github.com/gogits/gogs/modules/context" | ||||
"github.com/gogits/gogs/modules/setting" | "github.com/gogits/gogs/modules/setting" | ||||
"github.com/gogits/gogs/routers/api/v1/convert" | "github.com/gogits/gogs/routers/api/v1/convert" | ||||
"github.com/gogits/gogs/routers/repo" | |||||
) | ) | ||||
func ListIssues(ctx *context.APIContext) { | func ListIssues(ctx *context.APIContext) { | ||||
if err := models.NewIssue(ctx.Repo.Repository, issue, form.Labels, nil); err != nil { | if err := models.NewIssue(ctx.Repo.Repository, issue, form.Labels, nil); err != nil { | ||||
ctx.Error(500, "NewIssue", err) | ctx.Error(500, "NewIssue", err) | ||||
return | return | ||||
} else if err := repo.MailWatchersAndMentions(ctx.Context, issue); err != nil { | |||||
ctx.Error(500, "MailWatchersAndMentions", err) | |||||
return | |||||
} | } | ||||
if form.Closed { | if form.Closed { |
"github.com/gogits/gogs/modules/base" | "github.com/gogits/gogs/modules/base" | ||||
"github.com/gogits/gogs/modules/context" | "github.com/gogits/gogs/modules/context" | ||||
"github.com/gogits/gogs/modules/log" | "github.com/gogits/gogs/modules/log" | ||||
"github.com/gogits/gogs/modules/mailer" | |||||
"github.com/gogits/gogs/modules/markdown" | "github.com/gogits/gogs/modules/markdown" | ||||
"github.com/gogits/gogs/modules/setting" | "github.com/gogits/gogs/modules/setting" | ||||
) | ) | ||||
return labelIDs, milestoneID, assigneeID | return labelIDs, milestoneID, assigneeID | ||||
} | } | ||||
func MailWatchersAndMentions(ctx *context.Context, issue *models.Issue) error { | |||||
// Update mentions | |||||
mentions := markdown.MentionPattern.FindAllString(issue.Content, -1) | |||||
if len(mentions) > 0 { | |||||
for i := range mentions { | |||||
mentions[i] = strings.TrimSpace(mentions[i])[1:] | |||||
} | |||||
if err := models.UpdateMentions(mentions, issue.ID); err != nil { | |||||
return fmt.Errorf("UpdateMentions: %v", err) | |||||
} | |||||
} | |||||
repo := ctx.Repo.Repository | |||||
// Mail watchers and mentions. | |||||
if setting.Service.EnableNotifyMail { | |||||
tos, err := mailer.SendIssueNotifyMail(ctx.User, ctx.Repo.Owner, repo, issue) | |||||
if err != nil { | |||||
return fmt.Errorf("SendIssueNotifyMail: %v", err) | |||||
} | |||||
tos = append(tos, ctx.User.LowerName) | |||||
newTos := make([]string, 0, len(mentions)) | |||||
for _, m := range mentions { | |||||
if com.IsSliceContainsStr(tos, m) { | |||||
continue | |||||
} | |||||
newTos = append(newTos, m) | |||||
} | |||||
if err = mailer.SendIssueMentionMail(ctx.Render, ctx.User, ctx.Repo.Owner, | |||||
repo, issue, models.GetUserEmailsByNames(newTos)); err != nil { | |||||
return fmt.Errorf("SendIssueMentionMail: %v", err) | |||||
} | |||||
} | |||||
return nil | |||||
} | |||||
func NewIssuePost(ctx *context.Context, form auth.CreateIssueForm) { | func NewIssuePost(ctx *context.Context, form auth.CreateIssueForm) { | ||||
ctx.Data["Title"] = ctx.Tr("repo.issues.new") | ctx.Data["Title"] = ctx.Tr("repo.issues.new") | ||||
ctx.Data["PageIsIssueList"] = true | ctx.Data["PageIsIssueList"] = true | ||||
if err := models.NewIssue(repo, issue, labelIDs, attachments); err != nil { | if err := models.NewIssue(repo, issue, labelIDs, attachments); err != nil { | ||||
ctx.Handle(500, "NewIssue", err) | ctx.Handle(500, "NewIssue", err) | ||||
return | return | ||||
} else if err := MailWatchersAndMentions(ctx, issue); err != nil { | |||||
ctx.Handle(500, "MailWatchersAndMentions", err) | |||||
return | |||||
} | } | ||||
log.Trace("Issue created: %d/%d", repo.ID, issue.ID) | log.Trace("Issue created: %d/%d", repo.ID, issue.ID) | ||||
return | return | ||||
} | } | ||||
MailWatchersAndMentions(ctx, &models.Issue{ | |||||
ID: issue.ID, | |||||
Index: issue.Index, | |||||
Name: issue.Name, | |||||
Content: form.Content, | |||||
}) | |||||
if ctx.Written() { | |||||
return | |||||
} | |||||
log.Trace("Comment created: %d/%d/%d", ctx.Repo.Repository.ID, issue.ID, comment.ID) | log.Trace("Comment created: %d/%d/%d", ctx.Repo.Repository.ID, issue.ID, comment.ID) | ||||
} | } | ||||
return | return | ||||
} | } | ||||
fmt.Println(form.Title, form.Color) | |||||
l.Name = form.Title | l.Name = form.Title | ||||
l.Color = form.Color | l.Color = form.Color | ||||
if err := models.UpdateLabel(l); err != nil { | if err := models.UpdateLabel(l); err != nil { |
} else if err := pullRequest.PushToBaseRepo(); err != nil { | } else if err := pullRequest.PushToBaseRepo(); err != nil { | ||||
ctx.Handle(500, "PushToBaseRepo", err) | ctx.Handle(500, "PushToBaseRepo", err) | ||||
return | return | ||||
} else if err := MailWatchersAndMentions(ctx, pullIssue); err != nil { | |||||
ctx.Handle(500, "MailWatchersAndMentions", err) | |||||
return | |||||
} | } | ||||
log.Trace("Pull request created: %d/%d", repo.ID, pullIssue.ID) | log.Trace("Pull request created: %d/%d", repo.ID, pullIssue.ID) |
"github.com/gogits/gogs/modules/base" | "github.com/gogits/gogs/modules/base" | ||||
"github.com/gogits/gogs/modules/context" | "github.com/gogits/gogs/modules/context" | ||||
"github.com/gogits/gogs/modules/log" | "github.com/gogits/gogs/modules/log" | ||||
"github.com/gogits/gogs/modules/mailer" | |||||
"github.com/gogits/gogs/modules/setting" | "github.com/gogits/gogs/modules/setting" | ||||
) | ) | ||||
} | } | ||||
if setting.Service.EnableNotifyMail { | if setting.Service.EnableNotifyMail { | ||||
if err = mailer.SendCollaboratorMail(ctx.Render, u, ctx.User, ctx.Repo.Repository); err != nil { | |||||
ctx.Handle(500, "SendCollaboratorMail", err) | |||||
return | |||||
} | |||||
models.SendCollaboratorMail(u, ctx.User, ctx.Repo.Repository) | |||||
} | } | ||||
ctx.Flash.Success(ctx.Tr("repo.settings.add_collaborator_success")) | ctx.Flash.Success(ctx.Tr("repo.settings.add_collaborator_success")) |
"github.com/gogits/gogs/modules/base" | "github.com/gogits/gogs/modules/base" | ||||
"github.com/gogits/gogs/modules/context" | "github.com/gogits/gogs/modules/context" | ||||
"github.com/gogits/gogs/modules/log" | "github.com/gogits/gogs/modules/log" | ||||
"github.com/gogits/gogs/modules/mailer" | |||||
"github.com/gogits/gogs/modules/setting" | "github.com/gogits/gogs/modules/setting" | ||||
) | ) | ||||
} | } | ||||
} | } | ||||
// Send confirmation e-mail, no need for social account. | |||||
// Send confirmation email, no need for social account. | |||||
if setting.Service.RegisterEmailConfirm && u.Id > 1 { | if setting.Service.RegisterEmailConfirm && u.Id > 1 { | ||||
mailer.SendActivateAccountMail(ctx.Context, u) | |||||
models.SendActivateAccountMail(ctx.Context, u) | |||||
ctx.Data["IsSendRegisterMail"] = true | ctx.Data["IsSendRegisterMail"] = true | ||||
ctx.Data["Email"] = u.Email | ctx.Data["Email"] = u.Email | ||||
ctx.Data["Hours"] = setting.Service.ActiveCodeLives / 60 | ctx.Data["Hours"] = setting.Service.ActiveCodeLives / 60 | ||||
ctx.Error(404) | ctx.Error(404) | ||||
return | return | ||||
} | } | ||||
// Resend confirmation e-mail. | |||||
// Resend confirmation email. | |||||
if setting.Service.RegisterEmailConfirm { | if setting.Service.RegisterEmailConfirm { | ||||
if ctx.Cache.IsExist("MailResendLimit_" + ctx.User.LowerName) { | if ctx.Cache.IsExist("MailResendLimit_" + ctx.User.LowerName) { | ||||
ctx.Data["ResendLimited"] = true | ctx.Data["ResendLimited"] = true | ||||
} else { | } else { | ||||
ctx.Data["Hours"] = setting.Service.ActiveCodeLives / 60 | ctx.Data["Hours"] = setting.Service.ActiveCodeLives / 60 | ||||
mailer.SendActivateAccountMail(ctx.Context, ctx.User) | |||||
models.SendActivateAccountMail(ctx.Context, ctx.User) | |||||
if err := ctx.Cache.Put("MailResendLimit_"+ctx.User.LowerName, ctx.User.LowerName, 180); err != nil { | if err := ctx.Cache.Put("MailResendLimit_"+ctx.User.LowerName, ctx.User.LowerName, 180); err != nil { | ||||
log.Error(4, "Set cache(MailResendLimit) fail: %v", err) | log.Error(4, "Set cache(MailResendLimit) fail: %v", err) | ||||
return | return | ||||
} | } | ||||
mailer.SendResetPasswordMail(ctx.Context, u) | |||||
models.SendResetPasswordMail(ctx.Context, u) | |||||
if err = ctx.Cache.Put("MailResendLimit_"+u.LowerName, u.LowerName, 180); err != nil { | if err = ctx.Cache.Put("MailResendLimit_"+u.LowerName, u.LowerName, 180); err != nil { | ||||
log.Error(4, "Set cache(MailResendLimit) fail: %v", err) | log.Error(4, "Set cache(MailResendLimit) fail: %v", err) | ||||
} | } |
"github.com/gogits/gogs/modules/base" | "github.com/gogits/gogs/modules/base" | ||||
"github.com/gogits/gogs/modules/context" | "github.com/gogits/gogs/modules/context" | ||||
"github.com/gogits/gogs/modules/log" | "github.com/gogits/gogs/modules/log" | ||||
"github.com/gogits/gogs/modules/mailer" | |||||
"github.com/gogits/gogs/modules/setting" | "github.com/gogits/gogs/modules/setting" | ||||
) | ) | ||||
return | return | ||||
} | } | ||||
e := &models.EmailAddress{ | |||||
email := &models.EmailAddress{ | |||||
UID: ctx.User.Id, | UID: ctx.User.Id, | ||||
Email: form.Email, | Email: form.Email, | ||||
IsActivated: !setting.Service.RegisterEmailConfirm, | IsActivated: !setting.Service.RegisterEmailConfirm, | ||||
} | } | ||||
if err := models.AddEmailAddress(e); err != nil { | |||||
if err := models.AddEmailAddress(email); err != nil { | |||||
if models.IsErrEmailAlreadyUsed(err) { | if models.IsErrEmailAlreadyUsed(err) { | ||||
ctx.RenderWithErr(ctx.Tr("form.email_been_used"), SETTINGS_EMAILS, &form) | ctx.RenderWithErr(ctx.Tr("form.email_been_used"), SETTINGS_EMAILS, &form) | ||||
return | return | ||||
return | return | ||||
} | } | ||||
// Send confirmation e-mail | |||||
// Send confirmation email | |||||
if setting.Service.RegisterEmailConfirm { | if setting.Service.RegisterEmailConfirm { | ||||
mailer.SendActivateEmailMail(ctx.Context, ctx.User, e) | |||||
models.SendActivateEmailMail(ctx.Context, ctx.User, email) | |||||
if err := ctx.Cache.Put("MailResendLimit_"+ctx.User.LowerName, ctx.User.LowerName, 180); err != nil { | if err := ctx.Cache.Put("MailResendLimit_"+ctx.User.LowerName, ctx.User.LowerName, 180); err != nil { | ||||
log.Error(4, "Set cache(MailResendLimit) fail: %v", err) | log.Error(4, "Set cache(MailResendLimit) fail: %v", err) | ||||
} | } | ||||
ctx.Flash.Info(ctx.Tr("settings.add_email_confirmation_sent", e.Email, setting.Service.ActiveCodeLives/60)) | |||||
ctx.Flash.Info(ctx.Tr("settings.add_email_confirmation_sent", email.Email, setting.Service.ActiveCodeLives/60)) | |||||
} else { | } else { | ||||
ctx.Flash.Success(ctx.Tr("settings.add_email_success")) | ctx.Flash.Success(ctx.Tr("settings.add_email_success")) | ||||
} | } | ||||
log.Trace("Email address added: %s", e.Email) | |||||
log.Trace("Email address added: %s", email.Email) | |||||
ctx.Redirect(setting.AppSubUrl + "/user/settings/email") | ctx.Redirect(setting.AppSubUrl + "/user/settings/email") | ||||
} | } | ||||
<html> | <html> | ||||
<head> | <head> | ||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> | <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> | ||||
<title>{{.User.Name}}, please activate your account</title> | |||||
<title>{{.Username}}, please activate your account</title> | |||||
</head> | </head> | ||||
<body> | <body> | ||||
<p>Hi <b>{{.User.Name}}</b>, thanks for registering at {{.AppName}}!</p> | |||||
<p>Hi <b>{{.Username}}</b>, thanks for registering at {{AppName}}!</p> | |||||
<p>Please click the following link to verify your e-mail address within <b>{{.ActiveCodeLives}} hours</b>:</p> | <p>Please click the following link to verify your e-mail address within <b>{{.ActiveCodeLives}} hours</b>:</p> | ||||
<p><a href="{{.AppUrl}}user/activate?code={{.Code}}">{{.AppUrl}}user/activate?code={{.Code}}</a></p> | |||||
<p><a href="{{AppUrl}}user/activate?code={{.Code}}">{{AppUrl}}user/activate?code={{.Code}}</a></p> | |||||
<p>Not working? Try copying and pasting it to your browser.</p> | <p>Not working? Try copying and pasting it to your browser.</p> | ||||
<p>© 2015 <a target="_blank" href="http://gogs.io">Gogs: Go Git Service</a></p> | |||||
<p>© 2016 <a target="_blank" href="{{AppUrl}}">{{AppName}}</a></p> | |||||
</body> | </body> | ||||
</html> | </html> |
<html> | <html> | ||||
<head> | <head> | ||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> | <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> | ||||
<title>{{.User.Name}}, please verify your e-mail address</title> | |||||
<title>{{.Username}}, please verify your e-mail address</title> | |||||
</head> | </head> | ||||
<body> | <body> | ||||
<p>Hi <b>{{.User.Name}}</b>,</p> | |||||
<p>Please click the following link to verify your e-mail address within <b>{{.ActiveCodeLives}} hours</b>:</p> | |||||
<p><a href="{{.AppUrl}}user/activate_email?code={{.Code}}&email={{.Email}}">{{.AppUrl}}user/activate_email?code={{.Code}}&email={{.Email}}</a></p> | |||||
<p>Hi <b>{{.Username}}</b>,</p> | |||||
<p>Please click the following link to verify your email address within <b>{{.ActiveCodeLives}} hours</b>:</p> | |||||
<p><a href="{{AppUrl}}user/activate_email?code={{.Code}}&email={{.Email}}">{{AppUrl}}user/activate_email?code={{.Code}}&email={{.Email}}</a></p> | |||||
<p>Not working? Try copying and pasting it to your browser.</p> | <p>Not working? Try copying and pasting it to your browser.</p> | ||||
<p>© 2015 <a target="_blank" href="http://gogs.io">Gogs: Go Git Service</a></p> | |||||
<p>© 2016 <a target="_blank" href="{{AppUrl}}">{{AppName}}</a></p> | |||||
</body> | </body> | ||||
</html> | </html> |
<html> | <html> | ||||
<head> | <head> | ||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> | <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> | ||||
<title>{{.User.Name}}, welcome to {{.AppName}}</title> | |||||
<title>{{.Username}}, welcome to {{AppName}}</title> | |||||
</head> | </head> | ||||
<body> | <body> | ||||
<p>Hi <b>{{.User.Name}}</b>, this is your registration confirmation email for {{.AppName}}!</p> | |||||
<p>You can now login via username: {{.User.Name}}.</p> | |||||
<p><a href="{{.AppUrl}}user/login">{{.AppUrl}}user/login</a></p> | |||||
<p>© 2015 <a target="_blank" href="http://gogs.io">Gogs: Go Git Service</a></p> | |||||
<p>Hi <b>{{.Username}}</b>, this is your registration confirmation email for {{AppName}}!</p> | |||||
<p>You can now login via username: {{.Username}}.</p> | |||||
<p><a href="{{AppUrl}}user/login">{{AppUrl}}user/login</a></p> | |||||
<p>© 2016 <a target="_blank" href="{{AppUrl}}">{{AppName}}</a></p> | |||||
</body> | </body> | ||||
</html> | </html> |
<html> | <html> | ||||
<head> | <head> | ||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> | <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> | ||||
<title>{{.User.Name}}, you have requested to reset your password</title> | |||||
<title>{{.Username}}, you have requested to reset your password</title> | |||||
</head> | </head> | ||||
<body> | <body> | ||||
<p>Hi <b>{{.User.Name}}</b>,</p> | |||||
<p>Please click the following link to verify your e-mail address within <b>{{.ActiveCodeLives}} hours</b>:</p> | |||||
<p><a href="{{.AppUrl}}user/reset_password?code={{.Code}}">{{.AppUrl}}user/reset_password?code={{.Code}}</a></p> | |||||
<p>Hi <b>{{.Username}}</b>,</p> | |||||
<p>Please click the following link to verify your email address within <b>{{.ResetPwdCodeLives}} hours</b>:</p> | |||||
<p><a href="{{AppUrl}}user/reset_password?code={{.Code}}">{{AppUrl}}user/reset_password?code={{.Code}}</a></p> | |||||
<p>Not working? Try copying and pasting it to your browser.</p> | <p>Not working? Try copying and pasting it to your browser.</p> | ||||
<p>© 2015 <a target="_blank" href="http://gogs.io">Gogs: Go Git Service</a></p> | |||||
<p>© 2016 <a target="_blank" href="{{AppUrl}}">{{AppName}}</a></p> | |||||
</body> | </body> | ||||
</html> | </html> |
</head> | </head> | ||||
<body> | <body> | ||||
<p>@{{.ActUserName}} mentioned you:</p> | |||||
<p>{{.Content | Str2html}}</p> | |||||
<p>{{.Body | Str2html}}</p> | |||||
<p> | <p> | ||||
--- | --- | ||||
<br> | <br> | ||||
<a href="{{.AppUrl}}{{.IssueLink}}">View it on Gogs</a>. | |||||
<a href="{{.Link}}">View it on Gogs</a>. | |||||
</p> | </p> | ||||
</body> | </body> | ||||
</html> | </html> |
<!DOCTYPE html> | |||||
<html> | |||||
<head> | |||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> | |||||
<title>{{.Subject}}</title> | |||||
</head> | |||||
<body> | |||||
<p>@{{.Doer.Name}} mentioned you:</p> | |||||
<p>{{.Body | Str2html}}</p> | |||||
<p> | |||||
--- | |||||
<br> | |||||
<a href="{{.Link}}">View it on Gogs</a>. | |||||
</p> | |||||
</body> | |||||
</html> |
</head> | </head> | ||||
<body> | <body> | ||||
<p>You are now a collaborator of this repository.</p> | |||||
<p>You have been added as a collaborator of repository: <code>{{.RepoName}}</code></p> | |||||
<p> | <p> | ||||
--- | --- | ||||
<br> | <br> | ||||
View it on Gogs: <a href="{{.AppUrl}}{{.RepoLink}}">{{.RepoLink}}</a> | |||||
<a href="{{.Link}}">View it on Gogs</a>. | |||||
</p> | </p> | ||||
</body> | </body> | ||||
</html> | </html> |