| @@ -5,4 +5,4 @@ gogs | |||||
| *.db | *.db | ||||
| *.log | *.log | ||||
| custom/ | custom/ | ||||
| .vendor/ | |||||
| .vendor/ | |||||
| @@ -0,0 +1,7 @@ | |||||
| filesets: | |||||
| includes: | |||||
| - templates | |||||
| - public | |||||
| - conf | |||||
| - LICENSE | |||||
| - README.md | |||||
| @@ -15,6 +15,7 @@ There are some very good products in this category such as [gitlab](http://gitla | |||||
| - Please see [Wiki](https://github.com/gogits/gogs/wiki) for project design, develop specification, change log and road map. | - Please see [Wiki](https://github.com/gogits/gogs/wiki) for project design, develop specification, change log and road map. | ||||
| - See [Trello Broad](https://trello.com/b/uxAoeLUl/gogs-go-git-service) to follow the develop team. | - See [Trello Broad](https://trello.com/b/uxAoeLUl/gogs-go-git-service) to follow the develop team. | ||||
| - Try it before anything? Go down to **Installation -> Install from binary** section!. | |||||
| ## Features | ## Features | ||||
| @@ -33,11 +34,12 @@ Make sure you install [Prerequirements](https://github.com/gogits/gogs/wiki/Prer | |||||
| There are two ways to install Gogs: | There are two ways to install Gogs: | ||||
| - [Install from binary](https://github.com/gogits/gogs/wiki/Install-from-binary) | |||||
| - [Install from binary](https://github.com/gogits/gogs/wiki/Install-from-binary): **STRONGLY RECOMMENDED** for just try and deployment! | |||||
| - [Install from source](https://github.com/gogits/gogs/wiki/Install-from-source) | - [Install from source](https://github.com/gogits/gogs/wiki/Install-from-source) | ||||
| ## Acknowledgments | ## Acknowledgments | ||||
| - Mail service is based on [WeTalk](https://github.com/beego/wetalk). | |||||
| - Logo inspired by [martini](https://github.com/martini-contrib). | - Logo inspired by [martini](https://github.com/martini-contrib). | ||||
| ## Contributors | ## Contributors | ||||
| @@ -1,28 +1,103 @@ | |||||
| # App name that shows on every page title | |||||
| ; App name that shows on every page title | |||||
| APP_NAME = Gogs: Go Git Service | APP_NAME = Gogs: Go Git Service | ||||
| # !!MUST CHANGE TO YOUR USER NAME!! | |||||
| APP_LOGO = img/favicon.png | |||||
| ; !!MUST CHANGE TO YOUR USER NAME!! | |||||
| RUN_USER = lunny | RUN_USER = lunny | ||||
| ; Either "dev", "prod" or "test", default is "dev" | |||||
| RUN_MODE = dev | |||||
| [repository] | [repository] | ||||
| ROOT = /Users/%(RUN_USER)s/git/gogs-repositories | ROOT = /Users/%(RUN_USER)s/git/gogs-repositories | ||||
| LANG_IGNS=Google Go|C|Python|Ruby|C Sharp | |||||
| LICENSES=Apache v2 License|GPL v2|MIT License|Affero GPL|BSD (3-Clause) License | |||||
| LANG_IGNS = Google Go|C|Python|Ruby|C Sharp | |||||
| LICENSES = Apache v2 License|GPL v2|MIT License|Affero GPL|BSD (3-Clause) License | |||||
| [server] | [server] | ||||
| DOMAIN = gogits.org | |||||
| DOMAIN = localhost | |||||
| ROOT_URL = http://%(DOMAIN)s:%(HTTP_PORT)s/ | |||||
| HTTP_ADDR = | HTTP_ADDR = | ||||
| HTTP_PORT = 3000 | HTTP_PORT = 3000 | ||||
| [database] | [database] | ||||
| # Either "mysql" or "postgres", it's your choice | |||||
| ; Either "mysql" or "postgres", it's your choice | |||||
| DB_TYPE = mysql | DB_TYPE = mysql | ||||
| HOST = | HOST = | ||||
| NAME = gogs | NAME = gogs | ||||
| USER = root | USER = root | ||||
| PASSWD = | PASSWD = | ||||
| # For "postgres" only, either "disable" or "verify-full" | |||||
| ; For "postgres" only, either "disable", "require" or "verify-full" | |||||
| SSL_MODE = disable | SSL_MODE = disable | ||||
| [security] | [security] | ||||
| # !!CHANGE THIS TO KEEP YOUR USER DATA SAFE!! | |||||
| USER_PASSWD_SALT = !#@FDEWREWR&*( | |||||
| ; !!CHANGE THIS TO KEEP YOUR USER DATA SAFE!! | |||||
| SECRET_KEY = !#@FDEWREWR&*( | |||||
| [service] | |||||
| ACTIVE_CODE_LIVE_MINUTES = 180 | |||||
| RESET_PASSWD_CODE_LIVE_MINUTES = 180 | |||||
| ; User need to confirm e-mail for registration | |||||
| REGISTER_EMAIL_CONFIRM = false | |||||
| [mailer] | |||||
| ENABLED = false | |||||
| ; Name displayed in mail title | |||||
| SUBJECT = %(APP_NAME)s | |||||
| ; Mail server | |||||
| ; Gmail: smtp.gmail.com:587 | |||||
| HOST = | |||||
| ; Mail from address | |||||
| FROM = | |||||
| ; Mailer user name and password | |||||
| USER = | |||||
| PASSWD = | |||||
| [log] | |||||
| ; Either "console", "file", "conn" or "smtp", default is "console" | |||||
| MODE = console | |||||
| ; Buffer length of channel, keep it as it is if you don't know what it is. | |||||
| BUFFER_LEN = 10000 | |||||
| ; Either "Trace", "Debug", "Info", "Warn", "Error", "Critical", default is "Trace" | |||||
| LEVEL = Trace | |||||
| ; For "console" mode only | |||||
| [log.console] | |||||
| LEVEL = | |||||
| ; For "file" mode only | |||||
| [log.file] | |||||
| LEVEL = | |||||
| FILE_NAME = log/gogs.log | |||||
| ; This enables automated log rotate(switch of following options), default is true | |||||
| LOG_ROTATE = true | |||||
| ; Max line number of single file, default is 1000000 | |||||
| MAX_LINES = 1000000 | |||||
| ; Max size shift of single file, default is 28 means 1 << 28, 256MB | |||||
| MAX_SIZE_SHIFT = 28 | |||||
| ; Segment log daily, default is true | |||||
| DAILY_ROTATE = true | |||||
| ; Expired days of log file(delete after max days), default is 7 | |||||
| MAX_DAYS = 7 | |||||
| ; For "conn" mode only | |||||
| [log.conn] | |||||
| LEVEL = | |||||
| ; Reconnect host for every single message, default is false | |||||
| RECONNECT_ON_MSG = false | |||||
| ; Try to reconnect when connection is lost, default is false | |||||
| RECONNECT = false | |||||
| ; Either "tcp", "unix" or "udp", default is "tcp" | |||||
| PROTOCOL = tcp | |||||
| ; Host address | |||||
| ADDR = | |||||
| ; For "smtp" mode only | |||||
| [log.smtp] | |||||
| LEVEL = | |||||
| ; Name displayed in mail title, default is "Diagnostic message from serve" | |||||
| SUBJECT = Diagnostic message from serve | |||||
| ; Mail server | |||||
| HOST = | |||||
| ; Mailer user name and password | |||||
| USER = | |||||
| PASSWD = | |||||
| ; Receivers, can be one or more, e.g. ["1@example.com","2@example.com"] | |||||
| RECEIVERS = | |||||
| @@ -20,7 +20,7 @@ import ( | |||||
| // Test that go1.1 tag above is included in builds. main.go refers to this definition. | // Test that go1.1 tag above is included in builds. main.go refers to this definition. | ||||
| const go11tag = true | const go11tag = true | ||||
| const APP_VER = "0.1.0.0318.1" | |||||
| const APP_VER = "0.1.0.0319.1" | |||||
| func init() { | func init() { | ||||
| base.AppVer = APP_VER | base.AppVer = APP_VER | ||||
| @@ -22,8 +22,8 @@ const ( | |||||
| // Action represents user operation type and information to the repository. | // Action represents user operation type and information to the repository. | ||||
| type Action struct { | type Action struct { | ||||
| Id int64 | Id int64 | ||||
| UserId int64 // Receiver user id. | |||||
| OpType int | |||||
| UserId int64 // Receiver user id. | |||||
| OpType int // Operations: CREATE DELETE STAR ... | |||||
| ActUserId int64 // Action user id. | ActUserId int64 // Action user id. | ||||
| ActUserName string // Action user name. | ActUserName string // Action user name. | ||||
| RepoId int64 | RepoId int64 | ||||
| @@ -5,6 +5,7 @@ | |||||
| package models | package models | ||||
| import ( | import ( | ||||
| "container/list" | |||||
| "errors" | "errors" | ||||
| "fmt" | "fmt" | ||||
| "io/ioutil" | "io/ioutil" | ||||
| @@ -144,10 +145,7 @@ func CreateRepository(user *User, repoName, desc, repoLang, license string, priv | |||||
| return nil, err | return nil, err | ||||
| } | } | ||||
| rawSql := "UPDATE user SET num_repos = num_repos + 1 WHERE id = ?" | |||||
| if base.Cfg.MustValue("database", "DB_TYPE") == "postgres" { | |||||
| rawSql = "UPDATE \"user\" SET num_repos = num_repos + 1 WHERE id = ?" | |||||
| } | |||||
| rawSql := "UPDATE `user` SET num_repos = num_repos + 1 WHERE id = ?" | |||||
| if _, err = session.Exec(rawSql, user.Id); err != nil { | if _, err = session.Exec(rawSql, user.Id); err != nil { | ||||
| session.Rollback() | session.Rollback() | ||||
| if err2 := os.RemoveAll(repoPath); err2 != nil { | if err2 := os.RemoveAll(repoPath); err2 != nil { | ||||
| @@ -448,7 +446,7 @@ func GetReposFiles(userName, reposName, branchName, commitId, rpath string) ([]* | |||||
| return nil, err | return nil, err | ||||
| } | } | ||||
| commit, err := GetCommit(userName, reposName, branchName, commitId) | |||||
| commit, err := repo.GetCommit(branchName, commitId) | |||||
| if err != nil { | if err != nil { | ||||
| return nil, err | return nil, err | ||||
| } | } | ||||
| @@ -464,8 +462,10 @@ func GetReposFiles(userName, reposName, branchName, commitId, rpath string) ([]* | |||||
| } | } | ||||
| var cm = commit | var cm = commit | ||||
| var i int | |||||
| for { | for { | ||||
| i = i + 1 | |||||
| //fmt.Println(".....", i, cm.Id(), cm.ParentCount()) | |||||
| if cm.ParentCount() == 0 { | if cm.ParentCount() == 0 { | ||||
| break | break | ||||
| } else if cm.ParentCount() == 1 { | } else if cm.ParentCount() == 1 { | ||||
| @@ -482,7 +482,10 @@ func GetReposFiles(userName, reposName, branchName, commitId, rpath string) ([]* | |||||
| } else { | } else { | ||||
| var emptyCnt = 0 | var emptyCnt = 0 | ||||
| var sameIdcnt = 0 | var sameIdcnt = 0 | ||||
| var lastSameCm *git.Commit | |||||
| //fmt.Println(".....", cm.ParentCount()) | |||||
| for i := 0; i < cm.ParentCount(); i++ { | for i := 0; i < cm.ParentCount(); i++ { | ||||
| //fmt.Println("parent", i, cm.Parent(i).Id()) | |||||
| p := cm.Parent(i) | p := cm.Parent(i) | ||||
| pt, _ := repo.SubTree(p.Tree, dirname) | pt, _ := repo.SubTree(p.Tree, dirname) | ||||
| var pEntry *git.TreeEntry | var pEntry *git.TreeEntry | ||||
| @@ -490,23 +493,31 @@ func GetReposFiles(userName, reposName, branchName, commitId, rpath string) ([]* | |||||
| pEntry = pt.EntryByName(entry.Name) | pEntry = pt.EntryByName(entry.Name) | ||||
| } | } | ||||
| //fmt.Println("pEntry", pEntry) | |||||
| if pEntry == nil { | if pEntry == nil { | ||||
| if emptyCnt == cm.ParentCount()-1 { | |||||
| goto loop | |||||
| } else { | |||||
| emptyCnt = emptyCnt + 1 | |||||
| continue | |||||
| emptyCnt = emptyCnt + 1 | |||||
| if emptyCnt+sameIdcnt == cm.ParentCount() { | |||||
| if lastSameCm == nil { | |||||
| goto loop | |||||
| } else { | |||||
| cm = lastSameCm | |||||
| break | |||||
| } | |||||
| } | } | ||||
| } else { | } else { | ||||
| //fmt.Println(i, "pEntry", pEntry.Id, "entry", entry.Id) | |||||
| if !pEntry.Id.Equal(entry.Id) { | if !pEntry.Id.Equal(entry.Id) { | ||||
| goto loop | goto loop | ||||
| } else { | } else { | ||||
| if sameIdcnt == cm.ParentCount()-1 { | |||||
| lastSameCm = cm.Parent(i) | |||||
| sameIdcnt = sameIdcnt + 1 | |||||
| if emptyCnt+sameIdcnt == cm.ParentCount() { | |||||
| // TODO: now follow the first parent commit? | // TODO: now follow the first parent commit? | ||||
| cm = cm.Parent(0) | |||||
| cm = lastSameCm | |||||
| //fmt.Println("sameId...") | |||||
| break | break | ||||
| } | } | ||||
| sameIdcnt = sameIdcnt + 1 | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -541,26 +552,11 @@ func GetCommit(userName, repoName, branchname, commitid string) (*git.Commit, er | |||||
| return nil, err | return nil, err | ||||
| } | } | ||||
| if commitid != "" { | |||||
| oid, err := git.NewOidFromString(commitid) | |||||
| if err != nil { | |||||
| return nil, err | |||||
| } | |||||
| return repo.LookupCommit(oid) | |||||
| } | |||||
| if branchname == "" { | |||||
| return nil, errors.New("no branch name and no commit id") | |||||
| } | |||||
| r, err := repo.LookupReference(fmt.Sprintf("refs/heads/%s", branchname)) | |||||
| if err != nil { | |||||
| return nil, err | |||||
| } | |||||
| return r.LastCommit() | |||||
| return repo.GetCommit(branchname, commitid) | |||||
| } | } | ||||
| // GetCommits returns all commits of given branch of repository. | // GetCommits returns all commits of given branch of repository. | ||||
| func GetCommits(userName, reposName, branchname string) ([]*git.Commit, error) { | |||||
| func GetCommits(userName, reposName, branchname string) (*list.List, error) { | |||||
| repo, err := git.OpenRepository(RepoPath(userName, reposName)) | repo, err := git.OpenRepository(RepoPath(userName, reposName)) | ||||
| if err != nil { | if err != nil { | ||||
| return nil, err | return nil, err | ||||
| @@ -19,14 +19,6 @@ import ( | |||||
| "github.com/gogits/gogs/modules/base" | "github.com/gogits/gogs/modules/base" | ||||
| ) | ) | ||||
| var ( | |||||
| UserPasswdSalt string | |||||
| ) | |||||
| func init() { | |||||
| UserPasswdSalt = base.Cfg.MustValue("security", "USER_PASSWD_SALT") | |||||
| } | |||||
| // User types. | // User types. | ||||
| const ( | const ( | ||||
| UT_INDIVIDUAL = iota + 1 | UT_INDIVIDUAL = iota + 1 | ||||
| @@ -56,6 +48,9 @@ type User struct { | |||||
| AvatarEmail string `xorm:"not null"` | AvatarEmail string `xorm:"not null"` | ||||
| Location string | Location string | ||||
| Website string | Website string | ||||
| IsActive bool | |||||
| Rands string `xorm:"VARCHAR(10)"` | |||||
| Expired time.Time | |||||
| Created time.Time `xorm:"created"` | Created time.Time `xorm:"created"` | ||||
| Updated time.Time `xorm:"updated"` | Updated time.Time `xorm:"updated"` | ||||
| } | } | ||||
| @@ -104,37 +99,44 @@ func (user *User) NewGitSig() *git.Signature { | |||||
| } | } | ||||
| } | } | ||||
| // return a user salt token | |||||
| func GetUserSalt() string { | |||||
| return base.GetRandomString(10) | |||||
| } | |||||
| // RegisterUser creates record of a new user. | // RegisterUser creates record of a new user. | ||||
| func RegisterUser(user *User) (err error) { | |||||
| func RegisterUser(user *User) (*User, error) { | |||||
| isExist, err := IsUserExist(user.Name) | isExist, err := IsUserExist(user.Name) | ||||
| if err != nil { | if err != nil { | ||||
| return err | |||||
| return nil, err | |||||
| } else if isExist { | } else if isExist { | ||||
| return ErrUserAlreadyExist | |||||
| return nil, ErrUserAlreadyExist | |||||
| } | } | ||||
| isExist, err = IsEmailUsed(user.Email) | isExist, err = IsEmailUsed(user.Email) | ||||
| if err != nil { | if err != nil { | ||||
| return err | |||||
| return nil, err | |||||
| } else if isExist { | } else if isExist { | ||||
| return ErrEmailAlreadyUsed | |||||
| return nil, ErrEmailAlreadyUsed | |||||
| } | } | ||||
| user.LowerName = strings.ToLower(user.Name) | user.LowerName = strings.ToLower(user.Name) | ||||
| user.Avatar = base.EncodeMd5(user.Email) | user.Avatar = base.EncodeMd5(user.Email) | ||||
| user.AvatarEmail = user.Email | user.AvatarEmail = user.Email | ||||
| user.Expired = time.Now().Add(3 * 24 * time.Hour) | |||||
| user.Rands = GetUserSalt() | |||||
| if err = user.EncodePasswd(); err != nil { | if err = user.EncodePasswd(); err != nil { | ||||
| return err | |||||
| return nil, err | |||||
| } else if _, err = orm.Insert(user); err != nil { | } else if _, err = orm.Insert(user); err != nil { | ||||
| return err | |||||
| return nil, err | |||||
| } else if err = os.MkdirAll(UserPath(user.Name), os.ModePerm); err != nil { | } else if err = os.MkdirAll(UserPath(user.Name), os.ModePerm); err != nil { | ||||
| if _, err := orm.Id(user.Id).Delete(&User{}); err != nil { | if _, err := orm.Id(user.Id).Delete(&User{}); err != nil { | ||||
| return errors.New(fmt.Sprintf( | |||||
| return nil, errors.New(fmt.Sprintf( | |||||
| "both create userpath %s and delete table record faild: %v", user.Name, err)) | "both create userpath %s and delete table record faild: %v", user.Name, err)) | ||||
| } | } | ||||
| return err | |||||
| return nil, err | |||||
| } | } | ||||
| return nil | |||||
| return user, nil | |||||
| } | } | ||||
| // UpdateUser updates user's information. | // UpdateUser updates user's information. | ||||
| @@ -183,7 +185,7 @@ func DeleteUser(user *User) error { | |||||
| // EncodePasswd encodes password to safe format. | // EncodePasswd encodes password to safe format. | ||||
| func (user *User) EncodePasswd() error { | func (user *User) EncodePasswd() error { | ||||
| newPasswd, err := scrypt.Key([]byte(user.Passwd), []byte(UserPasswdSalt), 16384, 8, 1, 64) | |||||
| newPasswd, err := scrypt.Key([]byte(user.Passwd), []byte(base.SecretKey), 16384, 8, 1, 64) | |||||
| user.Passwd = fmt.Sprintf("%x", newPasswd) | user.Passwd = fmt.Sprintf("%x", newPasswd) | ||||
| return err | return err | ||||
| } | } | ||||
| @@ -252,7 +254,7 @@ func LoginUserPlain(name, passwd string) (*User, error) { | |||||
| } else if !has { | } else if !has { | ||||
| err = ErrUserNotExist | err = ErrUserNotExist | ||||
| } | } | ||||
| return &user, nil | |||||
| return &user, err | |||||
| } | } | ||||
| // FollowUser marks someone be another's follower. | // FollowUser marks someone be another's follower. | ||||
| @@ -0,0 +1,41 @@ | |||||
| // 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 auth | |||||
| import ( | |||||
| "encoding/hex" | |||||
| "fmt" | |||||
| "github.com/gogits/gogs/models" | |||||
| "github.com/gogits/gogs/modules/base" | |||||
| "github.com/gogits/gogs/modules/mailer" | |||||
| ) | |||||
| // create a time limit code for user active | |||||
| func CreateUserActiveCode(user *models.User, startInf interface{}) string { | |||||
| hours := base.Service.ActiveCodeLives / 60 | |||||
| data := base.ToStr(user.Id) + user.Email + user.LowerName + user.Passwd + user.Rands | |||||
| code := base.CreateTimeLimitCode(data, hours, startInf) | |||||
| // add tail hex username | |||||
| code += hex.EncodeToString([]byte(user.LowerName)) | |||||
| return code | |||||
| } | |||||
| // Send user register mail with active code | |||||
| func SendRegisterMail(user *models.User) { | |||||
| code := CreateUserActiveCode(user, nil) | |||||
| subject := "Register success, Welcome" | |||||
| data := mailer.GetMailTmplData(user) | |||||
| data["Code"] = code | |||||
| body := base.RenderTemplate("mail/auth/register_success.html", data) | |||||
| msg := mailer.NewMailMessage([]string{user.Email}, subject, body) | |||||
| msg.Info = fmt.Sprintf("UID: %d, send register mail", user.Id) | |||||
| // async send mail | |||||
| mailer.SendAsync(msg) | |||||
| } | |||||
| @@ -10,18 +10,38 @@ import ( | |||||
| "os/exec" | "os/exec" | ||||
| "path" | "path" | ||||
| "path/filepath" | "path/filepath" | ||||
| "strings" | |||||
| "github.com/Unknwon/com" | "github.com/Unknwon/com" | ||||
| "github.com/Unknwon/goconfig" | "github.com/Unknwon/goconfig" | ||||
| "github.com/gogits/gogs/modules/log" | |||||
| ) | ) | ||||
| // Mailer represents a mail service. | |||||
| type Mailer struct { | |||||
| Name string | |||||
| Host string | |||||
| User, Passwd string | |||||
| } | |||||
| var ( | var ( | ||||
| AppVer string | |||||
| AppName string | |||||
| Domain string | |||||
| Cfg *goconfig.ConfigFile | |||||
| AppVer string | |||||
| AppName string | |||||
| AppLogo string | |||||
| AppUrl string | |||||
| Domain string | |||||
| SecretKey string | |||||
| Cfg *goconfig.ConfigFile | |||||
| MailService *Mailer | |||||
| ) | ) | ||||
| var Service struct { | |||||
| RegisterEmailConfirm bool | |||||
| ActiveCodeLives int | |||||
| ResetPwdCodeLives int | |||||
| } | |||||
| func exeDir() (string, error) { | func exeDir() (string, error) { | ||||
| file, err := exec.LookPath(os.Args[0]) | file, err := exec.LookPath(os.Args[0]) | ||||
| if err != nil { | if err != nil { | ||||
| @@ -34,6 +54,94 @@ func exeDir() (string, error) { | |||||
| return path.Dir(p), nil | return path.Dir(p), nil | ||||
| } | } | ||||
| var logLevels = map[string]string{ | |||||
| "Trace": "0", | |||||
| "Debug": "1", | |||||
| "Info": "2", | |||||
| "Warn": "3", | |||||
| "Error": "4", | |||||
| "Critical": "5", | |||||
| } | |||||
| func newService() { | |||||
| Service.ActiveCodeLives = Cfg.MustInt("service", "ACTIVE_CODE_LIVE_MINUTES", 180) | |||||
| Service.ResetPwdCodeLives = Cfg.MustInt("service", "RESET_PASSWD_CODE_LIVE_MINUTES", 180) | |||||
| } | |||||
| func newLogService() { | |||||
| // Get and check log mode. | |||||
| mode := Cfg.MustValue("log", "MODE", "console") | |||||
| modeSec := "log." + mode | |||||
| if _, err := Cfg.GetSection(modeSec); err != nil { | |||||
| fmt.Printf("Unknown log mode: %s\n", mode) | |||||
| os.Exit(2) | |||||
| } | |||||
| // Log level. | |||||
| levelName := Cfg.MustValue("log."+mode, "LEVEL", "Trace") | |||||
| level, ok := logLevels[levelName] | |||||
| if !ok { | |||||
| fmt.Printf("Unknown log level: %s\n", levelName) | |||||
| os.Exit(2) | |||||
| } | |||||
| // Generate log configuration. | |||||
| var config string | |||||
| switch mode { | |||||
| case "console": | |||||
| config = fmt.Sprintf(`{"level":%s}`, level) | |||||
| case "file": | |||||
| 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"), | |||||
| Cfg.MustBool(modeSec, "LOG_ROTATE", true), | |||||
| Cfg.MustInt(modeSec, "MAX_LINES", 1000000), | |||||
| 1<<uint(Cfg.MustInt(modeSec, "MAX_SIZE_SHIFT", 28)), | |||||
| Cfg.MustBool(modeSec, "DAILY_ROTATE", true), | |||||
| Cfg.MustInt(modeSec, "MAX_DAYS", 7)) | |||||
| case "conn": | |||||
| config = fmt.Sprintf(`{"level":%s,"reconnectOnMsg":%v,"reconnect":%v,"net":%s,"addr":%s}`, level, | |||||
| Cfg.MustBool(modeSec, "RECONNECT_ON_MSG", false), | |||||
| Cfg.MustBool(modeSec, "RECONNECT", false), | |||||
| Cfg.MustValue(modeSec, "PROTOCOL", "tcp"), | |||||
| Cfg.MustValue(modeSec, "ADDR", ":7020")) | |||||
| case "smtp": | |||||
| config = fmt.Sprintf(`{"level":%s,"username":%s,"password":%s,"host":%s,"sendTos":%s,"subject":%s}`, level, | |||||
| Cfg.MustValue(modeSec, "USER", "example@example.com"), | |||||
| Cfg.MustValue(modeSec, "PASSWD", "******"), | |||||
| Cfg.MustValue(modeSec, "HOST", "127.0.0.1:25"), | |||||
| Cfg.MustValue(modeSec, "RECEIVERS", "[]"), | |||||
| Cfg.MustValue(modeSec, "SUBJECT", "Diagnostic message from serve")) | |||||
| } | |||||
| log.NewLogger(Cfg.MustInt64("log", "BUFFER_LEN", 10000), mode, config) | |||||
| log.Info("Log Mode: %s(%s)", strings.Title(mode), levelName) | |||||
| } | |||||
| func newMailService() { | |||||
| // Check mailer setting. | |||||
| if Cfg.MustBool("mailer", "ENABLED") { | |||||
| MailService = &Mailer{ | |||||
| Name: Cfg.MustValue("mailer", "NAME", AppName), | |||||
| Host: Cfg.MustValue("mailer", "HOST", "127.0.0.1:25"), | |||||
| User: Cfg.MustValue("mailer", "USER", "example@example.com"), | |||||
| Passwd: Cfg.MustValue("mailer", "PASSWD", "******"), | |||||
| } | |||||
| log.Info("Mail Service Enabled") | |||||
| } | |||||
| } | |||||
| func newRegisterService() { | |||||
| if !Cfg.MustBool("service", "REGISTER_EMAIL_CONFIRM") { | |||||
| return | |||||
| } else if MailService == nil { | |||||
| log.Warn("Register Service: Mail Service is not enabled") | |||||
| return | |||||
| } | |||||
| Service.RegisterEmailConfirm = true | |||||
| log.Info("Register Service Enabled") | |||||
| } | |||||
| func init() { | func init() { | ||||
| var err error | var err error | ||||
| workDir, err := exeDir() | workDir, err := exeDir() | ||||
| @@ -59,6 +167,14 @@ func init() { | |||||
| } | } | ||||
| Cfg.BlockMode = false | Cfg.BlockMode = false | ||||
| AppName = Cfg.MustValue("", "APP_NAME") | |||||
| AppName = Cfg.MustValue("", "APP_NAME", "Gogs: Go Git Service") | |||||
| AppLogo = Cfg.MustValue("", "APP_LOGO", "img/favicon.png") | |||||
| AppUrl = Cfg.MustValue("server", "ROOT_URL") | |||||
| Domain = Cfg.MustValue("server", "DOMAIN") | Domain = Cfg.MustValue("server", "DOMAIN") | ||||
| SecretKey = Cfg.MustValue("security", "SECRET_KEY") | |||||
| // Extensions. | |||||
| newLogService() | |||||
| newMailService() | |||||
| newRegisterService() | |||||
| } | } | ||||
| @@ -5,6 +5,7 @@ | |||||
| package base | package base | ||||
| import ( | import ( | ||||
| "container/list" | |||||
| "html/template" | "html/template" | ||||
| ) | ) | ||||
| @@ -12,6 +13,23 @@ func Str2html(raw string) template.HTML { | |||||
| return template.HTML(raw) | return template.HTML(raw) | ||||
| } | } | ||||
| func Range(l int) []int { | |||||
| return make([]int, l) | |||||
| } | |||||
| func List(l *list.List) chan interface{} { | |||||
| e := l.Front() | |||||
| c := make(chan interface{}) | |||||
| go func() { | |||||
| for e != nil { | |||||
| c <- e.Value | |||||
| e = e.Next() | |||||
| } | |||||
| close(c) | |||||
| }() | |||||
| return c | |||||
| } | |||||
| var TemplateFuncs template.FuncMap = map[string]interface{}{ | var TemplateFuncs template.FuncMap = map[string]interface{}{ | ||||
| "AppName": func() string { | "AppName": func() string { | ||||
| return AppName | return AppName | ||||
| @@ -30,4 +48,5 @@ var TemplateFuncs template.FuncMap = map[string]interface{}{ | |||||
| "ActionIcon": ActionIcon, | "ActionIcon": ActionIcon, | ||||
| "ActionDesc": ActionDesc, | "ActionDesc": ActionDesc, | ||||
| "DateFormat": DateFormat, | "DateFormat": DateFormat, | ||||
| "List": List, | |||||
| } | } | ||||
| @@ -7,10 +7,13 @@ package base | |||||
| import ( | import ( | ||||
| "bytes" | "bytes" | ||||
| "crypto/md5" | "crypto/md5" | ||||
| "crypto/rand" | |||||
| "crypto/sha1" | |||||
| "encoding/hex" | "encoding/hex" | ||||
| "encoding/json" | "encoding/json" | ||||
| "fmt" | "fmt" | ||||
| "math" | "math" | ||||
| "strconv" | |||||
| "strings" | "strings" | ||||
| "time" | "time" | ||||
| ) | ) | ||||
| @@ -22,6 +25,67 @@ func EncodeMd5(str string) string { | |||||
| return hex.EncodeToString(m.Sum(nil)) | return hex.EncodeToString(m.Sum(nil)) | ||||
| } | } | ||||
| // Random generate string | |||||
| func GetRandomString(n int) string { | |||||
| const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" | |||||
| var bytes = make([]byte, n) | |||||
| rand.Read(bytes) | |||||
| for i, b := range bytes { | |||||
| bytes[i] = alphanum[b%byte(len(alphanum))] | |||||
| } | |||||
| return string(bytes) | |||||
| } | |||||
| // 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 { | |||||
| format := "YmdHi" | |||||
| var start, end time.Time | |||||
| var startStr, endStr string | |||||
| if startInf == nil { | |||||
| // Use now time create code | |||||
| start = time.Now() | |||||
| startStr = DateFormat(start, format) | |||||
| } else { | |||||
| // use start string create code | |||||
| startStr = startInf.(string) | |||||
| start, _ = DateParse(startStr, format) | |||||
| startStr = DateFormat(start, format) | |||||
| } | |||||
| end = start.Add(time.Minute * time.Duration(minutes)) | |||||
| endStr = DateFormat(end, format) | |||||
| // create sha1 encode string | |||||
| sh := sha1.New() | |||||
| sh.Write([]byte(data + SecretKey + startStr + endStr + ToStr(minutes))) | |||||
| encoded := hex.EncodeToString(sh.Sum(nil)) | |||||
| code := fmt.Sprintf("%s%06d%s", startStr, minutes, encoded) | |||||
| return code | |||||
| } | |||||
| // TODO: | |||||
| func RenderTemplate(TplNames string, Data map[interface{}]interface{}) string { | |||||
| // if beego.RunMode == "dev" { | |||||
| // beego.BuildTemplate(beego.ViewsPath) | |||||
| // } | |||||
| // ibytes := bytes.NewBufferString("") | |||||
| // if _, ok := beego.BeeTemplates[TplNames]; !ok { | |||||
| // panic("can't find templatefile in the path:" + TplNames) | |||||
| // } | |||||
| // err := beego.BeeTemplates[TplNames].ExecuteTemplate(ibytes, TplNames, Data) | |||||
| // if err != nil { | |||||
| // beego.Trace("template Execute err:", err) | |||||
| // } | |||||
| // icontent, _ := ioutil.ReadAll(ibytes) | |||||
| // return string(icontent) | |||||
| return "not implement yet" | |||||
| } | |||||
| // AvatarLink returns avatar link by given e-mail. | // AvatarLink returns avatar link by given e-mail. | ||||
| func AvatarLink(email string) string { | func AvatarLink(email string) string { | ||||
| return "http://1.gravatar.com/avatar/" + EncodeMd5(email) | return "http://1.gravatar.com/avatar/" + EncodeMd5(email) | ||||
| @@ -238,6 +302,57 @@ func DateFormat(t time.Time, format string) string { | |||||
| return t.Format(format) | return t.Format(format) | ||||
| } | } | ||||
| 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 | |||||
| } | |||||
| // convert any type to string | |||||
| func ToStr(value interface{}, args ...int) (s string) { | |||||
| switch v := value.(type) { | |||||
| case bool: | |||||
| s = strconv.FormatBool(v) | |||||
| case float32: | |||||
| s = strconv.FormatFloat(float64(v), 'f', argInt(args).Get(0, -1), argInt(args).Get(1, 32)) | |||||
| case float64: | |||||
| s = strconv.FormatFloat(v, 'f', argInt(args).Get(0, -1), argInt(args).Get(1, 64)) | |||||
| case int: | |||||
| s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10)) | |||||
| case int8: | |||||
| s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10)) | |||||
| case int16: | |||||
| s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10)) | |||||
| case int32: | |||||
| s = strconv.FormatInt(int64(v), argInt(args).Get(0, 10)) | |||||
| case int64: | |||||
| s = strconv.FormatInt(v, argInt(args).Get(0, 10)) | |||||
| case uint: | |||||
| s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10)) | |||||
| case uint8: | |||||
| s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10)) | |||||
| case uint16: | |||||
| s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10)) | |||||
| case uint32: | |||||
| s = strconv.FormatUint(uint64(v), argInt(args).Get(0, 10)) | |||||
| case uint64: | |||||
| s = strconv.FormatUint(v, argInt(args).Get(0, 10)) | |||||
| case string: | |||||
| s = v | |||||
| case []byte: | |||||
| s = string(v) | |||||
| default: | |||||
| s = fmt.Sprintf("%v", v) | |||||
| } | |||||
| return s | |||||
| } | |||||
| type Actioner interface { | type Actioner interface { | ||||
| GetOpType() int | GetOpType() int | ||||
| GetActUserName() string | GetActUserName() string | ||||
| @@ -11,9 +11,9 @@ import ( | |||||
| var logger *logs.BeeLogger | var logger *logs.BeeLogger | ||||
| func init() { | |||||
| logger = logs.NewLogger(10000) | |||||
| logger.SetLogger("console", "") | |||||
| func NewLogger(bufLen int64, mode, config string) { | |||||
| logger = logs.NewLogger(bufLen) | |||||
| logger.SetLogger(mode, config) | |||||
| } | } | ||||
| func Trace(format string, v ...interface{}) { | func Trace(format string, v ...interface{}) { | ||||
| @@ -0,0 +1,31 @@ | |||||
| // 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 ( | |||||
| "github.com/gogits/gogs/models" | |||||
| "github.com/gogits/gogs/modules/base" | |||||
| ) | |||||
| // Create New mail message use MailFrom and MailUser | |||||
| func NewMailMessage(To []string, subject, body string) Message { | |||||
| msg := NewHtmlMessage(To, base.MailService.User, subject, body) | |||||
| msg.User = base.MailService.User | |||||
| return msg | |||||
| } | |||||
| func GetMailTmplData(user *models.User) map[interface{}]interface{} { | |||||
| data := make(map[interface{}]interface{}, 10) | |||||
| data["AppName"] = base.AppName | |||||
| data["AppVer"] = base.AppVer | |||||
| data["AppUrl"] = base.AppUrl | |||||
| data["AppLogo"] = base.AppLogo | |||||
| data["ActiveCodeLives"] = base.Service.ActiveCodeLives | |||||
| data["ResetPwdCodeLives"] = base.Service.ResetPwdCodeLives | |||||
| if user != nil { | |||||
| data["User"] = user | |||||
| } | |||||
| return data | |||||
| } | |||||
| @@ -0,0 +1,112 @@ | |||||
| // 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" | |||||
| "net/smtp" | |||||
| "strings" | |||||
| "github.com/gogits/gogs/modules/base" | |||||
| "github.com/gogits/gogs/modules/log" | |||||
| ) | |||||
| type Message struct { | |||||
| To []string | |||||
| From string | |||||
| Subject string | |||||
| Body string | |||||
| User string | |||||
| Type string | |||||
| Massive bool | |||||
| Info string | |||||
| } | |||||
| // create mail content | |||||
| func (m Message) Content() string { | |||||
| // set mail type | |||||
| contentType := "text/plain; charset=UTF-8" | |||||
| if m.Type == "html" { | |||||
| contentType = "text/html; charset=UTF-8" | |||||
| } | |||||
| // create mail content | |||||
| content := "From: " + m.User + "<" + m.From + | |||||
| ">\r\nSubject: " + m.Subject + "\r\nContent-Type: " + contentType + "\r\n\r\n" + m.Body | |||||
| return content | |||||
| } | |||||
| // Direct Send mail message | |||||
| func Send(msg Message) (int, error) { | |||||
| log.Trace("Sending mails to: %s", strings.Join(msg.To, "; ")) | |||||
| host := strings.Split(base.MailService.Host, ":") | |||||
| // get message body | |||||
| content := msg.Content() | |||||
| auth := smtp.PlainAuth("", base.MailService.User, base.MailService.Passwd, host[0]) | |||||
| if len(msg.To) == 0 { | |||||
| return 0, fmt.Errorf("empty receive emails") | |||||
| } | |||||
| if len(msg.Body) == 0 { | |||||
| return 0, fmt.Errorf("empty email body") | |||||
| } | |||||
| if msg.Massive { | |||||
| // send mail to multiple emails one by one | |||||
| num := 0 | |||||
| for _, to := range msg.To { | |||||
| body := []byte("To: " + to + "\r\n" + content) | |||||
| err := smtp.SendMail(base.MailService.Host, auth, msg.From, []string{to}, body) | |||||
| if err != nil { | |||||
| return num, err | |||||
| } | |||||
| num++ | |||||
| } | |||||
| return num, nil | |||||
| } else { | |||||
| body := []byte("To: " + strings.Join(msg.To, ";") + "\r\n" + content) | |||||
| // send to multiple emails in one message | |||||
| err := smtp.SendMail(base.MailService.Host, auth, msg.From, msg.To, body) | |||||
| if err != nil { | |||||
| return 0, err | |||||
| } else { | |||||
| return 1, nil | |||||
| } | |||||
| } | |||||
| } | |||||
| // Async Send mail message | |||||
| func SendAsync(msg Message) { | |||||
| // TODO may be need pools limit concurrent nums | |||||
| 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)) | |||||
| }() | |||||
| } | |||||
| // Create html mail message | |||||
| func NewHtmlMessage(To []string, From, Subject, Body string) Message { | |||||
| return Message{ | |||||
| To: To, | |||||
| From: From, | |||||
| Subject: Subject, | |||||
| Body: Body, | |||||
| Type: "html", | |||||
| } | |||||
| } | |||||
| @@ -67,8 +67,13 @@ func (ctx *Context) RenderWithErr(msg, tpl string, form auth.Form) { | |||||
| // Handle handles and logs error by given status. | // Handle handles and logs error by given status. | ||||
| func (ctx *Context) Handle(status int, title string, err error) { | func (ctx *Context) Handle(status int, title string, err error) { | ||||
| ctx.Data["ErrorMsg"] = err | |||||
| log.Error("%s: %v", title, err) | log.Error("%s: %v", title, err) | ||||
| if martini.Dev == martini.Prod { | |||||
| ctx.Render.HTML(500, "status/500", ctx.Data) | |||||
| return | |||||
| } | |||||
| ctx.Data["ErrorMsg"] = err | |||||
| ctx.Render.HTML(status, fmt.Sprintf("status/%d", status), ctx.Data) | ctx.Render.HTML(status, fmt.Sprintf("status/%d", status), ctx.Data) | ||||
| } | } | ||||
| @@ -0,0 +1,46 @@ | |||||
| // 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 middleware | |||||
| import ( | |||||
| "fmt" | |||||
| "log" | |||||
| "net/http" | |||||
| "runtime" | |||||
| "time" | |||||
| "github.com/codegangsta/martini" | |||||
| ) | |||||
| var isWindows bool | |||||
| func init() { | |||||
| isWindows = runtime.GOOS == "windows" | |||||
| } | |||||
| func Logger() martini.Handler { | |||||
| return func(res http.ResponseWriter, req *http.Request, ctx martini.Context, log *log.Logger) { | |||||
| start := time.Now() | |||||
| log.Printf("Started %s %s", req.Method, req.URL.Path) | |||||
| rw := res.(martini.ResponseWriter) | |||||
| ctx.Next() | |||||
| content := fmt.Sprintf("Completed %v %s in %v", rw.Status(), http.StatusText(rw.Status()), time.Since(start)) | |||||
| if !isWindows { | |||||
| switch rw.Status() { | |||||
| case 200: | |||||
| content = fmt.Sprintf("\033[1;32m%s\033[0m", content) | |||||
| case 304: | |||||
| content = fmt.Sprintf("\033[1;33m%s\033[0m", content) | |||||
| case 404: | |||||
| content = fmt.Sprintf("\033[1;31m%s\033[0m", content) | |||||
| case 500: | |||||
| content = fmt.Sprintf("\033[1;36m%s\033[0m", content) | |||||
| } | |||||
| } | |||||
| log.Println(content) | |||||
| } | |||||
| } | |||||
| @@ -575,6 +575,15 @@ html, body { | |||||
| padding: 9px 20px; | padding: 9px 20px; | ||||
| } | } | ||||
| .info-box .info-head { | |||||
| font-weight: normal; | |||||
| } | |||||
| .info-box .info-content a, | |||||
| .info-box .info-head a { | |||||
| color: #666; | |||||
| } | |||||
| .file-list { | .file-list { | ||||
| background-color: #fafafa; | background-color: #fafafa; | ||||
| } | } | ||||
| @@ -105,7 +105,7 @@ | |||||
| .markdown > pre { | .markdown > pre { | ||||
| line-height: 1.6; | line-height: 1.6; | ||||
| overflow: auto; | overflow: auto; | ||||
| background: #fff; | |||||
| background: #f8f8f8; | |||||
| padding: 6px 10px; | padding: 6px 10px; | ||||
| border: 1px solid #ddd; | border: 1px solid #ddd; | ||||
| } | } | ||||
| @@ -18,6 +18,7 @@ func Home(ctx *middleware.Context) { | |||||
| ctx.Render.HTML(200, "home", ctx.Data) | ctx.Render.HTML(200, "home", ctx.Data) | ||||
| } | } | ||||
| func Help(ctx *middleware.Context) string { | |||||
| return "This is help page" | |||||
| func Help(ctx *middleware.Context) { | |||||
| ctx.Data["PageIsHelp"] = true | |||||
| ctx.Render.HTML(200, "help", ctx.Data) | |||||
| } | } | ||||
| @@ -7,6 +7,7 @@ package repo | |||||
| import ( | import ( | ||||
| "github.com/gogits/gogs/models" | "github.com/gogits/gogs/models" | ||||
| "github.com/gogits/gogs/modules/auth" | "github.com/gogits/gogs/modules/auth" | ||||
| "github.com/gogits/gogs/modules/log" | |||||
| "github.com/gogits/gogs/modules/middleware" | "github.com/gogits/gogs/modules/middleware" | ||||
| ) | ) | ||||
| @@ -23,6 +24,7 @@ func Create(ctx *middleware.Context, form auth.CreateRepoForm) { | |||||
| _, err := models.CreateRepository(ctx.User, form.RepoName, form.Description, | _, err := models.CreateRepository(ctx.User, form.RepoName, form.Description, | ||||
| form.Language, form.License, form.Visibility == "private", form.InitReadme == "on") | form.Language, form.License, form.Visibility == "private", form.InitReadme == "on") | ||||
| if err == nil { | if err == nil { | ||||
| log.Trace("%s Repository created: %s/%s", ctx.Req.RequestURI, ctx.User.LowerName, form.RepoName) | |||||
| ctx.Render.Redirect("/"+ctx.User.Name+"/"+form.RepoName, 302) | ctx.Render.Redirect("/"+ctx.User.Name+"/"+form.RepoName, 302) | ||||
| return | return | ||||
| } else if err == models.ErrRepoAlreadyExist { | } else if err == models.ErrRepoAlreadyExist { | ||||
| @@ -52,5 +54,6 @@ func SettingPost(ctx *middleware.Context) { | |||||
| } | } | ||||
| } | } | ||||
| log.Trace("%s Repository deleted: %s/%s", ctx.Req.RequestURI, ctx.User.LowerName, ctx.Repo.Repository.LowerName) | |||||
| ctx.Render.Redirect("/", 302) | ctx.Render.Redirect("/", 302) | ||||
| } | } | ||||
| @@ -182,10 +182,12 @@ func Commits(ctx *middleware.Context, params martini.Params) { | |||||
| ctx.Render.HTML(200, "repo/commits", ctx.Data) | ctx.Render.HTML(200, "repo/commits", ctx.Data) | ||||
| } | } | ||||
| func Issues(ctx *middleware.Context) string { | |||||
| return "This is issues page" | |||||
| func Issues(ctx *middleware.Context) { | |||||
| ctx.Data["IsRepoToolbarIssues"] = true | |||||
| ctx.Render.HTML(200, "repo/issues", ctx.Data) | |||||
| } | } | ||||
| func Pulls(ctx *middleware.Context) string { | |||||
| return "This is pulls page" | |||||
| func Pulls(ctx *middleware.Context) { | |||||
| ctx.Data["IsRepoToolbarPulls"] = true | |||||
| ctx.Render.HTML(200, "repo/pulls", ctx.Data) | |||||
| } | } | ||||
| @@ -14,9 +14,11 @@ import ( | |||||
| "github.com/gogits/gogs/modules/middleware" | "github.com/gogits/gogs/modules/middleware" | ||||
| ) | ) | ||||
| // Render user setting page (email, website modify) | |||||
| func Setting(ctx *middleware.Context, form auth.UpdateProfileForm) { | func Setting(ctx *middleware.Context, form auth.UpdateProfileForm) { | ||||
| ctx.Data["Title"] = "Setting" | ctx.Data["Title"] = "Setting" | ||||
| ctx.Data["PageIsUserSetting"] = true | |||||
| ctx.Data["PageIsUserSetting"] = true // For navbar arrow. | |||||
| ctx.Data["IsUserPageSetting"] = true // For setting nav highlight. | |||||
| user := ctx.User | user := ctx.User | ||||
| ctx.Data["Owner"] = user | ctx.Data["Owner"] = user | ||||
| @@ -26,6 +28,7 @@ func Setting(ctx *middleware.Context, form auth.UpdateProfileForm) { | |||||
| return | return | ||||
| } | } | ||||
| // below is for POST requests | |||||
| if hasErr, ok := ctx.Data["HasError"]; ok && hasErr.(bool) { | if hasErr, ok := ctx.Data["HasError"]; ok && hasErr.(bool) { | ||||
| ctx.Render.HTML(200, "user/setting", ctx.Data) | ctx.Render.HTML(200, "user/setting", ctx.Data) | ||||
| return | return | ||||
| @@ -43,11 +46,13 @@ func Setting(ctx *middleware.Context, form auth.UpdateProfileForm) { | |||||
| ctx.Data["IsSuccess"] = true | ctx.Data["IsSuccess"] = true | ||||
| ctx.Render.HTML(200, "user/setting", ctx.Data) | ctx.Render.HTML(200, "user/setting", ctx.Data) | ||||
| log.Trace("%s User setting updated: %s", ctx.Req.RequestURI, ctx.User.LowerName) | |||||
| } | } | ||||
| func SettingPassword(ctx *middleware.Context, form auth.UpdatePasswdForm) { | func SettingPassword(ctx *middleware.Context, form auth.UpdatePasswdForm) { | ||||
| ctx.Data["Title"] = "Password" | ctx.Data["Title"] = "Password" | ||||
| ctx.Data["PageIsUserSetting"] = true | ctx.Data["PageIsUserSetting"] = true | ||||
| ctx.Data["IsUserPageSettingPasswd"] = true | |||||
| if ctx.Req.Method == "GET" { | if ctx.Req.Method == "GET" { | ||||
| ctx.Render.HTML(200, "user/password", ctx.Data) | ctx.Render.HTML(200, "user/password", ctx.Data) | ||||
| @@ -78,6 +83,7 @@ func SettingPassword(ctx *middleware.Context, form auth.UpdatePasswdForm) { | |||||
| ctx.Data["Owner"] = user | ctx.Data["Owner"] = user | ||||
| ctx.Render.HTML(200, "user/password", ctx.Data) | ctx.Render.HTML(200, "user/password", ctx.Data) | ||||
| log.Trace("%s User password updated: %s", ctx.Req.RequestURI, ctx.User.LowerName) | |||||
| } | } | ||||
| func SettingSSHKeys(ctx *middleware.Context, form auth.AddSSHKeyForm) { | func SettingSSHKeys(ctx *middleware.Context, form auth.AddSSHKeyForm) { | ||||
| @@ -108,6 +114,7 @@ func SettingSSHKeys(ctx *middleware.Context, form auth.AddSSHKeyForm) { | |||||
| "err": err.Error(), | "err": err.Error(), | ||||
| }) | }) | ||||
| } else { | } else { | ||||
| log.Trace("%s User SSH key deleted: %s", ctx.Req.RequestURI, ctx.User.LowerName) | |||||
| ctx.Render.JSON(200, map[string]interface{}{ | ctx.Render.JSON(200, map[string]interface{}{ | ||||
| "ok": true, | "ok": true, | ||||
| }) | }) | ||||
| @@ -133,6 +140,7 @@ func SettingSSHKeys(ctx *middleware.Context, form auth.AddSSHKeyForm) { | |||||
| return | return | ||||
| } | } | ||||
| ctx.Handle(200, "ssh.AddPublicKey", err) | ctx.Handle(200, "ssh.AddPublicKey", err) | ||||
| log.Trace("%s User SSH key added: %s", ctx.Req.RequestURI, ctx.User.LowerName) | |||||
| return | return | ||||
| } else { | } else { | ||||
| ctx.Data["AddSSHKeySuccess"] = true | ctx.Data["AddSSHKeySuccess"] = true | ||||
| @@ -147,20 +155,23 @@ func SettingSSHKeys(ctx *middleware.Context, form auth.AddSSHKeyForm) { | |||||
| } | } | ||||
| ctx.Data["PageIsUserSetting"] = true | ctx.Data["PageIsUserSetting"] = true | ||||
| ctx.Data["IsUserPageSettingSSH"] = true | |||||
| ctx.Data["Keys"] = keys | ctx.Data["Keys"] = keys | ||||
| ctx.Render.HTML(200, "user/publickey", ctx.Data) | ctx.Render.HTML(200, "user/publickey", ctx.Data) | ||||
| } | } | ||||
| func SettingNotification(ctx *middleware.Context) { | func SettingNotification(ctx *middleware.Context) { | ||||
| // todo user setting notification | |||||
| // TODO: user setting notification | |||||
| ctx.Data["Title"] = "Notification" | ctx.Data["Title"] = "Notification" | ||||
| ctx.Data["PageIsUserSetting"] = true | ctx.Data["PageIsUserSetting"] = true | ||||
| ctx.Data["IsUserPageSettingNotify"] = true | |||||
| ctx.Render.HTML(200, "user/notification", ctx.Data) | ctx.Render.HTML(200, "user/notification", ctx.Data) | ||||
| } | } | ||||
| func SettingSecurity(ctx *middleware.Context) { | func SettingSecurity(ctx *middleware.Context) { | ||||
| // todo user setting security | |||||
| // TODO: user setting security | |||||
| ctx.Data["Title"] = "Security" | ctx.Data["Title"] = "Security" | ||||
| ctx.Data["PageIsUserSetting"] = true | ctx.Data["PageIsUserSetting"] = true | ||||
| ctx.Data["IsUserPageSettingSecurity"] = true | |||||
| ctx.Render.HTML(200, "user/security", ctx.Data) | ctx.Render.HTML(200, "user/security", ctx.Data) | ||||
| } | } | ||||
| @@ -6,6 +6,7 @@ package user | |||||
| import ( | import ( | ||||
| "fmt" | "fmt" | ||||
| "strings" | |||||
| "github.com/codegangsta/martini" | "github.com/codegangsta/martini" | ||||
| "github.com/martini-contrib/render" | "github.com/martini-contrib/render" | ||||
| @@ -14,6 +15,7 @@ import ( | |||||
| "github.com/gogits/gogs/models" | "github.com/gogits/gogs/models" | ||||
| "github.com/gogits/gogs/modules/auth" | "github.com/gogits/gogs/modules/auth" | ||||
| "github.com/gogits/gogs/modules/base" | "github.com/gogits/gogs/modules/base" | ||||
| "github.com/gogits/gogs/modules/log" | |||||
| "github.com/gogits/gogs/modules/middleware" | "github.com/gogits/gogs/modules/middleware" | ||||
| ) | ) | ||||
| @@ -129,12 +131,14 @@ func SignUp(ctx *middleware.Context, form auth.RegisterForm) { | |||||
| } | } | ||||
| u := &models.User{ | u := &models.User{ | ||||
| Name: form.UserName, | |||||
| Email: form.Email, | |||||
| Passwd: form.Password, | |||||
| Name: form.UserName, | |||||
| Email: form.Email, | |||||
| Passwd: form.Password, | |||||
| IsActive: !base.Service.RegisterEmailConfirm, | |||||
| } | } | ||||
| if err := models.RegisterUser(u); err != nil { | |||||
| var err error | |||||
| if u, err = models.RegisterUser(u); err != nil { | |||||
| switch err.Error() { | switch err.Error() { | ||||
| case models.ErrUserAlreadyExist.Error(): | case models.ErrUserAlreadyExist.Error(): | ||||
| ctx.RenderWithErr("Username has been already taken", "user/signup", &form) | ctx.RenderWithErr("Username has been already taken", "user/signup", &form) | ||||
| @@ -146,11 +150,19 @@ func SignUp(ctx *middleware.Context, form auth.RegisterForm) { | |||||
| return | return | ||||
| } | } | ||||
| log.Trace("%s User created: %s", ctx.Req.RequestURI, strings.ToLower(form.UserName)) | |||||
| // Send confirmation e-mail. | |||||
| if base.Service.RegisterEmailConfirm { | |||||
| auth.SendRegisterMail(u) | |||||
| } | |||||
| ctx.Render.Redirect("/user/login") | ctx.Render.Redirect("/user/login") | ||||
| } | } | ||||
| func Delete(ctx *middleware.Context) { | func Delete(ctx *middleware.Context) { | ||||
| ctx.Data["Title"] = "Delete Account" | ctx.Data["Title"] = "Delete Account" | ||||
| ctx.Data["PageIsUserSetting"] = true | |||||
| ctx.Data["IsUserPageSettingDelete"] = true | |||||
| if ctx.Req.Method == "GET" { | if ctx.Req.Method == "GET" { | ||||
| ctx.Render.HTML(200, "user/delete", ctx.Data) | ctx.Render.HTML(200, "user/delete", ctx.Data) | ||||
| @@ -182,7 +194,7 @@ func Delete(ctx *middleware.Context) { | |||||
| } | } | ||||
| const ( | const ( | ||||
| feedTpl = `<i class="icon fa fa-%s"></i> | |||||
| TPL_FEED = `<i class="icon fa fa-%s"></i> | |||||
| <div class="info"><span class="meta">%s</span><br>%s</div>` | <div class="info"><span class="meta">%s</span><br>%s</div>` | ||||
| ) | ) | ||||
| @@ -194,20 +206,20 @@ func Feeds(ctx *middleware.Context, form auth.FeedsForm) { | |||||
| feeds := make([]string, len(actions)) | feeds := make([]string, len(actions)) | ||||
| for i := range actions { | for i := range actions { | ||||
| feeds[i] = fmt.Sprintf(feedTpl, base.ActionIcon(actions[i].OpType), | |||||
| feeds[i] = fmt.Sprintf(TPL_FEED, base.ActionIcon(actions[i].OpType), | |||||
| base.TimeSince(actions[i].Created), base.ActionDesc(actions[i], ctx.User.AvatarLink())) | base.TimeSince(actions[i].Created), base.ActionDesc(actions[i], ctx.User.AvatarLink())) | ||||
| } | } | ||||
| ctx.Render.JSON(200, &feeds) | ctx.Render.JSON(200, &feeds) | ||||
| } | } | ||||
| func Issues(ctx *middleware.Context) string { | |||||
| return "This is issues page" | |||||
| func Issues(ctx *middleware.Context) { | |||||
| ctx.Render.HTML(200, "user/issues", ctx.Data) | |||||
| } | } | ||||
| func Pulls(ctx *middleware.Context) string { | |||||
| return "This is pulls page" | |||||
| func Pulls(ctx *middleware.Context) { | |||||
| ctx.Render.HTML(200, "user/pulls", ctx.Data) | |||||
| } | } | ||||
| func Stars(ctx *middleware.Context) string { | |||||
| return "This is stars page" | |||||
| func Stars(ctx *middleware.Context) { | |||||
| ctx.Render.HTML(200, "user/stars", ctx.Data) | |||||
| } | } | ||||
| @@ -3,7 +3,7 @@ | |||||
| <nav class="gogs-nav"> | <nav class="gogs-nav"> | ||||
| <a id="gogs-nav-logo" class="gogs-nav-item{{if .PageIsHome}} active{{end}}" href="/"><img src="/img/favicon.png" alt="Gogs Logo" id="gogs-logo"></a> | <a id="gogs-nav-logo" class="gogs-nav-item{{if .PageIsHome}} active{{end}}" href="/"><img src="/img/favicon.png" alt="Gogs Logo" id="gogs-logo"></a> | ||||
| <a class="gogs-nav-item{{if .PageIsUserDashboard}} active{{end}}" href="/">Dashboard</a> | <a class="gogs-nav-item{{if .PageIsUserDashboard}} active{{end}}" href="/">Dashboard</a> | ||||
| <a class="gogs-nav-item" href="/help">Help</a>{{if .IsSigned}} | |||||
| <a class="gogs-nav-item{{if .PageIsHelp}} active{{end}}" href="/help">Help</a>{{if .IsSigned}} | |||||
| <a id="gogs-nav-out" class="gogs-nav-item navbar-right navbar-btn btn btn-danger" href="/user/logout/"><i class="fa fa-power-off fa-lg"></i></a> | <a id="gogs-nav-out" class="gogs-nav-item navbar-right navbar-btn btn btn-danger" href="/user/logout/"><i class="fa fa-power-off fa-lg"></i></a> | ||||
| <a id="gogs-nav-avatar" class="gogs-nav-item navbar-right" href="{{.SignedUser.HomeLink}}" data-toggle="tooltip" data-placement="bottom" title="{{.SignedUserName}}"> | <a id="gogs-nav-avatar" class="gogs-nav-item navbar-right" href="{{.SignedUser.HomeLink}}" data-toggle="tooltip" data-placement="bottom" title="{{.SignedUserName}}"> | ||||
| <img src="{{.SignedUser.AvatarLink}}?s=28" alt="user-avatar" title="username"/> | <img src="{{.SignedUser.AvatarLink}}?s=28" alt="user-avatar" title="username"/> | ||||
| @@ -0,0 +1,11 @@ | |||||
| {{template "base/head" .}} | |||||
| {{template "base/navbar" .}} | |||||
| <div id="gogs-body-nav"> | |||||
| <div class="container"> | |||||
| <h3>Help</h3> | |||||
| </div> | |||||
| </div> | |||||
| <div id="gogs-body" class="container" data-page="user"> | |||||
| {{if .HasInfo}}<div class="alert alert-info">{{.InfoMsg}}</div>{{end}} | |||||
| </div> | |||||
| {{template "base/footer" .}} | |||||
| @@ -1,6 +1,6 @@ | |||||
| {{template "base/head" .}} | {{template "base/head" .}} | ||||
| {{template "base/navbar" .}} | {{template "base/navbar" .}} | ||||
| <div id="gogs-body" class="container"> | <div id="gogs-body" class="container"> | ||||
| Website is still in the progress of building...please come back later! | |||||
| Welcome to the land of Gogs! There will be some indroduction! | |||||
| </div> | </div> | ||||
| {{template "base/footer" .}} | {{template "base/footer" .}} | ||||
| @@ -5,8 +5,9 @@ | |||||
| <div id="gogs-body" class="container"> | <div id="gogs-body" class="container"> | ||||
| <div id="gogs-commits"> | <div id="gogs-commits"> | ||||
| <ul> | <ul> | ||||
| {{range .Commits}} | |||||
| <li>{{.Committer.Name}} - {{.Id}} - {{.Message}} - {{.Committer.When}}</li> | |||||
| {{$r := List .Commits}} | |||||
| {{range $r}} | |||||
| <li>{{.Committer.Name}} - {{.Id}} - {{.Message}} - {{.Committer.When}}</li> | |||||
| {{end}} | {{end}} | ||||
| </ul> | </ul> | ||||
| </div> | </div> | ||||
| @@ -0,0 +1,9 @@ | |||||
| {{template "base/head" .}} | |||||
| {{template "base/navbar" .}} | |||||
| {{template "repo/nav" .}} | |||||
| {{template "repo/toolbar" .}} | |||||
| <div id="gogs-body" class="container"> | |||||
| <div id="gogs-source"> | |||||
| </div> | |||||
| </div> | |||||
| {{template "base/footer" .}} | |||||
| @@ -0,0 +1,9 @@ | |||||
| {{template "base/head" .}} | |||||
| {{template "base/navbar" .}} | |||||
| {{template "repo/nav" .}} | |||||
| {{template "repo/toolbar" .}} | |||||
| <div id="gogs-body" class="container"> | |||||
| <div id="gogs-source"> | |||||
| </div> | |||||
| </div> | |||||
| {{template "base/footer" .}} | |||||
| @@ -0,0 +1,17 @@ | |||||
| {{template "base/head" .}} | |||||
| {{template "base/navbar" .}} | |||||
| <div id="gogs-body-nav"> | |||||
| <div class="container"> | |||||
| <ul class="nav nav-pills pull-right"> | |||||
| <li><a href="/">Feed</a></li> | |||||
| <li class="active"><a href="/issues">Issues</a></li> | |||||
| <li><a href="/pulls">Pull Requests</a></li> | |||||
| <li><a href="/stars">Stars</a></li> | |||||
| </ul> | |||||
| <h3>Issues</h3> | |||||
| </div> | |||||
| </div> | |||||
| <div id="gogs-body" class="container" data-page="user"> | |||||
| {{if .HasInfo}}<div class="alert alert-info">{{.InfoMsg}}</div>{{end}} | |||||
| </div> | |||||
| {{template "base/footer" .}} | |||||
| @@ -1,17 +1,7 @@ | |||||
| {{template "base/head" .}} | {{template "base/head" .}} | ||||
| {{template "base/navbar" .}} | {{template "base/navbar" .}} | ||||
| <div id="gogs-body" class="container" data-page="user"> | <div id="gogs-body" class="container" data-page="user"> | ||||
| <div id="gogs-user-setting-nav" class="col-md-3"> | |||||
| <h4>Account Setting</h4> | |||||
| <ul class="list-group"> | |||||
| <li class="list-group-item"><a href="/user/setting">Account Profile</a></li> | |||||
| <li class="list-group-item"><a href="/user/setting/password">Password</a></li> | |||||
| <li class="list-group-item list-group-item-success"><a href="/user/setting/notification">Notifications</a></li> | |||||
| <li class="list-group-item"><a href="/user/setting/ssh/">SSH Keys</a></li> | |||||
| <li class="list-group-item"><a href="/user/setting/security">Security</a></li> | |||||
| <li class="list-group-item"><a href="/user/delete">Delete Account</a></li> | |||||
| </ul> | |||||
| </div> | |||||
| {{template "user/setting_nav" .}} | |||||
| <div id="gogs-user-setting-container" class="col-md-9"> | <div id="gogs-user-setting-container" class="col-md-9"> | ||||
| <h4>Notification</h4> | <h4>Notification</h4> | ||||
| </div> | </div> | ||||
| @@ -1,45 +1,35 @@ | |||||
| {{template "base/head" .}} | {{template "base/head" .}} | ||||
| {{template "base/navbar" .}} | {{template "base/navbar" .}} | ||||
| <div id="gogs-body" class="container" data-page="user"> | <div id="gogs-body" class="container" data-page="user"> | ||||
| <div id="gogs-user-setting-nav" class="col-md-3"> | |||||
| <h4>Account Setting</h4> | |||||
| <ul class="list-group"> | |||||
| <li class="list-group-item"><a href="/user/setting">Account Profile</a></li> | |||||
| <li class="list-group-item list-group-item-success"><a href="/user/setting/password">Password</a></li> | |||||
| <li class="list-group-item"><a href="/user/setting/notification">Notifications</a></li> | |||||
| <li class="list-group-item"><a href="/user/setting/ssh/">SSH Keys</a></li> | |||||
| <li class="list-group-item"><a href="/user/setting/security">Security</a></li> | |||||
| <li class="list-group-item"><a href="/user/delete">Delete Account</a></li> | |||||
| </ul> | |||||
| </div> | |||||
| {{template "user/setting_nav" .}} | |||||
| <div id="gogs-user-setting-container" class="col-md-9"> | <div id="gogs-user-setting-container" class="col-md-9"> | ||||
| <div id="gogs-setting-pwd"> | <div id="gogs-setting-pwd"> | ||||
| <h4>Password</h4> | <h4>Password</h4> | ||||
| <form class="form-horizontal" id="gogs-password-form" method="post" action="/user/setting/password">{{if .IsSuccess}} | <form class="form-horizontal" id="gogs-password-form" method="post" action="/user/setting/password">{{if .IsSuccess}} | ||||
| <p class="alert alert-success">Password is changed successfully. You can now sign in via new password.</p>{{else if .HasError}}<p class="alert alert-danger form-error">{{.ErrorMsg}}</p>{{end}} | <p class="alert alert-success">Password is changed successfully. You can now sign in via new password.</p>{{else if .HasError}}<p class="alert alert-danger form-error">{{.ErrorMsg}}</p>{{end}} | ||||
| <div class="form-group"> | <div class="form-group"> | ||||
| <label class="col-md-2 control-label">Old Password<strong class="text-danger">*</strong></label> | |||||
| <div class="col-md-8"> | |||||
| <label class="col-md-3 control-label">Old Password<strong class="text-danger">*</strong></label> | |||||
| <div class="col-md-7"> | |||||
| <input type="password" name="oldpasswd" class="form-control" placeholder="Type your current password" required="required"> | <input type="password" name="oldpasswd" class="form-control" placeholder="Type your current password" required="required"> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div class="form-group"> | <div class="form-group"> | ||||
| <label class="col-md-2 control-label">New Password<strong class="text-danger">*</strong></label> | |||||
| <div class="col-md-8"> | |||||
| <label class="col-md-3 control-label">New Password<strong class="text-danger">*</strong></label> | |||||
| <div class="col-md-7"> | |||||
| <input type="password" name="newpasswd" class="form-control" placeholder="Type your new password" required="required"> | <input type="password" name="newpasswd" class="form-control" placeholder="Type your new password" required="required"> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div class="form-group"> | <div class="form-group"> | ||||
| <label class="col-md-2 control-label">Re-Type<strong class="text-danger">*</strong></label> | |||||
| <div class="col-md-8"> | |||||
| <label class="col-md-3 control-label">Re-Type<strong class="text-danger">*</strong></label> | |||||
| <div class="col-md-7"> | |||||
| <input type="password" name="retypepasswd" class="form-control" placeholder="Re-type your new password" required="required"> | <input type="password" name="retypepasswd" class="form-control" placeholder="Re-type your new password" required="required"> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| <div class="form-group"> | <div class="form-group"> | ||||
| <div class="col-md-offset-2 col-md-8"> | |||||
| <div class="col-md-offset-3 col-md-7"> | |||||
| <button type="submit" class="btn btn-primary">Change Password</button> | <button type="submit" class="btn btn-primary">Change Password</button> | ||||
| <a href="/forget-password/">Forgot your password?</a> | <a href="/forget-password/">Forgot your password?</a> | ||||
| </div> | </div> | ||||
| @@ -1,18 +1,7 @@ | |||||
| {{template "base/head" .}} | {{template "base/head" .}} | ||||
| {{template "base/navbar" .}} | {{template "base/navbar" .}} | ||||
| <div id="gogs-body" class="container" data-page="user"> | <div id="gogs-body" class="container" data-page="user"> | ||||
| <div id="gogs-user-setting-nav" class="col-md-3"> | |||||
| <h4>Account Setting</h4> | |||||
| <ul class="list-group"> | |||||
| <li class="list-group-item"><a href="/user/setting">Account Profile</a></li> | |||||
| <li class="list-group-item"><a href="/user/setting/password">Password</a></li> | |||||
| <li class="list-group-item"><a href="/user/setting/notification">Notifications</a></li> | |||||
| <li class="list-group-item list-group-item-success"><a href="/user/setting/ssh/">SSH Keys</a></li> | |||||
| <li class="list-group-item"><a href="/user/setting/security">Security</a></li> | |||||
| <li class="list-group-item"><a href="/user/delete">Delete Account</a></li> | |||||
| </ul> | |||||
| </div> | |||||
| {{template "user/setting_nav" .}} | |||||
| <div id="gogs-user-setting-container" class="col-md-9"> | <div id="gogs-user-setting-container" class="col-md-9"> | ||||
| <div id="gogs-ssh-keys"> | <div id="gogs-ssh-keys"> | ||||
| <h4>SSH Keys</h4>{{if .AddSSHKeySuccess}} | <h4>SSH Keys</h4>{{if .AddSSHKeySuccess}} | ||||
| @@ -0,0 +1,17 @@ | |||||
| {{template "base/head" .}} | |||||
| {{template "base/navbar" .}} | |||||
| <div id="gogs-body-nav"> | |||||
| <div class="container"> | |||||
| <ul class="nav nav-pills pull-right"> | |||||
| <li><a href="/">Feed</a></li> | |||||
| <li><a href="/issues">Issues</a></li> | |||||
| <li class="active"><a href="/pulls">Pull Requests</a></li> | |||||
| <li><a href="/stars">Stars</a></li> | |||||
| </ul> | |||||
| <h3>Pull Requests</h3> | |||||
| </div> | |||||
| </div> | |||||
| <div id="gogs-body" class="container" data-page="user"> | |||||
| {{if .HasInfo}}<div class="alert alert-info">{{.InfoMsg}}</div>{{end}} | |||||
| </div> | |||||
| {{template "base/footer" .}} | |||||
| @@ -1,17 +1,7 @@ | |||||
| {{template "base/head" .}} | {{template "base/head" .}} | ||||
| {{template "base/navbar" .}} | {{template "base/navbar" .}} | ||||
| <div id="gogs-body" class="container" data-page="user"> | <div id="gogs-body" class="container" data-page="user"> | ||||
| <div id="gogs-user-setting-nav" class="col-md-3"> | |||||
| <h4>Account Setting</h4> | |||||
| <ul class="list-group"> | |||||
| <li class="list-group-item"><a href="/user/setting">Account Profile</a></li> | |||||
| <li class="list-group-item"><a href="/user/setting/password">Password</a></li> | |||||
| <li class="list-group-item"><a href="/user/setting/notification">Notifications</a></li> | |||||
| <li class="list-group-item"><a href="/user/setting/ssh/">SSH Keys</a></li> | |||||
| <li class="list-group-item list-group-item-success"><a href="/user/setting/security">Security</a></li> | |||||
| <li class="list-group-item"><a href="/user/delete">Delete Account</a></li> | |||||
| </ul> | |||||
| </div> | |||||
| {{template "user/setting_nav" .}} | |||||
| <div id="gogs-user-setting-container" class="col-md-9"> | <div id="gogs-user-setting-container" class="col-md-9"> | ||||
| <h4>Security</h4> | <h4>Security</h4> | ||||
| </div> | </div> | ||||
| @@ -1,17 +1,7 @@ | |||||
| {{template "base/head" .}} | {{template "base/head" .}} | ||||
| {{template "base/navbar" .}} | {{template "base/navbar" .}} | ||||
| <div id="gogs-body" class="container" data-page="user"> | <div id="gogs-body" class="container" data-page="user"> | ||||
| <div id="gogs-user-setting-nav" class="col-md-3"> | |||||
| <h4>Account Setting</h4> | |||||
| <ul class="list-group"> | |||||
| <li class="list-group-item list-group-item-success"><a href="/user/setting">Account Profile</a></li> | |||||
| <li class="list-group-item"><a href="/user/setting/password">Password</a></li> | |||||
| <li class="list-group-item"><a href="/user/setting/notification">Notifications</a></li> | |||||
| <li class="list-group-item"><a href="/user/setting/ssh/">SSH Keys</a></li> | |||||
| <li class="list-group-item"><a href="/user/setting/security">Security</a></li> | |||||
| <li class="list-group-item"><a href="/user/delete">Delete Account</a></li> | |||||
| </ul> | |||||
| </div> | |||||
| {{template "user/setting_nav" .}} | |||||
| <div id="gogs-user-setting-container" class="col-md-9"> | <div id="gogs-user-setting-container" class="col-md-9"> | ||||
| <div id="gogs-setting-pwd"> | <div id="gogs-setting-pwd"> | ||||
| <h4>Account Profile</h4> | <h4>Account Profile</h4> | ||||
| @@ -0,0 +1,11 @@ | |||||
| <div id="gogs-user-setting-nav" class="col-md-3"> | |||||
| <h4>Account Setting</h4> | |||||
| <ul class="list-group"> | |||||
| <li class="list-group-item{{if .IsUserPageSetting}} list-group-item-success{{end}}"><a href="/user/setting">Account Profile</a></li> | |||||
| <li class="list-group-item{{if .IsUserPageSettingPasswd}} list-group-item-success{{end}}"><a href="/user/setting/password">Password</a></li> | |||||
| <li class="list-group-item{{if .IsUserPageSettingNotify}} list-group-item-success{{end}}"><a href="/user/setting/notification">Notifications</a></li> | |||||
| <li class="list-group-item{{if .IsUserPageSettingSSH}} list-group-item-success{{end}}"><a href="/user/setting/ssh/">SSH Keys</a></li> | |||||
| <li class="list-group-item{{if .IsUserPageSettingSecurity}} list-group-item-success{{end}}"><a href="/user/setting/security">Security</a></li> | |||||
| <li class="list-group-item{{if .IsUserPageSettingDelete}} list-group-item-success{{end}}"><a href="/user/delete">Delete Account</a></li> | |||||
| </ul> | |||||
| </div> | |||||
| @@ -0,0 +1,17 @@ | |||||
| {{template "base/head" .}} | |||||
| {{template "base/navbar" .}} | |||||
| <div id="gogs-body-nav"> | |||||
| <div class="container"> | |||||
| <ul class="nav nav-pills pull-right"> | |||||
| <li><a href="/">Feed</a></li> | |||||
| <li><a href="/issues">Issues</a></li> | |||||
| <li><a href="/pulls">Pull Requests</a></li> | |||||
| <li class="active"><a href="/stars">Stars</a></li> | |||||
| </ul> | |||||
| <h3>Stars</h3> | |||||
| </div> | |||||
| </div> | |||||
| <div id="gogs-body" class="container" data-page="user"> | |||||
| {{if .HasInfo}}<div class="alert alert-info">{{.InfoMsg}}</div>{{end}} | |||||
| </div> | |||||
| {{template "base/footer" .}} | |||||
| @@ -10,8 +10,7 @@ import ( | |||||
| "github.com/codegangsta/cli" | "github.com/codegangsta/cli" | ||||
| git "github.com/gogits/git" | |||||
| "github.com/gogits/git" | |||||
| "github.com/gogits/gogs/models" | "github.com/gogits/gogs/models" | ||||
| "github.com/gogits/gogs/modules/log" | "github.com/gogits/gogs/modules/log" | ||||
| ) | ) | ||||
| @@ -25,6 +24,7 @@ gogs serv provide access auth for repositories`, | |||||
| Flags: []cli.Flag{}, | Flags: []cli.Flag{}, | ||||
| } | } | ||||
| // for command: ./gogs update | |||||
| func runUpdate(*cli.Context) { | func runUpdate(*cli.Context) { | ||||
| userName := os.Getenv("userName") | userName := os.Getenv("userName") | ||||
| userId := os.Getenv("userId") | userId := os.Getenv("userId") | ||||
| @@ -8,6 +8,7 @@ import ( | |||||
| "fmt" | "fmt" | ||||
| "html/template" | "html/template" | ||||
| "net/http" | "net/http" | ||||
| "strings" | |||||
| "github.com/codegangsta/cli" | "github.com/codegangsta/cli" | ||||
| "github.com/codegangsta/martini" | "github.com/codegangsta/martini" | ||||
| @@ -34,10 +35,33 @@ gogs web`, | |||||
| Flags: []cli.Flag{}, | Flags: []cli.Flag{}, | ||||
| } | } | ||||
| // Check run mode(Default of martini is Dev). | |||||
| func checkRunMode() { | |||||
| switch base.Cfg.MustValue("", "RUN_MODE") { | |||||
| case "prod": | |||||
| martini.Env = martini.Prod | |||||
| case "test": | |||||
| martini.Env = martini.Test | |||||
| } | |||||
| log.Info("Run Mode: %s", strings.Title(martini.Env)) | |||||
| } | |||||
| func newMartini() *martini.ClassicMartini { | |||||
| r := martini.NewRouter() | |||||
| m := martini.New() | |||||
| m.Use(middleware.Logger()) | |||||
| m.Use(martini.Recovery()) | |||||
| m.Use(martini.Static("public")) | |||||
| m.MapTo(r, (*martini.Routes)(nil)) | |||||
| m.Action(r.Handle) | |||||
| return &martini.ClassicMartini{m, r} | |||||
| } | |||||
| func runWeb(*cli.Context) { | func runWeb(*cli.Context) { | ||||
| checkRunMode() | |||||
| log.Info("%s %s", base.AppName, base.AppVer) | log.Info("%s %s", base.AppName, base.AppVer) | ||||
| m := martini.Classic() | |||||
| m := newMartini() | |||||
| // Middlewares. | // Middlewares. | ||||
| m.Use(render.Renderer(render.Options{Funcs: []template.FuncMap{base.TemplateFuncs}})) | m.Use(render.Renderer(render.Options{Funcs: []template.FuncMap{base.TemplateFuncs}})) | ||||
| @@ -48,48 +72,52 @@ func runWeb(*cli.Context) { | |||||
| m.Use(middleware.InitContext()) | m.Use(middleware.InitContext()) | ||||
| ignSignIn := middleware.SignInRequire(false) | |||||
| reqSignIn, reqSignOut := middleware.SignInRequire(true), middleware.SignOutRequire() | |||||
| // Routers. | // Routers. | ||||
| m.Get("/", middleware.SignInRequire(false), routers.Home) | |||||
| m.Get("/issues", middleware.SignInRequire(true), user.Issues) | |||||
| m.Get("/pulls", middleware.SignInRequire(true), user.Pulls) | |||||
| m.Get("/stars", middleware.SignInRequire(true), user.Stars) | |||||
| m.Any("/user/login", middleware.SignOutRequire(), binding.BindIgnErr(auth.LogInForm{}), user.SignIn) | |||||
| m.Any("/user/logout", middleware.SignInRequire(true), user.SignOut) | |||||
| m.Any("/user/sign_up", middleware.SignOutRequire(), binding.BindIgnErr(auth.RegisterForm{}), user.SignUp) | |||||
| m.Any("/user/delete", middleware.SignInRequire(true), user.Delete) | |||||
| m.Get("/", ignSignIn, routers.Home) | |||||
| m.Get("/issues", reqSignIn, user.Issues) | |||||
| m.Get("/pulls", reqSignIn, user.Pulls) | |||||
| m.Get("/stars", reqSignIn, user.Stars) | |||||
| m.Any("/user/login", reqSignOut, binding.BindIgnErr(auth.LogInForm{}), user.SignIn) | |||||
| m.Any("/user/logout", reqSignIn, user.SignOut) | |||||
| m.Any("/user/sign_up", reqSignOut, binding.BindIgnErr(auth.RegisterForm{}), user.SignUp) | |||||
| m.Any("/user/delete", reqSignIn, user.Delete) | |||||
| m.Get("/user/feeds", binding.Bind(auth.FeedsForm{}), user.Feeds) | m.Get("/user/feeds", binding.Bind(auth.FeedsForm{}), user.Feeds) | ||||
| m.Any("/user/setting", middleware.SignInRequire(true), binding.BindIgnErr(auth.UpdateProfileForm{}), user.Setting) | |||||
| m.Any("/user/setting/password", middleware.SignInRequire(true), binding.BindIgnErr(auth.UpdatePasswdForm{}), user.SettingPassword) | |||||
| m.Any("/user/setting/ssh", middleware.SignInRequire(true), binding.BindIgnErr(auth.AddSSHKeyForm{}), user.SettingSSHKeys) | |||||
| m.Any("/user/setting/notification", middleware.SignInRequire(true), user.SettingNotification) | |||||
| m.Any("/user/setting/security", middleware.SignInRequire(true), user.SettingSecurity) | |||||
| m.Any("/user/setting", reqSignIn, binding.BindIgnErr(auth.UpdateProfileForm{}), user.Setting) | |||||
| m.Any("/user/setting/password", reqSignIn, binding.BindIgnErr(auth.UpdatePasswdForm{}), user.SettingPassword) | |||||
| m.Any("/user/setting/ssh", reqSignIn, binding.BindIgnErr(auth.AddSSHKeyForm{}), user.SettingSSHKeys) | |||||
| m.Any("/user/setting/notification", reqSignIn, user.SettingNotification) | |||||
| m.Any("/user/setting/security", reqSignIn, user.SettingSecurity) | |||||
| m.Get("/user/:username", middleware.SignInRequire(false), user.Profile) | |||||
| m.Get("/user/:username", ignSignIn, user.Profile) | |||||
| m.Any("/repo/create", middleware.SignInRequire(true), binding.BindIgnErr(auth.CreateRepoForm{}), repo.Create) | |||||
| m.Any("/repo/create", reqSignIn, binding.BindIgnErr(auth.CreateRepoForm{}), repo.Create) | |||||
| m.Get("/help", routers.Help) | m.Get("/help", routers.Help) | ||||
| m.Post("/:username/:reponame/settings", middleware.SignInRequire(true), middleware.RepoAssignment(true), repo.SettingPost) | |||||
| m.Get("/:username/:reponame/settings", middleware.SignInRequire(true), middleware.RepoAssignment(true), repo.Setting) | |||||
| m.Post("/:username/:reponame/settings", reqSignIn, middleware.RepoAssignment(true), repo.SettingPost) | |||||
| m.Get("/:username/:reponame/settings", reqSignIn, middleware.RepoAssignment(true), repo.Setting) | |||||
| m.Get("/:username/:reponame/commits/:branchname", middleware.SignInRequire(false), middleware.RepoAssignment(true), repo.Commits) | |||||
| m.Get("/:username/:reponame/issues", middleware.SignInRequire(false), middleware.RepoAssignment(true), repo.Issues) | |||||
| m.Get("/:username/:reponame/pulls", middleware.SignInRequire(false), middleware.RepoAssignment(true), repo.Pulls) | |||||
| m.Get("/:username/:reponame/branches", middleware.SignInRequire(false), middleware.RepoAssignment(true), repo.Branches) | |||||
| m.Get("/:username/:reponame/commits/:branchname", ignSignIn, middleware.RepoAssignment(true), repo.Commits) | |||||
| m.Get("/:username/:reponame/issues", ignSignIn, middleware.RepoAssignment(true), repo.Issues) | |||||
| m.Get("/:username/:reponame/pulls", ignSignIn, middleware.RepoAssignment(true), repo.Pulls) | |||||
| m.Get("/:username/:reponame/branches", ignSignIn, middleware.RepoAssignment(true), repo.Branches) | |||||
| m.Get("/:username/:reponame/tree/:branchname/**", | m.Get("/:username/:reponame/tree/:branchname/**", | ||||
| middleware.SignInRequire(false), middleware.RepoAssignment(true), repo.Single) | |||||
| ignSignIn, middleware.RepoAssignment(true), repo.Single) | |||||
| m.Get("/:username/:reponame/tree/:branchname", | m.Get("/:username/:reponame/tree/:branchname", | ||||
| middleware.SignInRequire(false), middleware.RepoAssignment(true), repo.Single) | |||||
| m.Get("/:username/:reponame/commit/:commitid/**", middleware.SignInRequire(false), middleware.RepoAssignment(true), repo.Single) | |||||
| m.Get("/:username/:reponame/commit/:commitid", middleware.SignInRequire(false), middleware.RepoAssignment(true), repo.Single) | |||||
| ignSignIn, middleware.RepoAssignment(true), repo.Single) | |||||
| m.Get("/:username/:reponame/commit/:commitid/**", ignSignIn, middleware.RepoAssignment(true), repo.Single) | |||||
| m.Get("/:username/:reponame/commit/:commitid", ignSignIn, middleware.RepoAssignment(true), repo.Single) | |||||
| m.Get("/:username/:reponame", middleware.SignInRequire(false), middleware.RepoAssignment(true), repo.Single) | |||||
| m.Get("/:username/:reponame", ignSignIn, middleware.RepoAssignment(true), repo.Single) | |||||
| listenAddr := fmt.Sprintf("%s:%s", | listenAddr := fmt.Sprintf("%s:%s", | ||||
| base.Cfg.MustValue("server", "HTTP_ADDR"), | base.Cfg.MustValue("server", "HTTP_ADDR"), | ||||
| base.Cfg.MustValue("server", "HTTP_PORT", "3000")) | base.Cfg.MustValue("server", "HTTP_PORT", "3000")) | ||||
| log.Info("Listen: %s", listenAddr) | log.Info("Listen: %s", listenAddr) | ||||
| http.ListenAndServe(listenAddr, m) | |||||
| if err := http.ListenAndServe(listenAddr, m); err != nil { | |||||
| log.Critical(err.Error()) | |||||
| } | |||||
| } | } | ||||