mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-11-03 08:02:36 +09:00 
			
		
		
		
	#2854 fix no mail notification when issue is closed/reopened
This commit is contained in:
		
							
								
								
									
										10
									
								
								cmd/web.go
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								cmd/web.go
									
									
									
									
									
								
							@@ -78,7 +78,7 @@ func checkVersion() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// 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"},
 | 
				
			||||||
@@ -86,7 +86,7 @@ func checkVersion() {
 | 
				
			|||||||
		{"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"},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -126,12 +126,16 @@ func newMacaron() *macaron.Macaron {
 | 
				
			|||||||
			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 {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -73,6 +73,15 @@ func (i *Issue) BeforeUpdate() {
 | 
				
			|||||||
	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 {
 | 
				
			||||||
@@ -146,6 +155,10 @@ func (i *Issue) State() string {
 | 
				
			|||||||
	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
 | 
				
			||||||
@@ -390,7 +403,7 @@ func newIssue(e *xorm.Session, repo *Repository, issue *Issue, labelIDs []int64,
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nil
 | 
						return issue.loadAttributes()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewIssue creates new issue with labels for repository.
 | 
					// NewIssue creates new issue with labels for repository.
 | 
				
			||||||
@@ -422,7 +435,9 @@ func NewIssue(repo *Repository, issue *Issue, labelIDs []int64, uuids []string)
 | 
				
			|||||||
		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
 | 
				
			||||||
@@ -451,8 +466,7 @@ func GetIssueByRef(ref string) (*Issue, error) {
 | 
				
			|||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	issue.Repo = repo
 | 
						return issue, issue.loadAttributes()
 | 
				
			||||||
	return issue, nil
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetIssueByIndex returns issue by given index in repository.
 | 
					// GetIssueByIndex returns issue by given index in repository.
 | 
				
			||||||
@@ -467,7 +481,7 @@ func GetIssueByIndex(repoID, index int64) (*Issue, error) {
 | 
				
			|||||||
	} 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.
 | 
				
			||||||
@@ -479,7 +493,7 @@ func GetIssueByID(id int64) (*Issue, error) {
 | 
				
			|||||||
	} 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 {
 | 
				
			||||||
@@ -700,42 +714,44 @@ func GetIssueUserPairsByMode(uid, rid int64, isClosed bool, page, filterMode int
 | 
				
			|||||||
	return ius, err
 | 
						return ius, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func UpdateMentions(userNames []string, issueId int64) error {
 | 
					// UpdateIssueMentions extracts mentioned people from content and
 | 
				
			||||||
	for i := range userNames {
 | 
					// updates issue-user relations for them.
 | 
				
			||||||
		userNames[i] = strings.ToLower(userNames[i])
 | 
					func UpdateIssueMentions(issueID int64, mentions []string) error {
 | 
				
			||||||
	}
 | 
						if len(mentions) == 0 {
 | 
				
			||||||
	users := make([]*User, 0, len(userNames))
 | 
							return nil
 | 
				
			||||||
 | 
					 | 
				
			||||||
	if err := x.Where("lower_name IN (?)", strings.Join(userNames, "\",\"")).OrderBy("lower_name ASC").Find(&users); err != nil {
 | 
					 | 
				
			||||||
		return err
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ids := make([]int64, 0, len(userNames))
 | 
						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(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 {
 | 
							memberIDs := make([]int64, 0, user.NumMembers)
 | 
				
			||||||
			continue
 | 
							orgUsers, err := GetOrgUsersByOrgID(user.Id)
 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		tempIds := 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 {
 | 
						if err := UpdateIssueUsersByMentions(issueID, ids); err != nil {
 | 
				
			||||||
		return err
 | 
							return fmt.Errorf("UpdateIssueUsersByMentions: %v", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
@@ -973,9 +989,12 @@ func UpdateIssueUserByRead(uid, issueID int64) error {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 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
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -13,6 +13,7 @@ import (
 | 
				
			|||||||
	"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.
 | 
				
			||||||
@@ -113,10 +114,34 @@ func (c *Comment) EventTag() string {
 | 
				
			|||||||
	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,
 | 
				
			||||||
@@ -157,7 +182,7 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err
 | 
				
			|||||||
				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)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -167,7 +192,7 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, err
 | 
				
			|||||||
			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)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -200,13 +225,15 @@ func createComment(e *xorm.Session, opts *CreateCommentOptions) (_ *Comment, 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
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										81
									
								
								models/issue_mail.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								models/issue_mail.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,81 @@
 | 
				
			|||||||
 | 
					// 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
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										183
									
								
								models/mail.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										183
									
								
								models/mail.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,183 @@
 | 
				
			|||||||
 | 
					// 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"))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -58,7 +58,7 @@ func (org *User) GetTeams() error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// 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
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -306,10 +306,10 @@ func GetOrgUsersByUserID(uid int64, all bool) ([]*OrgUser, error) {
 | 
				
			|||||||
	return ous, err
 | 
						return ous, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetOrgUsersByOrgId returns all organization-user relations by organization ID.
 | 
					// GetOrgUsersByOrgID returns all organization-user relations by organization ID.
 | 
				
			||||||
func GetOrgUsersByOrgId(orgId int64) ([]*OrgUser, error) {
 | 
					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
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -342,9 +342,6 @@ func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []str
 | 
				
			|||||||
		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 {
 | 
				
			||||||
@@ -364,7 +361,17 @@ func NewPullRequest(repo *Repository, pull *Issue, labelIDs []int64, uuids []str
 | 
				
			|||||||
		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
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,190 +0,0 @@
 | 
				
			|||||||
// 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)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -28,6 +28,8 @@ type Message struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// 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...)
 | 
				
			||||||
@@ -196,7 +198,7 @@ func processMailQueue() {
 | 
				
			|||||||
		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)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -93,6 +93,16 @@ var (
 | 
				
			|||||||
	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
 | 
				
			||||||
@@ -202,6 +212,9 @@ func (r *Renderer) Image(out *bytes.Buffer, link []byte, title []byte, alt []byt
 | 
				
			|||||||
// 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] == '/' {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,7 +17,6 @@ import (
 | 
				
			|||||||
	"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"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -178,7 +177,7 @@ func Dashboard(ctx *context.Context) {
 | 
				
			|||||||
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))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,6 @@ import (
 | 
				
			|||||||
	"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"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -115,9 +114,9 @@ func NewUserPost(ctx *context.Context, form auth.AdminCrateUserForm) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	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))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,7 +10,6 @@ import (
 | 
				
			|||||||
	"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"
 | 
				
			||||||
@@ -64,9 +63,9 @@ func CreateUser(ctx *context.APIContext, form api.CreateUserOption) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	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))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,7 +14,6 @@ import (
 | 
				
			|||||||
	"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) {
 | 
				
			||||||
@@ -80,9 +79,6 @@ func CreateIssue(ctx *context.APIContext, form api.CreateIssueOption) {
 | 
				
			|||||||
	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 {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,7 +22,6 @@ import (
 | 
				
			|||||||
	"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"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@@ -395,46 +394,6 @@ func ValidateRepoMetas(ctx *context.Context, form auth.CreateIssueForm) ([]int64
 | 
				
			|||||||
	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
 | 
				
			||||||
@@ -471,9 +430,6 @@ func NewIssuePost(ctx *context.Context, form auth.CreateIssueForm) {
 | 
				
			|||||||
	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)
 | 
				
			||||||
@@ -933,16 +889,6 @@ func NewComment(ctx *context.Context, form auth.CreateCommentForm) {
 | 
				
			|||||||
		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)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1024,7 +970,6 @@ func UpdateLabel(ctx *context.Context, form auth.CreateLabelForm) {
 | 
				
			|||||||
		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 {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -681,9 +681,6 @@ func CompareAndPullRequestPost(ctx *context.Context, form auth.CreateIssueForm)
 | 
				
			|||||||
	} 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)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,7 +15,6 @@ import (
 | 
				
			|||||||
	"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"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -325,10 +324,7 @@ func CollaborationPost(ctx *context.Context) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if setting.Service.EnableNotifyMail {
 | 
						if setting.Service.EnableNotifyMail {
 | 
				
			||||||
		if err = mailer.SendCollaboratorMail(ctx.Render, u, ctx.User, ctx.Repo.Repository); err != nil {
 | 
							models.SendCollaboratorMail(u, ctx.User, ctx.Repo.Repository)
 | 
				
			||||||
			ctx.Handle(500, "SendCollaboratorMail", err)
 | 
					 | 
				
			||||||
			return
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx.Flash.Success(ctx.Tr("repo.settings.add_collaborator_success"))
 | 
						ctx.Flash.Success(ctx.Tr("repo.settings.add_collaborator_success"))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,7 +15,6 @@ import (
 | 
				
			|||||||
	"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"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -220,9 +219,9 @@ func SignUpPost(ctx *context.Context, cpt *captcha.Captcha, form auth.RegisterFo
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 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
 | 
				
			||||||
@@ -245,13 +244,13 @@ func Activate(ctx *context.Context) {
 | 
				
			|||||||
			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)
 | 
				
			||||||
@@ -355,7 +354,7 @@ func ForgotPasswdPost(ctx *context.Context) {
 | 
				
			|||||||
		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)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,7 +17,6 @@ import (
 | 
				
			|||||||
	"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"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -239,12 +238,12 @@ func SettingsEmailPost(ctx *context.Context, form auth.AddEmailForm) {
 | 
				
			|||||||
		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
 | 
				
			||||||
@@ -253,19 +252,19 @@ func SettingsEmailPost(ctx *context.Context, form auth.AddEmailForm) {
 | 
				
			|||||||
		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")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,14 +2,14 @@
 | 
				
			|||||||
<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>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,14 +2,14 @@
 | 
				
			|||||||
<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>Hi <b>{{.Username}}</b>,</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 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><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>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,13 +2,13 @@
 | 
				
			|||||||
<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>Hi <b>{{.Username}}</b>, this is your registration confirmation email for {{AppName}}!</p>
 | 
				
			||||||
	<p>You can now login via username: {{.User.Name}}.</p>
 | 
						<p>You can now login via username: {{.Username}}.</p>
 | 
				
			||||||
	<p><a href="{{.AppUrl}}user/login">{{.AppUrl}}user/login</a></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>© 2016 <a target="_blank" href="{{AppUrl}}">{{AppName}}</a></p>
 | 
				
			||||||
</body>
 | 
					</body>
 | 
				
			||||||
</html>
 | 
					</html>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,14 +2,14 @@
 | 
				
			|||||||
<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>Hi <b>{{.Username}}</b>,</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 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><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>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,12 +6,11 @@
 | 
				
			|||||||
</head>
 | 
					</head>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<body>
 | 
					<body>
 | 
				
			||||||
	<p>@{{.ActUserName}} mentioned you:</p>
 | 
						<p>{{.Body | Str2html}}</p>
 | 
				
			||||||
	<p>{{.Content | 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>
 | 
				
			||||||
							
								
								
									
										17
									
								
								templates/mail/issue/mention.tmpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								templates/mail/issue/mention.tmpl
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					<!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>
 | 
				
			||||||
@@ -6,11 +6,11 @@
 | 
				
			|||||||
</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>
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user