@@ -161,6 +161,13 @@ func GetIssueCountByPoster(uid, rid int64, isClosed bool) int64 { | |||||
return count | return count | ||||
} | } | ||||
// .___ ____ ___ | |||||
// | | ______ ________ __ ____ | | \______ ___________ | |||||
// | |/ ___// ___/ | \_/ __ \| | / ___// __ \_ __ \ | |||||
// | |\___ \ \___ \| | /\ ___/| | /\___ \\ ___/| | \/ | |||||
// |___/____ >____ >____/ \___ >______//____ >\___ >__| | |||||
// \/ \/ \/ \/ \/ | |||||
// IssueUser represents an issue-user relation. | // IssueUser represents an issue-user relation. | ||||
type IssueUser struct { | type IssueUser struct { | ||||
Id int64 | Id int64 | ||||
@@ -404,6 +411,13 @@ type Label struct { | |||||
NumOpenIssues int `xorm:"-"` | NumOpenIssues int `xorm:"-"` | ||||
} | } | ||||
// _____ .__.__ __ | |||||
// / \ |__| | ____ _______/ |_ ____ ____ ____ | |||||
// / \ / \| | | _/ __ \ / ___/\ __\/ _ \ / \_/ __ \ | |||||
// / Y \ | |_\ ___/ \___ \ | | ( <_> ) | \ ___/ | |||||
// \____|__ /__|____/\___ >____ > |__| \____/|___| /\___ > | |||||
// \/ \/ \/ \/ \/ | |||||
// Milestone represents a milestone of repository. | // Milestone represents a milestone of repository. | ||||
type Milestone struct { | type Milestone struct { | ||||
Id int64 | Id int64 | ||||
@@ -517,7 +531,7 @@ func ChangeMilestoneStatus(m *Milestone, isClosed bool) (err error) { | |||||
} | } | ||||
// ChangeMilestoneAssign changes assignment of milestone for issue. | // ChangeMilestoneAssign changes assignment of milestone for issue. | ||||
func ChangeMilestoneAssign(oldMid, mid int64, isIssueClosed bool) (err error) { | |||||
func ChangeMilestoneAssign(oldMid, mid int64, issue *Issue) (err error) { | |||||
sess := orm.NewSession() | sess := orm.NewSession() | ||||
defer sess.Close() | defer sess.Close() | ||||
if err = sess.Begin(); err != nil { | if err = sess.Begin(); err != nil { | ||||
@@ -531,7 +545,7 @@ func ChangeMilestoneAssign(oldMid, mid int64, isIssueClosed bool) (err error) { | |||||
} | } | ||||
m.NumIssues-- | m.NumIssues-- | ||||
if isIssueClosed { | |||||
if issue.IsClosed { | |||||
m.NumClosedIssues-- | m.NumClosedIssues-- | ||||
} | } | ||||
if m.NumIssues > 0 { | if m.NumIssues > 0 { | ||||
@@ -543,6 +557,12 @@ func ChangeMilestoneAssign(oldMid, mid int64, isIssueClosed bool) (err error) { | |||||
sess.Rollback() | sess.Rollback() | ||||
return err | return err | ||||
} | } | ||||
rawSql := "UPDATE `issue_user` SET milestone_id = 0 WHERE issue_id = ?" | |||||
if _, err = sess.Exec(rawSql, issue.Id); err != nil { | |||||
sess.Rollback() | |||||
return err | |||||
} | |||||
} | } | ||||
if mid > 0 { | if mid > 0 { | ||||
@@ -551,7 +571,7 @@ func ChangeMilestoneAssign(oldMid, mid int64, isIssueClosed bool) (err error) { | |||||
return err | return err | ||||
} | } | ||||
m.NumIssues++ | m.NumIssues++ | ||||
if isIssueClosed { | |||||
if issue.IsClosed { | |||||
m.NumClosedIssues++ | m.NumClosedIssues++ | ||||
} | } | ||||
m.Completeness = m.NumClosedIssues * 100 / m.NumIssues | m.Completeness = m.NumClosedIssues * 100 / m.NumIssues | ||||
@@ -559,6 +579,12 @@ func ChangeMilestoneAssign(oldMid, mid int64, isIssueClosed bool) (err error) { | |||||
sess.Rollback() | sess.Rollback() | ||||
return err | return err | ||||
} | } | ||||
rawSql := "UPDATE `issue_user` SET milestone_id = ? WHERE issue_id = ?" | |||||
if _, err = sess.Exec(rawSql, m.Id, issue.Id); err != nil { | |||||
sess.Rollback() | |||||
return err | |||||
} | |||||
} | } | ||||
return sess.Commit() | return sess.Commit() | ||||
} | } | ||||
@@ -587,9 +613,22 @@ func DeleteMilestone(m *Milestone) (err error) { | |||||
sess.Rollback() | sess.Rollback() | ||||
return err | return err | ||||
} | } | ||||
rawSql = "UPDATE `issue_user` SET milestone_id = 0 WHERE milestone_id = ?" | |||||
if _, err = sess.Exec(rawSql, m.Id); err != nil { | |||||
sess.Rollback() | |||||
return err | |||||
} | |||||
return sess.Commit() | return sess.Commit() | ||||
} | } | ||||
// _________ __ | |||||
// \_ ___ \ ____ _____ _____ ____ _____/ |_ | |||||
// / \ \/ / _ \ / \ / \_/ __ \ / \ __\ | |||||
// \ \___( <_> ) Y Y \ Y Y \ ___/| | \ | | |||||
// \______ /\____/|__|_| /__|_| /\___ >___| /__| | |||||
// \/ \/ \/ \/ \/ | |||||
// Issue types. | // Issue types. | ||||
const ( | const ( | ||||
IT_PLAIN = iota // Pure comment. | IT_PLAIN = iota // Pure comment. | ||||
@@ -5,6 +5,7 @@ | |||||
package models | package models | ||||
import ( | import ( | ||||
"crypto/tls" | |||||
"encoding/json" | "encoding/json" | ||||
"errors" | "errors" | ||||
"fmt" | "fmt" | ||||
@@ -133,7 +134,7 @@ func AddSource(source *LoginSource) error { | |||||
} | } | ||||
func UpdateSource(source *LoginSource) error { | func UpdateSource(source *LoginSource) error { | ||||
_, err := orm.AllCols().Id(source.Id).Update(source) | |||||
_, err := orm.Id(source.Id).AllCols().Update(source) | |||||
return err | return err | ||||
} | } | ||||
@@ -197,7 +198,7 @@ func LoginUser(uname, passwd string) (*User, error) { | |||||
if err == nil { | if err == nil { | ||||
return u, nil | return u, nil | ||||
} else { | } else { | ||||
log.Warn("try ldap login", source.Name, "by", uname, "error:", err) | |||||
log.Warn("Fail to login(%s) by LDAP(%s): %v", uname, source.Name, err) | |||||
} | } | ||||
} else if source.Type == LT_SMTP { | } else if source.Type == LT_SMTP { | ||||
u, err := LoginUserSMTPSource(nil, uname, passwd, | u, err := LoginUserSMTPSource(nil, uname, passwd, | ||||
@@ -205,7 +206,7 @@ func LoginUser(uname, passwd string) (*User, error) { | |||||
if err == nil { | if err == nil { | ||||
return u, nil | return u, nil | ||||
} else { | } else { | ||||
log.Warn("try smtp login", source.Name, "by", uname, "error:", err) | |||||
log.Warn("Fail to login(%s) by SMTP(%s): %v", uname, source.Name, err) | |||||
} | } | ||||
} | } | ||||
} | } | ||||
@@ -217,12 +218,9 @@ func LoginUser(uname, passwd string) (*User, error) { | |||||
hasSource, err := orm.Id(u.LoginSource).Get(&source) | hasSource, err := orm.Id(u.LoginSource).Get(&source) | ||||
if err != nil { | if err != nil { | ||||
return nil, err | return nil, err | ||||
} | |||||
if !hasSource { | |||||
} else if !hasSource { | |||||
return nil, ErrLoginSourceNotExist | return nil, ErrLoginSourceNotExist | ||||
} | |||||
if !source.IsActived { | |||||
} else if !source.IsActived { | |||||
return nil, ErrLoginSourceNotActived | return nil, ErrLoginSourceNotActived | ||||
} | } | ||||
@@ -296,20 +294,25 @@ var ( | |||||
SMTPAuths = []string{SMTP_PLAIN, SMTP_LOGIN} | SMTPAuths = []string{SMTP_PLAIN, SMTP_LOGIN} | ||||
) | ) | ||||
func SmtpAuth(addr string, a smtp.Auth, tls bool) error { | |||||
c, err := smtp.Dial(addr) | |||||
func SmtpAuth(host string, port int, a smtp.Auth, useTls bool) error { | |||||
c, err := smtp.Dial(fmt.Sprintf("%s:%d", host, port)) | |||||
if err != nil { | if err != nil { | ||||
return err | return err | ||||
} | } | ||||
defer c.Close() | defer c.Close() | ||||
if tls { | |||||
if err = c.Hello("gogs"); err != nil { | |||||
return err | |||||
} | |||||
if useTls { | |||||
if ok, _ := c.Extension("STARTTLS"); ok { | if ok, _ := c.Extension("STARTTLS"); ok { | ||||
if err = c.StartTLS(nil); err != nil { | |||||
config := &tls.Config{ServerName: host} | |||||
if err = c.StartTLS(config); err != nil { | |||||
return err | return err | ||||
} | } | ||||
} else { | } else { | ||||
return errors.New("smtp server unsupported tls") | |||||
return errors.New("SMTP server unsupported TLS") | |||||
} | } | ||||
} | } | ||||
@@ -333,11 +336,13 @@ func LoginUserSMTPSource(user *User, name, passwd string, sourceId int64, cfg *S | |||||
} else if cfg.Auth == SMTP_LOGIN { | } else if cfg.Auth == SMTP_LOGIN { | ||||
auth = LoginAuth(name, passwd) | auth = LoginAuth(name, passwd) | ||||
} else { | } else { | ||||
return nil, errors.New("Unsupported smtp auth type") | |||||
return nil, errors.New("Unsupported SMTP auth type") | |||||
} | } | ||||
err := SmtpAuth(fmt.Sprintf("%s:%d", cfg.Host, cfg.Port), auth, cfg.TLS) | |||||
if err != nil { | |||||
if err := SmtpAuth(cfg.Host, cfg.Port, auth, cfg.TLS); err != nil { | |||||
if strings.Contains(err.Error(), "Username and Password not accepted") { | |||||
return nil, ErrUserNotExist | |||||
} | |||||
return nil, err | return nil, err | ||||
} | } | ||||
@@ -34,7 +34,7 @@ var ( | |||||
ErrUserNameIllegal = errors.New("User name contains illegal characters") | ErrUserNameIllegal = errors.New("User name contains illegal characters") | ||||
ErrLoginSourceNotExist = errors.New("Login source does not exist") | ErrLoginSourceNotExist = errors.New("Login source does not exist") | ||||
ErrLoginSourceNotActived = errors.New("Login source is not actived") | ErrLoginSourceNotActived = errors.New("Login source is not actived") | ||||
ErrUnsupportedLoginType = errors.New("Login source is unknow") | |||||
ErrUnsupportedLoginType = errors.New("Login source is unknown") | |||||
) | ) | ||||
// User represents the object of individual and member of organization. | // User represents the object of individual and member of organization. | ||||
@@ -72,16 +72,14 @@ func Send(msg *Message) (int, error) { | |||||
// get message body | // get message body | ||||
content := msg.Content() | content := msg.Content() | ||||
auth := smtp.PlainAuth("", base.MailService.User, base.MailService.Passwd, host[0]) | |||||
if len(msg.To) == 0 { | if len(msg.To) == 0 { | ||||
return 0, fmt.Errorf("empty receive emails") | return 0, fmt.Errorf("empty receive emails") | ||||
} | |||||
if len(msg.Body) == 0 { | |||||
} else if len(msg.Body) == 0 { | |||||
return 0, fmt.Errorf("empty email body") | return 0, fmt.Errorf("empty email body") | ||||
} | } | ||||
auth := smtp.PlainAuth("", base.MailService.User, base.MailService.Passwd, host[0]) | |||||
if msg.Massive { | if msg.Massive { | ||||
// send mail to multiple emails one by one | // send mail to multiple emails one by one | ||||
num := 0 | num := 0 | ||||
@@ -158,7 +158,7 @@ func EditAuthSourcePost(ctx *middleware.Context, form auth.AuthenticationForm) { | |||||
} | } | ||||
log.Trace("%s Authentication changed by admin(%s): %s", ctx.Req.RequestURI, | log.Trace("%s Authentication changed by admin(%s): %s", ctx.Req.RequestURI, | ||||
ctx.User.LowerName, strings.ToLower(form.AuthName)) | |||||
ctx.User.LowerName, form.AuthName) | |||||
ctx.Redirect("/admin/auths") | ctx.Redirect("/admin/auths") | ||||
} | } | ||||
@@ -426,7 +426,7 @@ func UpdateIssueMilestone(ctx *middleware.Context) { | |||||
// Not check for invalid milestone id and give responsibility to owners. | // Not check for invalid milestone id and give responsibility to owners. | ||||
issue.MilestoneId = mid | issue.MilestoneId = mid | ||||
if err = models.ChangeMilestoneAssign(oldMid, mid, issue.IsClosed); err != nil { | |||||
if err = models.ChangeMilestoneAssign(oldMid, mid, issue); err != nil { | |||||
ctx.Handle(500, "issue.UpdateIssueMilestone(ChangeMilestoneAssign)", err) | ctx.Handle(500, "issue.UpdateIssueMilestone(ChangeMilestoneAssign)", err) | ||||
return | return | ||||
} else if err = models.UpdateIssue(issue); err != nil { | } else if err = models.UpdateIssue(issue); err != nil { | ||||
@@ -19,7 +19,7 @@ | |||||
<th>Actived</th> | <th>Actived</th> | ||||
<th>Updated</th> | <th>Updated</th> | ||||
<th>Created</th> | <th>Created</th> | ||||
<th>Operation</th> | |||||
<th>Edit</th> | |||||
</tr> | </tr> | ||||
</thead> | </thead> | ||||
<tbody> | <tbody> | ||||
@@ -115,16 +115,20 @@ | |||||
<input name="smtpport" class="form-control" placeholder="Type port number" value="{{.Source.SMTP.Port}}"> | <input name="smtpport" class="form-control" placeholder="Type port number" value="{{.Source.SMTP.Port}}"> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
<!-- <div class="form-group {{if .Err_TLS}}has-error has-feedback{{end}}"> | |||||
<label class="col-md-3 control-label">TLS: </label> | |||||
<div class="col-md-7"> | |||||
<input name="tls" type="checkbox" class="form-control" {{if .Source.SMTP.TLS}}checked{{end}}> | |||||
</div> | |||||
</div> --> | |||||
{{end}} | {{end}} | ||||
<div class="form-group"> | <div class="form-group"> | ||||
{{if eq $type 3}} | |||||
<div class="col-md-offset-3 col-md-7"> | |||||
<div class="checkbox"> | |||||
<label> | |||||
<input name="tls" type="checkbox" class="form-control" {{if .Source.SMTP.TLS}}checked{{end}}> | |||||
<strong>Enable TLS Encryption</strong> | |||||
</label> | |||||
</div> | |||||
</div> | |||||
{{end}} | |||||
<div class="col-md-offset-3 col-md-7"> | <div class="col-md-offset-3 col-md-7"> | ||||
<div class="checkbox"> | <div class="checkbox"> | ||||
<label> | <label> | ||||
@@ -114,16 +114,16 @@ | |||||
</div> | </div> | ||||
</div> | </div> | ||||
<!-- <div class="form-group"> | |||||
<div class="form-group"> | |||||
<div class="col-md-offset-3 col-md-7"> | <div class="col-md-offset-3 col-md-7"> | ||||
<div class="checkbox"> | <div class="checkbox"> | ||||
<label> | <label> | ||||
<input name="tls" type="checkbox" {{if .tls}}checked{{end}}> | <input name="tls" type="checkbox" {{if .tls}}checked{{end}}> | ||||
<strong>Enable Register Confirmation</strong> | |||||
<strong>Enable TLS Encryption</strong> | |||||
</label> | </label> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
</div> --> | |||||
</div> | |||||
</div> | </div> | ||||
<div class="form-group"> | <div class="form-group"> | ||||
@@ -148,6 +148,16 @@ | |||||
</div> | </div> | ||||
</div> | </div> | ||||
<div class="panel panel-info"> | |||||
<div class="panel-heading"> | |||||
Tips | |||||
</div> | |||||
<div class="panel-body"> | |||||
<h5>GMail Setting:</h5> | |||||
<p>Host: smtp.gmail.com, Post: 587, Enable TLS Encryption: true</p> | |||||
</div> | |||||
</div> | |||||
</div> | </div> | ||||
</div> | </div> | ||||
<script> | <script> | ||||
@@ -29,7 +29,7 @@ | |||||
<div class="form-group"> | <div class="form-group"> | ||||
<label class="col-md-3 control-label">Auth Login Name: </label> | <label class="col-md-3 control-label">Auth Login Name: </label> | ||||
<div class="col-md-7"> | <div class="col-md-7"> | ||||
<input name="loginname" class="form-control" placeholder="Type auth login's username" value="{{.loginname}}"> | |||||
<input name="loginname" class="form-control" placeholder="Type auth login's username" value="{{.User.LoginName}}"> | |||||
</div> | </div> | ||||
</div> | </div> | ||||