conf: overhaul email settings (#5940)

This commit is contained in:
ᴜɴᴋɴᴡᴏɴ
2020-02-25 00:35:35 +08:00
committed by GitHub
parent 0d6c405ccb
commit 52ffb67b33
21 changed files with 259 additions and 229 deletions

View File

@@ -22,6 +22,7 @@ All notable changes to Gogs are documented in this file.
- Configuration option `[server] LANDING_PAGE` is deprecated and will end support in 0.13.0, please start using `[server] LANDING_URL`.
- Configuration option `[database] DB_TYPE` is deprecated and will end support in 0.13.0, please start using `[database] TYPE`.
- Configuration option `[database] PASSWD` is deprecated and will end support in 0.13.0, please start using `[database] PASSWORD`.
- Configuration section `[mailer]` is deprecated and will end support in 0.13.0, please start using `[email]`.
### Fixed

View File

@@ -71,11 +71,8 @@ less: public/css/gogs.css
public/css/gogs.css: $(LESS_FILES)
@type lessc >/dev/null 2>&1 && lessc --source-map "public/less/gogs.less" $@ || echo "lessc command not found or failed"
clean:
go clean -i ./...
clean-mac: clean
find . -name ".DS_Store" -print0 | xargs -0 rm
clean-mac:
find . -name "*.DS_Store" -type f -delete
test:
go test -cover -race ./...

View File

@@ -167,6 +167,40 @@ ENABLE_LOGIN_STATUS_COOKIE = false
; The cookie name to store user login status.
LOGIN_STATUS_COOKIE_NAME = login_status
[email]
; Whether to enable the email service.
ENABLED = false
; The prefix prepended to the subject line.
SUBJECT_PREFIX = `[%(BRAND_NAME)s] `
; The SMTP server with its port, e.g. smtp.mailgun.org:587, smtp.gmail.com:587, smtp.qq.com:465
; If the port ends is "465", SMTPS will be used. Using STARTTLS on port 587 is recommended per RFC 6409.
; If the server supports STARTTLS it will always be used.
HOST = smtp.mailgun.org:587
; The email from address (RFC 5322). This can be just an email address, or the `"Name" <email@example.com>` format.
FROM = noreply@gogs.localhost
; The login user.
USER = noreply@gogs.localhost
; The login password.
PASSWORD =
; Whether to disable HELO operation when the hostname is different.
DISABLE_HELO =
; The custom hostname for HELO operation, default is from system.
HELO_HOSTNAME =
; Whether to skip verifying the certificate of the server. Only use this for self-signed certificates.
SKIP_VERIFY = false
; Whether to use client certificates.
USE_CERTIFICATE = false
CERT_FILE = custom/email/cert.pem
KEY_FILE = custom/email/key.pem
; Whether to use "text/plain" as content format.
USE_PLAIN_TEXT = false
; Whether to attach a plaintext alternative to the MIME message while sending HTML emails.
; It is used to support older mail clients and make spam filters happier.
ADD_PLAIN_TEXT_ALT = false
; Attachment settings for releases
[release.attachment]
; Whether attachments are enabled. Defaults to `true`
@@ -234,37 +268,6 @@ SKIP_TLS_VERIFY = false
; Number of history information in each page
PAGING_NUM = 10
[mailer]
ENABLED = false
; Buffer length of channel, keep it as it is if you don't know what it is.
SEND_BUFFER_LEN = 100
; Prefix prepended to the subject line
SUBJECT_PREFIX = `[%(BRAND_NAME)s] `
; Mail server
; Gmail: smtp.gmail.com:587
; QQ: smtp.qq.com:465
; Note, if the port ends with "465", SMTPS will be used. Using STARTTLS on port 587 is recommended per RFC 6409. If the server supports STARTTLS it will always be used.
HOST =
; Disable HELO operation when hostname are different.
DISABLE_HELO =
; Custom hostname for HELO operation, default is from system.
HELO_HOSTNAME =
; Do not verify the certificate of the server. Only use this for self-signed certificates
SKIP_VERIFY =
; Use client certificate
USE_CERTIFICATE = false
CERT_FILE = custom/mailer/cert.pem
KEY_FILE = custom/mailer/key.pem
; Mail from address, RFC 5322. This can be just an email address, or the `"Name" <email@example.com>` format
FROM =
; Mailer user name and password
USER =
PASSWD =
; Use text/plain as format of content
USE_PLAIN_TEXT = false
; If sending html emails, then also attach a plaintext alternative to the MIME message, to support older mail clients and make spam filters happier.
ADD_PLAIN_TEXT_ALT = false
[cache]
; Either "memory", "redis", or "memcache", default is "memory"
ADAPTER = memory

View File

@@ -1235,6 +1235,24 @@ config.security.reverse_proxy_auth_user = Reverse proxy authentication header
config.security.enable_login_status_cookie = Enable login status cookie
config.security.login_status_cookie_name = Login status cookie
config.email_config = Email configuration
config.email.enabled = Enabled
config.email.subject_prefix = Subject prefix
config.email.host = Host
config.email.from = From
config.email.user = User
config.email.disable_helo = Disable HELO
config.email.helo_hostname = HELO hostname
config.email.skip_verify = Skip certificate verify
config.email.use_certificate = Use custom certificate
config.email.cert_file = Certificate file
config.email.key_file = Key file
config.email.use_plain_text = Use plain text
config.email.add_plain_text_alt = Add plain text alternative
config.email.send_test_mail = Send test email
config.email.test_mail_failed = Failed to send test email to '%s': %v
config.email.test_mail_sent = Test email has been sent to '%s'.
config.log_file_root_path = Log File Root Path
config.http_config = HTTP Configuration
@@ -1256,16 +1274,6 @@ config.queue_length = Queue Length
config.deliver_timeout = Deliver Timeout
config.skip_tls_verify = Skip TLS Verify
config.mailer_config = Mailer Configuration
config.mailer_enabled = Enabled
config.mailer_disable_helo = Disable HELO
config.mailer_subject_prefix = Subject Prefix
config.mailer_host = Host
config.mailer_user = User
config.send_test_mail = Send Test Email
config.test_mail_failed = Failed to send test email to '%s': %v
config.test_mail_sent = Test email has been sent to '%s'.
config.oauth_config = OAuth Configuration
config.oauth_enabled = Enabled

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -24,7 +24,7 @@ import (
"gogs.io/gogs/internal/db"
"gogs.io/gogs/internal/db/errors"
"gogs.io/gogs/internal/httplib"
"gogs.io/gogs/internal/mailer"
"gogs.io/gogs/internal/email"
"gogs.io/gogs/internal/template"
)
@@ -199,7 +199,7 @@ func runHookPostReceive(c *cli.Context) error {
// Post-receive hook does more than just gather Git information,
// so we need to setup additional services for email notifications.
conf.NewPostReceiveHookServices()
mailer.NewContext()
email.NewContext()
isWiki := strings.Contains(os.Getenv(db.ENV_REPO_CUSTOM_HOOKS_PATH), ".wiki.git/")

View File

@@ -208,6 +208,30 @@ func Init(customConf string) error {
}
}
// **************************
// ----- Email settings -----
// **************************
if err = File.Section("email").MapTo(&Email); err != nil {
return errors.Wrap(err, "mapping [email] section")
}
// LEGACY [0.13]: In case there are values with old section name.
if err = File.Section("mailer").MapTo(&Email); err != nil {
return errors.Wrap(err, "mapping [mailer] section")
}
if Email.Enabled {
if Email.From == "" {
Email.From = Email.User
}
parsed, err := mail.ParseAddress(Email.From)
if err != nil {
return errors.Wrapf(err, "parse mail address %q", Email.From)
}
Email.FromEmail = parsed.Address
}
handleDeprecated()
// TODO
@@ -689,71 +713,10 @@ func newSessionService() {
log.Trace("Session service is enabled")
}
// Mailer represents mail service.
type Mailer struct {
QueueLength int
SubjectPrefix string
Host string
From string
FromEmail string
User, Passwd string
DisableHelo bool
HeloHostname string
SkipVerify bool
UseCertificate bool
CertFile, KeyFile string
UsePlainText bool
AddPlainTextAlt bool
}
var (
MailService *Mailer
)
// newMailService initializes mail service options from configuration.
// No non-error log will be printed in hook mode.
func newMailService() {
sec := File.Section("mailer")
if !sec.Key("ENABLED").MustBool() {
return
}
MailService = &Mailer{
QueueLength: sec.Key("SEND_BUFFER_LEN").MustInt(100),
SubjectPrefix: sec.Key("SUBJECT_PREFIX").MustString("[" + App.BrandName + "] "),
Host: sec.Key("HOST").String(),
User: sec.Key("USER").String(),
Passwd: sec.Key("PASSWD").String(),
DisableHelo: sec.Key("DISABLE_HELO").MustBool(),
HeloHostname: sec.Key("HELO_HOSTNAME").String(),
SkipVerify: sec.Key("SKIP_VERIFY").MustBool(),
UseCertificate: sec.Key("USE_CERTIFICATE").MustBool(),
CertFile: sec.Key("CERT_FILE").String(),
KeyFile: sec.Key("KEY_FILE").String(),
UsePlainText: sec.Key("USE_PLAIN_TEXT").MustBool(),
AddPlainTextAlt: sec.Key("ADD_PLAIN_TEXT_ALT").MustBool(),
}
MailService.From = sec.Key("FROM").MustString(MailService.User)
if len(MailService.From) > 0 {
parsed, err := mail.ParseAddress(MailService.From)
if err != nil {
log.Fatal("Failed to parse value %q for '[mailer] FROM': %v", MailService.From, err)
return
}
MailService.FromEmail = parsed.Address
}
if HookMode {
return
}
log.Trace("Mail service is enabled")
}
func newRegisterMailService() {
if !File.Section("service").Key("REGISTER_EMAIL_CONFIRM").MustBool() {
return
} else if MailService == nil {
} else if !Email.Enabled {
log.Warn("Email confirmation is not enabled due to the mail service is not available")
return
}
@@ -766,7 +729,7 @@ func newRegisterMailService() {
func newNotifyMailService() {
if !File.Section("service").Key("ENABLE_NOTIFY_MAIL").MustBool() {
return
} else if MailService == nil {
} else if !Email.Enabled {
log.Warn("Email notification is not enabled due to the mail service is not available")
return
}
@@ -786,7 +749,6 @@ func NewServices() {
newService()
newCacheService()
newSessionService()
newMailService()
newRegisterMailService()
newNotifyMailService()
}
@@ -799,6 +761,5 @@ var HookMode bool
func NewPostReceiveHookServices() {
HookMode = true
newService()
newMailService()
newNotifyMailService()
}

View File

@@ -152,6 +152,33 @@ var (
EnableLoginStatusCookie bool
LoginStatusCookieName string
}
// Email settings
Email struct {
Enabled bool
SubjectPrefix string
Host string
From string
User string
Password string
DisableHELO bool `ini:"DISABLE_HELO"`
HELOHostname string `ini:"HELO_HOSTNAME"`
SkipVerify bool
UseCertificate bool
CertFile string
KeyFile string
UsePlainText bool
AddPlainTextAlt bool
// Derived from other static values
FromEmail string `ini:"-"` // Parsed email address of From without person's name.
// Deprecated: Use Password instead, will be removed in 0.13.
Passwd string
}
)
// handleDeprecated transfers deprecated values to the new ones when set.
@@ -178,4 +205,9 @@ func handleDeprecated() {
Database.Password = Database.Passwd
Database.Passwd = ""
}
if Email.Passwd != "" {
Email.Password = Email.Passwd
Email.Passwd = ""
}
}

View File

@@ -10,7 +10,7 @@ import (
"github.com/unknwon/com"
log "unknwon.dev/clog/v2"
"gogs.io/gogs/internal/mailer"
"gogs.io/gogs/internal/email"
"gogs.io/gogs/internal/markup"
"gogs.io/gogs/internal/conf"
)
@@ -44,7 +44,7 @@ func (this mailerUser) GenerateEmailActivateCode(email string) string {
return this.user.GenerateEmailActivateCode(email)
}
func NewMailerUser(u *User) mailer.User {
func NewMailerUser(u *User) email.User {
return mailerUser{u}
}
@@ -65,7 +65,7 @@ func (this mailerRepo) ComposeMetas() map[string]string {
return this.repo.ComposeMetas()
}
func NewMailerRepo(repo *Repository) mailer.Repository {
func NewMailerRepo(repo *Repository) email.Repository {
return mailerRepo{repo}
}
@@ -86,7 +86,7 @@ func (this mailerIssue) HTMLURL() string {
return this.issue.HTMLURL()
}
func NewMailerIssue(issue *Issue) mailer.Issue {
func NewMailerIssue(issue *Issue) email.Issue {
return mailerIssue{issue}
}
@@ -148,7 +148,7 @@ func mailIssueCommentToParticipants(issue *Issue, doer *User, mentions []string)
names = append(names, issue.Assignee.Name)
}
}
mailer.SendIssueCommentMail(NewMailerIssue(issue), NewMailerRepo(issue.Repo), NewMailerUser(doer), tos)
email.SendIssueCommentMail(NewMailerIssue(issue), NewMailerRepo(issue.Repo), NewMailerUser(doer), tos)
// Mail mentioned people and exclude watchers.
names = append(names, doer.Name)
@@ -160,7 +160,7 @@ func mailIssueCommentToParticipants(issue *Issue, doer *User, mentions []string)
tos = append(tos, mentions[i])
}
mailer.SendIssueMentionMail(NewMailerIssue(issue), NewMailerRepo(issue.Repo), NewMailerUser(doer), GetUserEmailsByNames(tos))
email.SendIssueMentionMail(NewMailerIssue(issue), NewMailerRepo(issue.Repo), NewMailerUser(doer), GetUserEmailsByNames(tos))
return nil
}

View File

@@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package mailer
package email
import (
"fmt"
@@ -204,7 +204,7 @@ func composeIssueMessage(issue Issue, repo Repository, doer User, tplName string
if err != nil {
log.Error("HTMLString (%s): %v", tplName, err)
}
from := gomail.NewMessage().FormatAddress(conf.MailService.FromEmail, doer.DisplayName())
from := gomail.NewMessage().FormatAddress(conf.Email.FromEmail, doer.DisplayName())
msg := NewMessageFrom(tos, from, subject, content)
msg.Info = fmt.Sprintf("Subject: %s, %s", subject, info)
return msg

View File

@@ -2,7 +2,7 @@
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package mailer
package email
import (
"crypto/tls"
@@ -34,13 +34,13 @@ func NewMessageFrom(to []string, from, subject, htmlBody string) *Message {
msg := gomail.NewMessage()
msg.SetHeader("From", from)
msg.SetHeader("To", to...)
msg.SetHeader("Subject", conf.MailService.SubjectPrefix+subject)
msg.SetHeader("Subject", conf.Email.SubjectPrefix+subject)
msg.SetDateHeader("Date", time.Now())
contentType := "text/html"
body := htmlBody
switchedToPlaintext := false
if conf.MailService.UsePlainText || conf.MailService.AddPlainTextAlt {
if conf.Email.UsePlainText || conf.Email.AddPlainTextAlt {
plainBody, err := html2text.FromString(htmlBody)
if err != nil {
log.Error("html2text.FromString: %v", err)
@@ -51,7 +51,7 @@ func NewMessageFrom(to []string, from, subject, htmlBody string) *Message {
}
}
msg.SetBody(contentType, body)
if switchedToPlaintext && conf.MailService.AddPlainTextAlt && !conf.MailService.UsePlainText {
if switchedToPlaintext && conf.Email.AddPlainTextAlt && !conf.Email.UsePlainText {
// The AddAlternative method name is confusing - adding html as an "alternative" will actually cause mail
// clients to show it as first priority, and the text "main body" is the 2nd priority fallback.
// See: https://godoc.org/gopkg.in/gomail.v2#Message.AddAlternative
@@ -65,7 +65,7 @@ func NewMessageFrom(to []string, from, subject, htmlBody string) *Message {
// NewMessage creates new mail message object with default From header.
func NewMessage(to []string, subject, body string) *Message {
return NewMessageFrom(to, conf.MailService.From, subject, body)
return NewMessageFrom(to, conf.Email.From, subject, body)
}
type loginAuth struct {
@@ -99,7 +99,7 @@ type Sender struct {
}
func (s *Sender) Send(from string, to []string, msg io.WriterTo) error {
opts := conf.MailService
opts := conf.Email
host, port, err := net.SplitHostPort(opts.Host)
if err != nil {
@@ -137,8 +137,8 @@ func (s *Sender) Send(from string, to []string, msg io.WriterTo) error {
return fmt.Errorf("NewClient: %v", err)
}
if !opts.DisableHelo {
hostname := opts.HeloHostname
if !opts.DisableHELO {
hostname := opts.HELOHostname
if len(hostname) == 0 {
hostname, err = os.Hostname()
if err != nil {
@@ -164,12 +164,12 @@ func (s *Sender) Send(from string, to []string, msg io.WriterTo) error {
var auth smtp.Auth
if strings.Contains(options, "CRAM-MD5") {
auth = smtp.CRAMMD5Auth(opts.User, opts.Passwd)
auth = smtp.CRAMMD5Auth(opts.User, opts.Password)
} else if strings.Contains(options, "PLAIN") {
auth = smtp.PlainAuth("", opts.User, opts.Passwd, host)
auth = smtp.PlainAuth("", opts.User, opts.Password, host)
} else if strings.Contains(options, "LOGIN") {
// Patch for AUTH LOGIN
auth = LoginAuth(opts.User, opts.Passwd)
auth = LoginAuth(opts.User, opts.Password)
}
if auth != nil {
@@ -225,11 +225,11 @@ func NewContext() {
// Need to check if mailQueue is nil because in during reinstall (user had installed
// before but swithed install lock off), this function will be called again
// while mail queue is already processing tasks, and produces a race condition.
if conf.MailService == nil || mailQueue != nil {
if !conf.Email.Enabled || mailQueue != nil {
return
}
mailQueue = make(chan *Message, conf.MailService.QueueLength)
mailQueue = make(chan *Message, 1000)
go processMailQueue()
}

View File

@@ -17,7 +17,7 @@ import (
"gogs.io/gogs/internal/context"
"gogs.io/gogs/internal/cron"
"gogs.io/gogs/internal/db"
"gogs.io/gogs/internal/mailer"
"gogs.io/gogs/internal/email"
"gogs.io/gogs/internal/process"
"gogs.io/gogs/internal/tool"
)
@@ -180,12 +180,12 @@ func Dashboard(c *context.Context) {
}
func SendTestMail(c *context.Context) {
email := c.Query("email")
emailAddr := c.Query("email")
// Send a test email to the user's email address and redirect back to Config
if err := mailer.SendTestMail(email); err != nil {
c.Flash.Error(c.Tr("admin.config.test_mail_failed", email, err))
if err := email.SendTestMail(emailAddr); err != nil {
c.Flash.Error(c.Tr("admin.config.email.test_mail_failed", emailAddr, err))
} else {
c.Flash.Info(c.Tr("admin.config.test_mail_sent", email))
c.Flash.Info(c.Tr("admin.config.email.test_mail_sent", emailAddr))
}
c.Redirect(conf.Server.Subpath + "/admin/config")
@@ -202,6 +202,7 @@ func Config(c *context.Context) {
c.Data["Repository"] = conf.Repository
c.Data["Database"] = conf.Database
c.Data["Security"] = conf.Security
c.Data["Email"] = conf.Email
c.Data["LogRootPath"] = conf.LogRootPath
@@ -210,12 +211,6 @@ func Config(c *context.Context) {
c.Data["Service"] = conf.Service
c.Data["Webhook"] = conf.Webhook
c.Data["MailerEnabled"] = false
if conf.MailService != nil {
c.Data["MailerEnabled"] = true
c.Data["Mailer"] = conf.MailService
}
c.Data["CacheAdapter"] = conf.CacheAdapter
c.Data["CacheInterval"] = conf.CacheInterval
c.Data["CacheConn"] = conf.CacheConn

View File

@@ -13,8 +13,8 @@ import (
"gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/context"
"gogs.io/gogs/internal/db"
"gogs.io/gogs/internal/email"
"gogs.io/gogs/internal/form"
"gogs.io/gogs/internal/mailer"
"gogs.io/gogs/internal/route"
)
@@ -53,7 +53,7 @@ func NewUser(c *context.Context) {
}
c.Data["Sources"] = sources
c.Data["CanSendEmail"] = conf.MailService != nil
c.Data["CanSendEmail"] = conf.Email.Enabled
c.HTML(200, USER_NEW)
}
@@ -69,7 +69,7 @@ func NewUserPost(c *context.Context, f form.AdminCrateUser) {
}
c.Data["Sources"] = sources
c.Data["CanSendEmail"] = conf.MailService != nil
c.Data["CanSendEmail"] = conf.Email.Enabled
if c.HasError() {
c.HTML(200, USER_NEW)
@@ -115,8 +115,8 @@ func NewUserPost(c *context.Context, f form.AdminCrateUser) {
log.Trace("Account created by admin (%s): %s", c.User.Name, u.Name)
// Send email notification.
if f.SendNotify && conf.MailService != nil {
mailer.SendRegisterNotifyMail(c.Context, db.NewMailerUser(u))
if f.SendNotify && conf.Email.Enabled {
email.SendRegisterNotifyMail(c.Context, db.NewMailerUser(u))
}
c.Flash.Success(c.Tr("admin.users.new_success", u.Name))

View File

@@ -11,12 +11,12 @@ import (
api "github.com/gogs/go-gogs-client"
"gogs.io/gogs/internal/conf"
"gogs.io/gogs/internal/context"
"gogs.io/gogs/internal/db"
"gogs.io/gogs/internal/db/errors"
"gogs.io/gogs/internal/mailer"
"gogs.io/gogs/internal/email"
"gogs.io/gogs/internal/route/api/v1/user"
"gogs.io/gogs/internal/conf"
)
func parseLoginSource(c *context.APIContext, u *db.User, sourceID int64, loginName string) {
@@ -68,8 +68,8 @@ func CreateUser(c *context.APIContext, form api.CreateUserOption) {
log.Trace("Account created by admin %q: %s", c.User.Name, u.Name)
// Send email notification.
if form.SendNotify && conf.MailService != nil {
mailer.SendRegisterNotifyMail(c.Context.Context, db.NewMailerUser(u))
if form.SendNotify && conf.Email.Enabled {
email.SendRegisterNotifyMail(c.Context.Context, db.NewMailerUser(u))
}
c.JSON(http.StatusCreated, u.APIFormat())

View File

@@ -24,8 +24,8 @@ import (
"gogs.io/gogs/internal/context"
"gogs.io/gogs/internal/cron"
"gogs.io/gogs/internal/db"
"gogs.io/gogs/internal/email"
"gogs.io/gogs/internal/form"
"gogs.io/gogs/internal/mailer"
"gogs.io/gogs/internal/markup"
"gogs.io/gogs/internal/osutil"
"gogs.io/gogs/internal/ssh"
@@ -63,8 +63,12 @@ func GlobalInit(customConf string) error {
log.Trace("Build time: %s", conf.BuildTime)
log.Trace("Build commit: %s", conf.BuildCommit)
if conf.Email.Enabled {
log.Trace("Email service is enabled")
}
conf.NewServices()
mailer.NewContext()
email.NewContext()
if conf.Security.InstallLock {
highlight.NewContext()
@@ -171,10 +175,10 @@ func Install(c *context.Context) {
f.LogRootPath = conf.LogRootPath
// E-mail service settings
if conf.MailService != nil {
f.SMTPHost = conf.MailService.Host
f.SMTPFrom = conf.MailService.From
f.SMTPUser = conf.MailService.User
if conf.Email.Enabled {
f.SMTPHost = conf.Email.Host
f.SMTPFrom = conf.Email.From
f.SMTPUser = conf.Email.User
}
f.RegisterConfirm = conf.Service.RegisterEmailConfirm
f.MailNotify = conf.Service.EnableNotifyMail

View File

@@ -19,7 +19,7 @@ import (
"gogs.io/gogs/internal/db"
"gogs.io/gogs/internal/db/errors"
"gogs.io/gogs/internal/form"
"gogs.io/gogs/internal/mailer"
"gogs.io/gogs/internal/email"
"gogs.io/gogs/internal/tool"
)
@@ -399,7 +399,7 @@ func SettingsCollaborationPost(c *context.Context) {
}
if conf.Service.EnableNotifyMail {
mailer.SendCollaboratorMail(db.NewMailerUser(u), db.NewMailerUser(c.User), db.NewMailerRepo(c.Repo.Repository))
email.SendCollaboratorMail(db.NewMailerUser(u), db.NewMailerUser(c.User), db.NewMailerRepo(c.Repo.Repository))
}
c.Flash.Success(c.Tr("repo.settings.add_collaborator_success"))

View File

@@ -15,8 +15,8 @@ import (
"gogs.io/gogs/internal/context"
"gogs.io/gogs/internal/db"
"gogs.io/gogs/internal/db/errors"
"gogs.io/gogs/internal/email"
"gogs.io/gogs/internal/form"
"gogs.io/gogs/internal/mailer"
"gogs.io/gogs/internal/tool"
)
@@ -369,7 +369,7 @@ func SignUpPost(c *context.Context, cpt *captcha.Captcha, f form.Register) {
// Send confirmation email, no need for social account.
if conf.Service.RegisterEmailConfirm && u.ID > 1 {
mailer.SendActivateAccountMail(c.Context, db.NewMailerUser(u))
email.SendActivateAccountMail(c.Context, db.NewMailerUser(u))
c.Data["IsSendRegisterMail"] = true
c.Data["Email"] = u.Email
c.Data["Hours"] = conf.Service.ActiveCodeLives / 60
@@ -398,7 +398,7 @@ func Activate(c *context.Context) {
c.Data["ResendLimited"] = true
} else {
c.Data["Hours"] = conf.Service.ActiveCodeLives / 60
mailer.SendActivateAccountMail(c.Context, db.NewMailerUser(c.User))
email.SendActivateAccountMail(c.Context, db.NewMailerUser(c.User))
if err := c.Cache.Put(c.User.MailResendCacheKey(), 1, 180); err != nil {
log.Error("Failed to put cache key 'mail resend': %v", err)
@@ -457,7 +457,7 @@ func ActivateEmail(c *context.Context) {
func ForgotPasswd(c *context.Context) {
c.Title("auth.forgot_password")
if conf.MailService == nil {
if !conf.Email.Enabled {
c.Data["IsResetDisable"] = true
c.Success(FORGOT_PASSWORD)
return
@@ -470,16 +470,16 @@ func ForgotPasswd(c *context.Context) {
func ForgotPasswdPost(c *context.Context) {
c.Title("auth.forgot_password")
if conf.MailService == nil {
if !conf.Email.Enabled {
c.Status(403)
return
}
c.Data["IsResetRequest"] = true
email := c.Query("email")
c.Data["Email"] = email
emailAddr := c.Query("email")
c.Data["Email"] = emailAddr
u, err := db.GetUserByEmail(email)
u, err := db.GetUserByEmail(emailAddr)
if err != nil {
if errors.IsUserNotExist(err) {
c.Data["Hours"] = conf.Service.ActiveCodeLives / 60
@@ -504,7 +504,7 @@ func ForgotPasswdPost(c *context.Context) {
return
}
mailer.SendResetPasswordMail(c.Context, db.NewMailerUser(u))
email.SendResetPasswordMail(c.Context, db.NewMailerUser(u))
if err = c.Cache.Put(u.MailResendCacheKey(), 1, 180); err != nil {
log.Error("Failed to put cache key 'mail resend': %v", err)
}

View File

@@ -22,8 +22,8 @@ import (
"gogs.io/gogs/internal/context"
"gogs.io/gogs/internal/db"
"gogs.io/gogs/internal/db/errors"
"gogs.io/gogs/internal/email"
"gogs.io/gogs/internal/form"
"gogs.io/gogs/internal/mailer"
"gogs.io/gogs/internal/tool"
)
@@ -259,12 +259,12 @@ func SettingsEmailPost(c *context.Context, f form.AddEmail) {
return
}
email := &db.EmailAddress{
emailAddr := &db.EmailAddress{
UID: c.User.ID,
Email: f.Email,
IsActivated: !conf.Service.RegisterEmailConfirm,
}
if err := db.AddEmailAddress(email); err != nil {
if err := db.AddEmailAddress(emailAddr); err != nil {
if db.IsErrEmailAlreadyUsed(err) {
c.RenderWithErr(c.Tr("form.email_been_used"), SETTINGS_EMAILS, &f)
} else {
@@ -275,12 +275,12 @@ func SettingsEmailPost(c *context.Context, f form.AddEmail) {
// Send confirmation email
if conf.Service.RegisterEmailConfirm {
mailer.SendActivateEmailMail(c.Context, db.NewMailerUser(c.User), email.Email)
email.SendActivateEmailMail(c.Context, db.NewMailerUser(c.User), emailAddr.Email)
if err := c.Cache.Put("MailResendLimit_"+c.User.LowerName, c.User.LowerName, 180); err != nil {
log.Error("Set cache 'MailResendLimit' failed: %v", err)
}
c.Flash.Info(c.Tr("settings.add_email_confirmation_sent", email.Email, conf.Service.ActiveCodeLives/60))
c.Flash.Info(c.Tr("settings.add_email_confirmation_sent", emailAddr.Email, conf.Service.ActiveCodeLives/60))
} else {
c.Flash.Success(c.Tr("settings.add_email_success"))
}

View File

@@ -203,6 +203,64 @@
</dl>
</div>
{{/* Email settings */}}
<h4 class="ui top attached header">
{{.i18n.Tr "admin.config.email_config"}}
</h4>
<div class="ui attached table segment">
<dl class="dl-horizontal admin-dl-horizontal">
<dt>{{.i18n.Tr "admin.config.email.enabled"}}</dt>
<dd><i class="fa fa{{if .Email.Enabled}}-check{{end}}-square-o"></i></dd>
{{if .Email.Enabled}}
<dt>{{.i18n.Tr "admin.config.email.subject_prefix"}}</dt>
<dd><code>{{.Email.SubjectPrefix}}</code></dd>
<dt>{{.i18n.Tr "admin.config.email.host"}}</dt>
<dd>{{.Email.Host}}</dd>
<dt>{{.i18n.Tr "admin.config.email.from"}}</dt>
<dd>{{.Email.From}}</dd>
<dt>{{.i18n.Tr "admin.config.email.user"}}</dt>
<dd>{{.Email.User}}</dd>
<div class="ui divider"></div>
<dt>{{.i18n.Tr "admin.config.email.disable_helo"}}</dt>
<dd><i class="fa fa{{if .Email.DisableHELO}}-check{{end}}-square-o"></i></dd>
<dt>{{.i18n.Tr "admin.config.email.helo_hostname"}}</dt>
<dd>{{if .Email.HELOHostname}}{{.Email.HELOHostname}}{{else}}{{.i18n.Tr "admin.config.not_set"}}{{end}}</dd>
<div class="ui divider"></div>
<dt>{{.i18n.Tr "admin.config.email.skip_verify"}}</dt>
<dd><i class="fa fa{{if .Email.SkipVerify}}-check{{end}}-square-o"></i></dd>
<dt>{{.i18n.Tr "admin.config.email.use_certificate"}}</dt>
<dd><i class="fa fa{{if .Email.UseCertificate}}-check{{end}}-square-o"></i></dd>
<dt>{{.i18n.Tr "admin.config.email.cert_file"}}</dt>
<dd><code>{{.Email.CertFile}}</code></dd>
<dt>{{.i18n.Tr "admin.config.email.key_file"}}</dt>
<dd><code>{{.Email.KeyFile}}</code></dd>
<div class="ui divider"></div>
<dt>{{.i18n.Tr "admin.config.email.use_plain_text"}}</dt>
<dd><i class="fa fa{{if .Email.UsePlainText}}-check{{end}}-square-o"></i></dd>
<dt>{{.i18n.Tr "admin.config.email.add_plain_text_alt"}}</dt>
<dd><i class="fa fa{{if .Email.AddPlainTextAlt}}-check{{end}}-square-o"></i></dd>
<div class="ui divider"></div>
<form class="ui form" action="{{AppSubURL}}/admin/config/test_mail" method="post">
{{.CSRFTokenHTML}}
<div class="inline field ui left">
<div class="ui input">
<input type="email" name="email" required>
</div>
</div>
<button class="ui green button" id="test-mail-btn">{{.i18n.Tr "admin.config.email.send_test_mail"}}</button>
</form>
{{end}}
</dl>
</div>
<!-- HTTP Configuration -->
<h4 class="ui top attached header">
{{.i18n.Tr "admin.config.http_config"}}
@@ -261,35 +319,6 @@
</dl>
</div>
<h4 class="ui top attached header">
{{.i18n.Tr "admin.config.mailer_config"}}
</h4>
<div class="ui attached table segment">
<dl class="dl-horizontal admin-dl-horizontal">
<dt>{{.i18n.Tr "admin.config.mailer_enabled"}}</dt>
<dd><i class="fa fa{{if .MailerEnabled}}-check{{end}}-square-o"></i></dd>
{{if .MailerEnabled}}
<dt>{{.i18n.Tr "admin.config.mailer_subject_prefix"}}</dt>
<dd><code>{{.Mailer.SubjectPrefix}}</code></dd>
<dt>{{.i18n.Tr "admin.config.mailer_disable_helo"}}</dt>
<dd><i class="fa fa{{if .Mailer.DisableHelo}}-check{{end}}-square-o"></i></dd>
<dt>{{.i18n.Tr "admin.config.mailer_host"}}</dt>
<dd>{{.Mailer.Host}}</dd>
<dt>{{.i18n.Tr "admin.config.mailer_user"}}</dt>
<dd>{{if .Mailer.User}}{{.Mailer.User}}{{else}}(empty){{end}}</dd><br>
<form class="ui form" action="{{AppSubURL}}/admin/config/test_mail" method="post">
{{.CSRFTokenHTML}}
<div class="inline field ui left">
<div class="ui input">
<input type="email" name="email" required>
</div>
</div>
<button class="ui green button" id="test-mail-btn">{{.i18n.Tr "admin.config.send_test_mail"}}</button>
</form>
{{end}}
</dl>
</div>
<h4 class="ui top attached header">
{{.i18n.Tr "admin.config.cache_config"}}
</h4>