diff --git a/README.md b/README.md index ce5fd5f6d..b43e3c98a 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Gogs(Go Git Service) is a GitHub-like clone in the Go Programming Language. Since we choose to use pure Go implementation of Git manipulation, Gogs certainly supports **ALL platforms** that Go supports, including Linux, Max OS X, and Windows with **ZERO** dependency. -##### Current version: 0.1.0 Alpha +##### Current version: 0.1.1 Alpha ## Purpose @@ -26,6 +26,7 @@ There are some very good products in this category such as [gitlab](http://gitla - User profile page. - Repository viewer. - Gravatar support. +- Mail service(register). - Supports MySQL and PostgreSQL. ## Installation diff --git a/conf/app.ini b/conf/app.ini index c2c299cf6..658f7c015 100644 --- a/conf/app.ini +++ b/conf/app.ini @@ -39,6 +39,8 @@ REGISTER_EMAIL_CONFIRM = false [mailer] ENABLED = false +; Buffer length of channel, keep it as it is if you don't know what it is. +SEND_BUFFER_LEN = 10 ; Name displayed in mail title SUBJECT = %(APP_NAME)s ; Mail server diff --git a/gogs.go b/gogs.go index 106e8b1d9..385b74217 100644 --- a/gogs.go +++ b/gogs.go @@ -20,7 +20,7 @@ import ( // Test that go1.1 tag above is included in builds. main.go refers to this definition. const go11tag = true -const APP_VER = "0.1.0.0319.1" +const APP_VER = "0.1.1.0320.1" func init() { base.AppVer = APP_VER diff --git a/models/user.go b/models/user.go index 5f08f9e92..76cf2d20c 100644 --- a/models/user.go +++ b/models/user.go @@ -5,6 +5,7 @@ package models import ( + "encoding/hex" "errors" "fmt" "os" @@ -17,6 +18,7 @@ import ( "github.com/gogits/git" "github.com/gogits/gogs/modules/base" + "github.com/gogits/gogs/modules/log" ) // User types. @@ -49,8 +51,7 @@ type User struct { Location string Website string IsActive bool - Rands string `xorm:"VARCHAR(10)"` - Expired time.Time + Rands string `xorm:"VARCHAR(10)"` Created time.Time `xorm:"created"` Updated time.Time `xorm:"updated"` } @@ -123,7 +124,6 @@ func RegisterUser(user *User) (*User, error) { user.LowerName = strings.ToLower(user.Name) user.Avatar = base.EncodeMd5(user.Email) user.AvatarEmail = user.Email - user.Expired = time.Now().Add(3 * 24 * time.Hour) user.Rands = GetUserSalt() if err = user.EncodePasswd(); err != nil { return nil, err @@ -139,9 +139,43 @@ func RegisterUser(user *User) (*User, error) { return user, nil } +// get user by erify code +func getVerifyUser(code string) (user *User) { + if len(code) <= base.TimeLimitCodeLength { + return nil + } + + // use tail hex username query user + hexStr := code[base.TimeLimitCodeLength:] + if b, err := hex.DecodeString(hexStr); err == nil { + if user, err = GetUserByName(string(b)); user != nil { + return user + } + log.Error("user.getVerifyUser: %v", err) + } + + return nil +} + +// verify active code when active account +func VerifyUserActiveCode(code string) (user *User) { + minutes := base.Service.ActiveCodeLives + + if user = getVerifyUser(code); user != nil { + // time limit code + prefix := code[:base.TimeLimitCodeLength] + data := base.ToStr(user.Id) + user.Email + user.LowerName + user.Passwd + user.Rands + + if base.VerifyTimeLimitCode(data, minutes, prefix) { + return user + } + } + return nil +} + // UpdateUser updates user's information. func UpdateUser(user *User) (err error) { - _, err = orm.Id(user.Id).Update(user) + _, err = orm.Id(user.Id).UseBool().Update(user) return err } diff --git a/modules/base/conf.go b/modules/base/conf.go index 64c97028b..17ba3b879 100644 --- a/modules/base/conf.go +++ b/modules/base/conf.go @@ -91,9 +91,11 @@ func newLogService() { case "console": config = fmt.Sprintf(`{"level":%s}`, level) case "file": + logPath := Cfg.MustValue(modeSec, "FILE_NAME", "log/gogs.log") + os.MkdirAll(path.Dir(logPath), os.ModePerm) config = fmt.Sprintf( `{"level":%s,"filename":%s,"rotate":%v,"maxlines":%d,"maxsize",%d,"daily":%v,"maxdays":%d}`, level, - Cfg.MustValue(modeSec, "FILE_NAME", "log/gogs.log"), + logPath, Cfg.MustBool(modeSec, "LOG_ROTATE", true), Cfg.MustInt(modeSec, "MAX_LINES", 1000000), 1<<uint(Cfg.MustInt(modeSec, "MAX_SIZE_SHIFT", 28)), @@ -131,15 +133,15 @@ func newMailService() { } } -func newRegisterService() { +func newRegisterMailService() { if !Cfg.MustBool("service", "REGISTER_EMAIL_CONFIRM") { return } else if MailService == nil { - log.Warn("Register Service: Mail Service is not enabled") + log.Warn("Register Mail Service: Mail Service is not enabled") return } Service.RegisterEmailConfirm = true - log.Info("Register Service Enabled") + log.Info("Register Mail Service Enabled") } func init() { @@ -172,10 +174,11 @@ func init() { AppUrl = Cfg.MustValue("server", "ROOT_URL") Domain = Cfg.MustValue("server", "DOMAIN") SecretKey = Cfg.MustValue("security", "SECRET_KEY") +} - // Extensions. +func NewServices() { newService() newLogService() newMailService() - newRegisterService() + newRegisterMailService() } diff --git a/modules/base/template.go b/modules/base/template.go index 23d3d2771..e596d1dad 100644 --- a/modules/base/template.go +++ b/modules/base/template.go @@ -8,6 +8,7 @@ import ( "container/list" "fmt" "html/template" + "strings" "time" ) @@ -54,4 +55,10 @@ var TemplateFuncs template.FuncMap = map[string]interface{}{ "ActionDesc": ActionDesc, "DateFormat": DateFormat, "List": List, + "Mail2Domain": func(mail string) string { + return "mail." + strings.Split(mail, "@")[1] + }, + "SubStr": func(str string, start, length int) string { + return str[start : start+length] + }, } diff --git a/modules/base/tool.go b/modules/base/tool.go index d0b6bfbfd..8fabb8c53 100644 --- a/modules/base/tool.go +++ b/modules/base/tool.go @@ -36,6 +36,35 @@ func GetRandomString(n int) string { return string(bytes) } +// verify time limit code +func VerifyTimeLimitCode(data string, minutes int, code string) bool { + if len(code) <= 18 { + return false + } + + // split code + start := code[:12] + lives := code[12:18] + if d, err := StrTo(lives).Int(); err == nil { + minutes = d + } + + // right active code + retCode := CreateTimeLimitCode(data, minutes, start) + if retCode == code && minutes > 0 { + // check time is expired or not + before, _ := DateParse(start, "YmdHi") + now := time.Now() + if before.Add(time.Minute*time.Duration(minutes)).Unix() > now.Unix() { + return true + } + } + + return false +} + +const TimeLimitCodeLength = 12 + 6 + 40 + // create a time limit code // code format: 12 length date time string + 6 minutes string + 40 sha1 encoded string func CreateTimeLimitCode(data string, minutes int, startInf interface{}) string { @@ -283,16 +312,24 @@ func DateFormat(t time.Time, format string) string { return t.Format(format) } -type argInt []int +// convert string to specify type -func (a argInt) Get(i int, args ...int) (r int) { - if i >= 0 && i < len(a) { - r = a[i] +type StrTo string + +func (f StrTo) Exist() bool { + return string(f) != string(0x1E) +} + +func (f StrTo) Int() (int, error) { + v, err := strconv.ParseInt(f.String(), 10, 32) + return int(v), err +} + +func (f StrTo) String() string { + if f.Exist() { + return string(f) } - if len(args) > 0 { - r = args[0] - } - return + return "" } // convert any type to string @@ -334,6 +371,18 @@ func ToStr(value interface{}, args ...int) (s string) { return s } +type argInt []int + +func (a argInt) Get(i int, args ...int) (r int) { + if i >= 0 && i < len(a) { + r = a[i] + } + if len(args) > 0 { + r = args[0] + } + return +} + type Actioner interface { GetOpType() int GetActUserName() string diff --git a/modules/mailer/mail.go b/modules/mailer/mail.go index de4f24a47..92acd20ef 100644 --- a/modules/mailer/mail.go +++ b/modules/mailer/mail.go @@ -37,9 +37,9 @@ func GetMailTmplData(user *models.User) map[interface{}]interface{} { // create a time limit code for user active func CreateUserActiveCode(user *models.User, startInf interface{}) string { - hours := base.Service.ActiveCodeLives / 60 + minutes := base.Service.ActiveCodeLives data := base.ToStr(user.Id) + user.Email + user.LowerName + user.Passwd + user.Rands - code := base.CreateTimeLimitCode(data, hours, startInf) + code := base.CreateTimeLimitCode(data, minutes, startInf) // add tail hex username code += hex.EncodeToString([]byte(user.LowerName)) @@ -62,19 +62,18 @@ func SendRegisterMail(r *middleware.Render, user *models.User) { msg := NewMailMessage([]string{user.Email}, subject, body) msg.Info = fmt.Sprintf("UID: %d, send register mail", user.Id) - // async send mail - SendAsync(msg) + SendAsync(&msg) } // Send email verify active email. func SendActiveMail(r *middleware.Render, user *models.User) { code := CreateUserActiveCode(user, nil) - subject := "Verify your email address" + subject := "Verify your e-mail address" data := GetMailTmplData(user) data["Code"] = code - body, err := r.HTMLString("mail/auth/active_email.html", data) + body, err := r.HTMLString("mail/auth/active_email", data) if err != nil { log.Error("mail.SendActiveMail(fail to render): %v", err) return @@ -83,6 +82,5 @@ func SendActiveMail(r *middleware.Render, user *models.User) { msg := NewMailMessage([]string{user.Email}, subject, body) msg.Info = fmt.Sprintf("UID: %d, send email verify mail", user.Id) - // async send mail - SendAsync(msg) + SendAsync(&msg) } diff --git a/modules/mailer/mailer.go b/modules/mailer/mailer.go index cc76acb26..3823e01fa 100644 --- a/modules/mailer/mailer.go +++ b/modules/mailer/mailer.go @@ -38,8 +38,34 @@ func (m Message) Content() string { return content } +var mailQueue chan *Message + +func init() { + mailQueue = make(chan *Message, base.Cfg.MustInt("mailer", "SEND_BUFFER_LEN", 10)) + go processMailQueue() +} + +func processMailQueue() { + for { + select { + case msg := <-mailQueue: + num, err := Send(msg) + tos := strings.Join(msg.To, "; ") + info := "" + if err != nil { + if len(msg.Info) > 0 { + info = ", info: " + msg.Info + } + log.Error(fmt.Sprintf("Async sent email %d succeed, not send emails: %s%s err: %s", num, tos, info, err)) + return + } + log.Trace(fmt.Sprintf("Async sent email %d succeed, sent emails: %s%s", num, tos, info)) + } + } +} + // Direct Send mail message -func Send(msg Message) (int, error) { +func Send(msg *Message) (int, error) { log.Trace("Sending mails to: %s", strings.Join(msg.To, "; ")) host := strings.Split(base.MailService.Host, ":") @@ -82,21 +108,9 @@ func Send(msg Message) (int, error) { } // Async Send mail message -func SendAsync(msg Message) { - // TODO may be need pools limit concurrent nums +func SendAsync(msg *Message) { go func() { - num, err := Send(msg) - tos := strings.Join(msg.To, "; ") - info := "" - if err != nil { - if len(msg.Info) > 0 { - info = ", info: " + msg.Info - } - // log failed - log.Error(fmt.Sprintf("Async sent email %d succeed, not send emails: %s%s err: %s", num, tos, info, err)) - return - } - log.Trace(fmt.Sprintf("Async sent email %d succeed, sent emails: %s%s", num, tos, info)) + mailQueue <- msg }() } diff --git a/modules/middleware/auth.go b/modules/middleware/auth.go index ed847dd83..d45a21e98 100644 --- a/modules/middleware/auth.go +++ b/modules/middleware/auth.go @@ -6,6 +6,8 @@ package middleware import ( "github.com/codegangsta/martini" + + "github.com/gogits/gogs/modules/base" ) // SignInRequire requires user to sign in. @@ -16,10 +18,10 @@ func SignInRequire(redirect bool) martini.Handler { ctx.Redirect("/") } return - } else if !ctx.User.IsActive { - // ctx.Data["Title"] = "Activate Your Account" - // ctx.Render.HTML(200, "user/active", ctx.Data) - // return + } else if !ctx.User.IsActive && base.Service.RegisterEmailConfirm { + ctx.Data["Title"] = "Activate Your Account" + ctx.Render.HTML(200, "user/active", ctx.Data) + return } } } diff --git a/routers/dev/template.go b/routers/dev/template.go new file mode 100644 index 000000000..7d5225ece --- /dev/null +++ b/routers/dev/template.go @@ -0,0 +1,25 @@ +// 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 dev + +import ( + "github.com/codegangsta/martini" + + "github.com/gogits/gogs/models" + "github.com/gogits/gogs/modules/base" + "github.com/gogits/gogs/modules/middleware" +) + +func TemplatePreview(ctx *middleware.Context, params martini.Params) { + ctx.Data["User"] = models.User{Name: "Unknown"} + ctx.Data["AppName"] = base.AppName + ctx.Data["AppVer"] = base.AppVer + ctx.Data["AppUrl"] = base.AppUrl + ctx.Data["AppLogo"] = base.AppLogo + ctx.Data["Code"] = "2014031910370000009fff6782aadb2162b4a997acb69d4400888e0b9274657374" + ctx.Data["ActiveCodeLives"] = base.Service.ActiveCodeLives / 60 + ctx.Data["ResetPwdCodeLives"] = base.Service.ResetPwdCodeLives / 60 + ctx.HTML(200, params["_1"], ctx.Data) +} diff --git a/routers/repo/repo.go b/routers/repo/repo.go index fb54d4ef8..b38473b18 100644 --- a/routers/repo/repo.go +++ b/routers/repo/repo.go @@ -13,11 +13,11 @@ import ( func Create(ctx *middleware.Context, form auth.CreateRepoForm) { ctx.Data["Title"] = "Create repository" + ctx.Data["PageIsNewRepo"] = true // For navbar arrow. + ctx.Data["LanguageIgns"] = models.LanguageIgns + ctx.Data["Licenses"] = models.Licenses if ctx.Req.Method == "GET" { - ctx.Data["PageIsNewRepo"] = true // For navbar arrow. - ctx.Data["LanguageIgns"] = models.LanguageIgns - ctx.Data["Licenses"] = models.Licenses ctx.HTML(200, "repo/create", ctx.Data) return } diff --git a/routers/repo/single.go b/routers/repo/single.go index 3ab346302..3d0447edd 100644 --- a/routers/repo/single.go +++ b/routers/repo/single.go @@ -5,6 +5,7 @@ package repo import ( + "fmt" "strings" "github.com/codegangsta/martini" @@ -137,6 +138,8 @@ func Single(ctx *middleware.Context, params martini.Params) { } } + fmt.Println(Paths) + ctx.Data["Paths"] = Paths ctx.Data["Treenames"] = treenames ctx.Data["IsRepoToolbarSource"] = true @@ -189,6 +192,9 @@ func Commits(ctx *middleware.Context, params martini.Params) { ctx.Error(404) return } + ctx.Data["Username"] = params["username"] + ctx.Data["Reponame"] = params["reponame"] + ctx.Data["CommitCount"] = commits.Len() ctx.Data["Commits"] = commits ctx.HTML(200, "repo/commits", ctx.Data) } diff --git a/routers/user/user.go b/routers/user/user.go index da70ced9f..37070af3f 100644 --- a/routers/user/user.go +++ b/routers/user/user.go @@ -243,4 +243,21 @@ func Activate(ctx *middleware.Context) { ctx.Render.HTML(200, "user/active", ctx.Data) return } + + // Verify code. + if user := models.VerifyUserActiveCode(code); user != nil { + user.IsActive = true + user.Rands = models.GetUserSalt() + models.UpdateUser(user) + + log.Trace("%s User activated: %s", ctx.Req.RequestURI, user.LowerName) + + ctx.Session.Set("userId", user.Id) + ctx.Session.Set("userName", user.Name) + ctx.Redirect("/", 302) + return + } + + ctx.Data["IsActivateFailed"] = true + ctx.Render.HTML(200, "user/active", ctx.Data) } diff --git a/templates/mail/auth/active_email.html b/templates/mail/auth/active_email.html deleted file mode 100644 index ccb120268..000000000 --- a/templates/mail/auth/active_email.html +++ /dev/null @@ -1,25 +0,0 @@ -{{template "mail/base.html" .}} -{{define "title"}} - {{if eq .Lang "zh-CN"}} - {{.User.NickName}},激活你的账户 - {{end}} - {{if eq .Lang "en-US"}} - {{.User.NickName}}, please active your account - {{end}} -{{end}} -{{define "body"}} - {{if eq .Lang "zh-CN"}} - <p style="margin:0;padding:0 0 9px 0;">点击链接验证email,{{.ActiveCodeLives}} 分钟内有效</p> - <p style="margin:0;padding:0 0 9px 0;"> - <a href="{{.AppUrl}}active/{{.Code}}">{{.AppUrl}}active/{{.Code}}</a> - </p> - <p style="margin:0;padding:0 0 9px 0;">如果链接点击无反应,请复制到浏览器打开。</p> - {{end}} - {{if eq .Lang "en-US"}} - <p style="margin:0;padding:0 0 9px 0;">Please click following link to verify your e-mail in {{.ActiveCodeLives}} hours</p> - <p style="margin:0;padding:0 0 9px 0;"> - <a href="{{.AppUrl}}active/{{.Code}}">{{.AppUrl}}active/{{.Code}}</a> - </p> - <p style="margin:0;padding:0 0 9px 0;">Copy and paste it to your browser if it's not working.</p> - {{end}} -{{end}} \ No newline at end of file diff --git a/templates/mail/base.html b/templates/mail/auth/active_email.tmpl similarity index 53% rename from templates/mail/base.html rename to templates/mail/auth/active_email.tmpl index e86c2adfc..c04ddc8a3 100644 --- a/templates/mail/base.html +++ b/templates/mail/auth/active_email.tmpl @@ -2,26 +2,30 @@ <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> -<title>{{.Title}} - {{AppName}}</title> +<title>{{.User.Name}}, please activate your account</title> </head> <body style="background:#eee;"> -<div style="color:#555; font:12px/1.5 Tahoma,Arial,sans-serif;; text-shadow:1px 1px #fff; padding:0; margin:0;"> +<div style="color:#333; font:12px/1.5 Tahoma,Arial,sans-serif;; text-shadow:1px 1px #fff; padding:0; margin:0;"> <div style="width:600px;margin:0 auto; padding:40px 0 20px;"> <div style="border:1px solid #d9d9d9;border-radius:3px; background:#fff; box-shadow: 0px 2px 5px rgba(0, 0, 0,.05); -webkit-box-shadow: 0px 2px 5px rgba(0, 0, 0,.05);"> <div style="padding: 20px 15px;"> - <h1 style="font-size:20px; padding:10px 0 20px; margin:0; border-bottom:1px solid #ddd;"><a style="color:#333;text-decoration:none;" target="_blank" href="{{.AppUrl}}">{{AppName}}</a></h1> + <h1 style="font-size:20px; padding:10px 0 20px; margin:0; border-bottom:1px solid #ddd;"><img src="{{.AppUrl}}/{{.AppLogo}}" style="height: 32px; margin-bottom: -10px;"> <a style="color:#333;text-decoration:none;" target="_blank" href="{{.AppUrl}}">{{.AppName}}</a></h1> <div style="padding:40px 15px;"> <div style="font-size:16px; padding-bottom:30px; font-weight:bold;"> - {{.Title}} + Hi <span style="color: #00BFFF;">{{.User.Name}}</span>, </div> <div style="font-size:14px; padding:0 15px;"> - {{template "body" .}} + <p style="margin:0;padding:0 0 9px 0;">Please click following link to verify your e-mail address within <b>{{.ActiveCodeLives}} hours</b>.</p> + <p style="margin:0;padding:0 0 9px 0;"> + <a href="{{.AppUrl}}user/activate?code={{.Code}}">{{.AppUrl}}user/activate?code={{.Code}}</a> + </p> + <p style="margin:0;padding:0 0 9px 0;">Copy and paste it to your browser if the link is not working.</p> </div> </div> </div> </div> <div style="color:#aaa;padding:10px;text-align:center;"> - © 2014 <a style="color:#888;text-decoration:none;" target="_blank" href="{{.AppUrl}}">{{AppName}} + © 2014 <a style="color:#888;text-decoration:none;" target="_blank" href="http://gogits.org">Gogs: Go Git Service</a> </div> </div> </div> diff --git a/templates/mail/auth/register_success.tmpl b/templates/mail/auth/register_success.tmpl index 83c085505..0a6928084 100644 --- a/templates/mail/auth/register_success.tmpl +++ b/templates/mail/auth/register_success.tmpl @@ -5,27 +5,27 @@ <title>{{.User.Name}}, welcome to {{.AppName}}</title> </head> <body style="background:#eee;"> -<div style="color:#555; font:12px/1.5 Tahoma,Arial,sans-serif;; text-shadow:1px 1px #fff; padding:0; margin:0;"> +<div style="color:#333; font:12px/1.5 Tahoma,Arial,sans-serif;; text-shadow:1px 1px #fff; padding:0; margin:0;"> <div style="width:600px;margin:0 auto; padding:40px 0 20px;"> <div style="border:1px solid #d9d9d9;border-radius:3px; background:#fff; box-shadow: 0px 2px 5px rgba(0, 0, 0,.05); -webkit-box-shadow: 0px 2px 5px rgba(0, 0, 0,.05);"> <div style="padding: 20px 15px;"> - <h1 style="font-size:20px; padding:10px 0 20px; margin:0; border-bottom:1px solid #ddd;"><a style="color:#333;text-decoration:none;" target="_blank" href="{{.AppUrl}}">{{.AppName}}</a></h1> + <h1 style="font-size:20px; padding:10px 0 20px; margin:0; border-bottom:1px solid #ddd;"><img src="{{.AppUrl}}/{{.AppLogo}}" style="height: 32px; margin-bottom: -10px;"> <a style="color:#333;text-decoration:none;" target="_blank" href="{{.AppUrl}}">{{.AppName}}</a></h1> <div style="padding:40px 15px;"> <div style="font-size:16px; padding-bottom:30px; font-weight:bold;"> - {{.User.Name}}, welcome to {{.AppName}} + Hi <span style="color: #00BFFF;">{{.User.Name}}</span>, welcome to register {{.AppName}}! </div> <div style="font-size:14px; padding:0 15px;"> - <p style="margin:0;padding:0 0 9px 0;">Please click following link to verify your e-mail in {{.ActiveCodeLives}} hours</p> + <p style="margin:0;padding:0 0 9px 0;">Please click following link to verify your e-mail address within <b>{{.ActiveCodeLives}} hours</b>.</p> <p style="margin:0;padding:0 0 9px 0;"> - <a href="{{.AppUrl}}activate/code={{.Code}}">{{.AppUrl}}active/{{.Code}}</a> + <a href="{{.AppUrl}}user/activate?code={{.Code}}">{{.AppUrl}}user/activate?code={{.Code}}</a> </p> - <p style="margin:0;padding:0 0 9px 0;">Copy and paste it to your browser if it's not working.</p> + <p style="margin:0;padding:0 0 9px 0;">Copy and paste it to your browser if the link is not working.</p> </div> </div> </div> </div> <div style="color:#aaa;padding:10px;text-align:center;"> - © 2014 <a style="color:#888;text-decoration:none;" target="_blank" href="{{.AppUrl}}">{{AppName}}</a> + © 2014 <a style="color:#888;text-decoration:none;" target="_blank" href="http://gogits.org">Gogs: Go Git Service</a> </div> </div> </div> diff --git a/templates/repo/commits.tmpl b/templates/repo/commits.tmpl index 53c14d364..8b4e41f74 100644 --- a/templates/repo/commits.tmpl +++ b/templates/repo/commits.tmpl @@ -9,45 +9,32 @@ <div class="search pull-right form"> <input class="form-control search" type="search" placeholder="search commit"/> </div> - <h4>Commits</h4> + <h4>{{.CommitCount}} Commits</h4> </div> <table class="panel-footer table commit-list table table-striped"> <thead> - <tr> - <th class="author">Author</th> - <th class="sha">Commit</th> - <th class="message">Message</th> - <th class="date">Date</th> - </tr> + <tr> + <th class="author">Author</th> + <th class="sha">Commit</th> + <th class="message">Message</th> + <th class="date">Date</th> + </tr> </thead> <tbody> + {{ $username := .Username}} + {{ $reponame := .Reponame}} + {{$r := List .Commits}} + {{range $r}} <tr> - <td class="author"><img class="avatar" src="#" alt=""/>CommitUser</td> - <td class="sha"><a class="label label-success" href="#">d91b380</a></td> - <td class="message">Fix Synchronize to delete service objects in subduers upon move</td> - <td class="date">3 years ago</td> - </tr> - <tr> - <td class="author"><img class="avatar" src="#" alt=""/>CommitUser</td> - <td class="sha"><a class="label label-success" href="#">d91b380</a></td> - <td class="message">Fix Synchronize to delete service objects in subduers upon move</td> - <td class="date">3 years ago</td> - </tr> - <tr> - <td class="author"><img class="avatar" src="#" alt=""/>CommitUser</td> - <td class="sha"><a class="label label-success" href="#">d91b380</a></td> - <td class="message">Fix Synchronize to delete service objects in subduers upon move</td> - <td class="date">3 years ago</td> + <td class="author"><img class="avatar" src="{{AvatarLink .Committer.Email}}" alt=""/>{{.Committer.Name}}</td> + <td class="sha"><a class="label label-success" href="/{{$username}}/{{$reponame}}/commit/{{.Id}} ">{{SubStr .Id.String 0 7}} </a></td> + <td class="message">{{.Message}} </td> + <td class="date">{{TimeSince .Committer.When}}</td> </tr> + {{end}} </tbody> </table> </div> - <ul> - {{$r := List .Commits}} - {{range $r}} - <li>{{.Committer.Name}} - {{.Id}} - {{.Message}} - {{.Committer.When}}</li> - {{end}} - </ul> </div> </div> {{template "base/footer" .}} \ No newline at end of file diff --git a/templates/user/active.tmpl b/templates/user/active.tmpl index 1ee723ee0..fefd7d3ae 100644 --- a/templates/user/active.tmpl +++ b/templates/user/active.tmpl @@ -1,17 +1,23 @@ {{template "base/head" .}} {{template "base/navbar" .}} <div id="gogs-body" class="container"> - <form action="/user/activate" method="post" class="form-horizontal gogs-card" id="gogs-login-card"> + <form action="/user/activate" method="get" class="form-horizontal gogs-card" id="gogs-login-card"> <h3>Activate Your Account</h3> {{if .IsActivatePage}} {{if .ServiceNotEnabled}} <p>Sorry, Register Mail Confirmation has been disabled.</p> {{else}} - <p>New confirmation e-mail has been sent to <b>{{.SignedUser.Email}}</b>, please check your inbox within {{.Hours}} hours.</p> + <p>New confirmation e-mail has been sent to <b>{{.SignedUser.Email}}</b>, please check your inbox within {{.Hours}} hours to complete your registeration.</p> + <hr/> + <a href="http://{{Mail2Domain .SignedUser.Email}}" class="btn btn-lg btn-success">Sign in to your e-mail</a> {{end}} {{else}} {{if .IsSendRegisterMail}} - <p>A confirmation e-mail has been sent to <b>{{.Email}}</b>, please check your inbox within {{.Hours}} hours.</p> + <p>A confirmation e-mail has been sent to <b>{{.Email}}</b>, please check your inbox within {{.Hours}} hours to complete your registeration.</p> + <hr/> + <a href="http://{{Mail2Domain .Email}}" class="btn btn-lg btn-success">Sign in to your e-mail</a> + {{else if .IsActivateFailed}} + <p>Sorry, your confirmation code has been exipired or not valid.</p> {{else}} <p>Hi, {{.SignedUser.Name}}, you have an unconfirmed email address(<b>{{.SignedUser.Email}}</b>). If you haven't received a confirmation e-mail or need to resend a new one, please click botton below.</p> <hr/> diff --git a/web.go b/web.go index fe3596e22..2a9df17c8 100644 --- a/web.go +++ b/web.go @@ -21,6 +21,7 @@ import ( "github.com/gogits/gogs/modules/log" "github.com/gogits/gogs/modules/middleware" "github.com/gogits/gogs/routers" + "github.com/gogits/gogs/routers/dev" "github.com/gogits/gogs/routers/repo" "github.com/gogits/gogs/routers/user" ) @@ -57,6 +58,7 @@ func newMartini() *martini.ClassicMartini { } func runWeb(*cli.Context) { + base.NewServices() checkRunMode() log.Info("%s %s", base.AppName, base.AppVer) @@ -113,6 +115,10 @@ func runWeb(*cli.Context) { m.Get("/:username/:reponame", ignSignIn, middleware.RepoAssignment(true), repo.Single) + if martini.Env == martini.Dev { + m.Get("/template/**", dev.TemplatePreview) + } + listenAddr := fmt.Sprintf("%s:%s", base.Cfg.MustValue("server", "HTTP_ADDR"), base.Cfg.MustValue("server", "HTTP_PORT", "3000"))