| @@ -28,7 +28,7 @@ There are some very good products in this category such as [gitlab](http://gitla | |||||
| - Repository viewer. | - Repository viewer. | ||||
| - Gravatar support. | - Gravatar support. | ||||
| - Mail service(register). | - Mail service(register). | ||||
| - Supports MySQL and PostgreSQL. | |||||
| - Supports MySQL, PostgreSQL and SQLite3(binary release only). | |||||
| ## Installation | ## Installation | ||||
| @@ -18,7 +18,7 @@ HTTP_ADDR = | |||||
| HTTP_PORT = 3000 | HTTP_PORT = 3000 | ||||
| [database] | [database] | ||||
| ; Either "mysql" or "postgres", it's your choice | |||||
| ; Either "mysql", "postgres" or "sqlite3"(binary release only), it's your choice | |||||
| DB_TYPE = mysql | DB_TYPE = mysql | ||||
| HOST = | HOST = | ||||
| NAME = gogs | NAME = gogs | ||||
| @@ -26,6 +26,10 @@ USER = root | |||||
| PASSWD = | PASSWD = | ||||
| ; For "postgres" only, either "disable", "require" or "verify-full" | ; For "postgres" only, either "disable", "require" or "verify-full" | ||||
| SSL_MODE = disable | SSL_MODE = disable | ||||
| ; For "sqlite3" only | |||||
| PATH = data/gogs.db | |||||
| [admin] | |||||
| [security] | [security] | ||||
| ; !!CHANGE THIS TO KEEP YOUR USER DATA SAFE!! | ; !!CHANGE THIS TO KEEP YOUR USER DATA SAFE!! | ||||
| @@ -36,6 +40,8 @@ ACTIVE_CODE_LIVE_MINUTES = 180 | |||||
| RESET_PASSWD_CODE_LIVE_MINUTES = 180 | RESET_PASSWD_CODE_LIVE_MINUTES = 180 | ||||
| ; User need to confirm e-mail for registration | ; User need to confirm e-mail for registration | ||||
| REGISTER_EMAIL_CONFIRM = false | REGISTER_EMAIL_CONFIRM = false | ||||
| ; Does not allow register and admin create account only | |||||
| DISENABLE_REGISTERATION = false | |||||
| [mailer] | [mailer] | ||||
| ENABLED = false | ENABLED = false | ||||
| @@ -64,6 +64,10 @@ func CommitRepoAction(userId int64, userName string, | |||||
| watches = append(watches, Watch{UserId: userId}) | watches = append(watches, Watch{UserId: userId}) | ||||
| for i := range watches { | for i := range watches { | ||||
| if userId == watches[i].UserId && i > 0 { | |||||
| continue // Do not add twice in case author watches his/her repository. | |||||
| } | |||||
| _, err = orm.InsertOne(&Action{ | _, err = orm.InsertOne(&Action{ | ||||
| UserId: watches[i].UserId, | UserId: watches[i].UserId, | ||||
| ActUserId: userId, | ActUserId: userId, | ||||
| @@ -7,6 +7,7 @@ package models | |||||
| import ( | import ( | ||||
| "fmt" | "fmt" | ||||
| "os" | "os" | ||||
| "path" | |||||
| _ "github.com/go-sql-driver/mysql" | _ "github.com/go-sql-driver/mysql" | ||||
| _ "github.com/lib/pq" | _ "github.com/lib/pq" | ||||
| @@ -23,6 +24,7 @@ func setEngine() { | |||||
| dbName := base.Cfg.MustValue("database", "NAME") | dbName := base.Cfg.MustValue("database", "NAME") | ||||
| dbUser := base.Cfg.MustValue("database", "USER") | dbUser := base.Cfg.MustValue("database", "USER") | ||||
| dbPwd := base.Cfg.MustValue("database", "PASSWD") | dbPwd := base.Cfg.MustValue("database", "PASSWD") | ||||
| dbPath := base.Cfg.MustValue("database", "PATH", "data/gogs.db") | |||||
| sslMode := base.Cfg.MustValue("database", "SSL_MODE") | sslMode := base.Cfg.MustValue("database", "SSL_MODE") | ||||
| var err error | var err error | ||||
| @@ -33,6 +35,9 @@ func setEngine() { | |||||
| case "postgres": | case "postgres": | ||||
| orm, err = xorm.NewEngine("postgres", fmt.Sprintf("user=%s password=%s dbname=%s sslmode=%s", | orm, err = xorm.NewEngine("postgres", fmt.Sprintf("user=%s password=%s dbname=%s sslmode=%s", | ||||
| dbUser, dbPwd, dbName, sslMode)) | dbUser, dbPwd, dbName, sslMode)) | ||||
| case "sqlite3": | |||||
| os.MkdirAll(path.Dir(dbPath), os.ModePerm) | |||||
| orm, err = xorm.NewEngine("sqlite3", dbPath) | |||||
| default: | default: | ||||
| fmt.Printf("Unknown database type: %s\n", dbType) | fmt.Printf("Unknown database type: %s\n", dbType) | ||||
| os.Exit(2) | os.Exit(2) | ||||
| @@ -323,11 +323,33 @@ func initRepository(f string, user *User, repo *Repository, initReadme bool, rep | |||||
| return nil | return nil | ||||
| } | } | ||||
| // UserRepo reporesents a repository with user name. | |||||
| type UserRepo struct { | |||||
| *Repository | |||||
| UserName string | |||||
| } | |||||
| // GetRepos returns given number of repository objects with offset. | // GetRepos returns given number of repository objects with offset. | ||||
| func GetRepos(num, offset int) ([]Repository, error) { | |||||
| func GetRepos(num, offset int) ([]UserRepo, error) { | |||||
| repos := make([]Repository, 0, num) | repos := make([]Repository, 0, num) | ||||
| err := orm.Limit(num, offset).Asc("id").Find(&repos) | |||||
| return repos, err | |||||
| if err := orm.Limit(num, offset).Asc("id").Find(&repos); err != nil { | |||||
| return nil, err | |||||
| } | |||||
| urepos := make([]UserRepo, len(repos)) | |||||
| for i := range repos { | |||||
| urepos[i].Repository = &repos[i] | |||||
| u := new(User) | |||||
| has, err := orm.Id(urepos[i].Repository.OwnerId).Get(u) | |||||
| if err != nil { | |||||
| return nil, err | |||||
| } else if !has { | |||||
| return nil, ErrUserNotExist | |||||
| } | |||||
| urepos[i].UserName = u.Name | |||||
| } | |||||
| return urepos, nil | |||||
| } | } | ||||
| func RepoPath(userName, repoName string) string { | func RepoPath(userName, repoName string) string { | ||||
| @@ -39,9 +39,10 @@ var ( | |||||
| ) | ) | ||||
| var Service struct { | var Service struct { | ||||
| RegisterEmailConfirm bool | |||||
| ActiveCodeLives int | |||||
| ResetPwdCodeLives int | |||||
| RegisterEmailConfirm bool | |||||
| DisenableRegisteration bool | |||||
| ActiveCodeLives int | |||||
| ResetPwdCodeLives int | |||||
| } | } | ||||
| func exeDir() (string, error) { | func exeDir() (string, error) { | ||||
| @@ -68,6 +69,7 @@ var logLevels = map[string]string{ | |||||
| func newService() { | func newService() { | ||||
| Service.ActiveCodeLives = Cfg.MustInt("service", "ACTIVE_CODE_LIVE_MINUTES", 180) | Service.ActiveCodeLives = Cfg.MustInt("service", "ACTIVE_CODE_LIVE_MINUTES", 180) | ||||
| Service.ResetPwdCodeLives = Cfg.MustInt("service", "RESET_PASSWD_CODE_LIVE_MINUTES", 180) | Service.ResetPwdCodeLives = Cfg.MustInt("service", "RESET_PASSWD_CODE_LIVE_MINUTES", 180) | ||||
| Service.DisenableRegisteration = Cfg.MustBool("service", "DISENABLE_REGISTERATION", false) | |||||
| } | } | ||||
| func newLogService() { | func newLogService() { | ||||
| @@ -33,6 +33,10 @@ func List(l *list.List) chan interface{} { | |||||
| return c | return c | ||||
| } | } | ||||
| var mailDomains = map[string]string{ | |||||
| "gmail.com": "gmail.com", | |||||
| } | |||||
| var TemplateFuncs template.FuncMap = map[string]interface{}{ | var TemplateFuncs template.FuncMap = map[string]interface{}{ | ||||
| "AppName": func() string { | "AppName": func() string { | ||||
| return AppName | return AppName | ||||
| @@ -56,7 +60,12 @@ var TemplateFuncs template.FuncMap = map[string]interface{}{ | |||||
| "DateFormat": DateFormat, | "DateFormat": DateFormat, | ||||
| "List": List, | "List": List, | ||||
| "Mail2Domain": func(mail string) string { | "Mail2Domain": func(mail string) string { | ||||
| return "mail." + strings.Split(mail, "@")[1] | |||||
| suffix := strings.SplitN(mail, "@", 2)[1] | |||||
| domain, ok := mailDomains[suffix] | |||||
| if !ok { | |||||
| return "mail." + suffix | |||||
| } | |||||
| return domain | |||||
| }, | }, | ||||
| "SubStr": func(str string, start, length int) string { | "SubStr": func(str string, start, length int) string { | ||||
| return str[start : start+length] | return str[start : start+length] | ||||
| @@ -112,6 +112,12 @@ func SignUp(ctx *middleware.Context, form auth.RegisterForm) { | |||||
| ctx.Data["Title"] = "Sign Up" | ctx.Data["Title"] = "Sign Up" | ||||
| ctx.Data["PageIsSignUp"] = true | ctx.Data["PageIsSignUp"] = true | ||||
| if base.Service.DisenableRegisteration { | |||||
| ctx.Data["DisenableRegisteration"] = true | |||||
| ctx.HTML(200, "user/signup") | |||||
| return | |||||
| } | |||||
| if ctx.Req.Method == "GET" { | if ctx.Req.Method == "GET" { | ||||
| ctx.HTML(200, "user/signup") | ctx.HTML(200, "user/signup") | ||||
| return | return | ||||
| @@ -20,6 +20,7 @@ | |||||
| <thead> | <thead> | ||||
| <tr> | <tr> | ||||
| <th>Id</th> | <th>Id</th> | ||||
| <th>Owner</th> | |||||
| <th>Name</th> | <th>Name</th> | ||||
| <th>Private</th> | <th>Private</th> | ||||
| <th>Watches</th> | <th>Watches</th> | ||||
| @@ -31,7 +32,8 @@ | |||||
| {{range .Repos}} | {{range .Repos}} | ||||
| <tr> | <tr> | ||||
| <td>{{.Id}}</td> | <td>{{.Id}}</td> | ||||
| <td>{{.Name}}</td> | |||||
| <th>{{.UserName}}</th> | |||||
| <td><a href="/{{.UserName}}/{{.Name}}">{{.Name}}</a></td> | |||||
| <td><i class="fa fa{{if .Private}}-check{{end}}-square-o"></i></td> | <td><i class="fa fa{{if .Private}}-check{{end}}-square-o"></i></td> | ||||
| <td>{{.NumWatches}}</td> | <td>{{.NumWatches}}</td> | ||||
| <td>{{.NumForks}}</td> | <td>{{.NumForks}}</td> | ||||
| @@ -32,7 +32,7 @@ | |||||
| {{range .Users}} | {{range .Users}} | ||||
| <tr> | <tr> | ||||
| <td>{{.Id}}</td> | <td>{{.Id}}</td> | ||||
| <td>{{.Name}}</td> | |||||
| <td><a href="/user/{{.Name}}">{{.Name}}</a></td> | |||||
| <td>{{.Email}}</td> | <td>{{.Email}}</td> | ||||
| <td><i class="fa fa{{if .IsActive}}-check{{end}}-square-o"></i></td> | <td><i class="fa fa{{if .IsActive}}-check{{end}}-square-o"></i></td> | ||||
| <td><i class="fa fa{{if .IsAdmin}}-check{{end}}-square-o"></i></td> | <td><i class="fa fa{{if .IsAdmin}}-check{{end}}-square-o"></i></td> | ||||
| @@ -11,7 +11,8 @@ | |||||
| <a class="navbar-right gogs-nav-item{{if .PageIsNewRepo}} active{{end}}" href="/repo/create" data-toggle="tooltip" data-placement="bottom" title="New Repository"><i class="fa fa-plus fa-lg"></i></a> | <a class="navbar-right gogs-nav-item{{if .PageIsNewRepo}} active{{end}}" href="/repo/create" data-toggle="tooltip" data-placement="bottom" title="New Repository"><i class="fa fa-plus fa-lg"></i></a> | ||||
| <a class="navbar-right gogs-nav-item{{if .PageIsUserSetting}} active{{end}}" href="/user/setting" data-toggle="tooltip" data-placement="bottom" title="Setting"><i class="fa fa-cogs fa-lg"></i></a> | <a class="navbar-right gogs-nav-item{{if .PageIsUserSetting}} active{{end}}" href="/user/setting" data-toggle="tooltip" data-placement="bottom" title="Setting"><i class="fa fa-cogs fa-lg"></i></a> | ||||
| {{if .IsAdmin}}<a class="navbar-right gogs-nav-item{{if .PageIsAdmin}} active{{end}}" href="/admin" data-toggle="tooltip" data-placement="bottom" title="Admin"><i class="fa fa-gear fa-lg"></i></a>{{end}} | {{if .IsAdmin}}<a class="navbar-right gogs-nav-item{{if .PageIsAdmin}} active{{end}}" href="/admin" data-toggle="tooltip" data-placement="bottom" title="Admin"><i class="fa fa-gear fa-lg"></i></a>{{end}} | ||||
| {{else}}<a id="gogs-nav-signin" class="gogs-nav-item navbar-right navbar-btn btn btn-danger" href="/user/login/">Sign in</a>{{end}} | |||||
| {{else}}<a id="gogs-nav-signin" class="gogs-nav-item navbar-right navbar-btn btn btn-success" href="/user/login/">Sign In</a> | |||||
| <a id="gogs-nav-signup" class="gogs-nav-item navbar-right navbar-btn btn btn-info" href="/user/sign_up/">Sign Up</a>{{end}} | |||||
| </nav> | </nav> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| @@ -15,7 +15,7 @@ | |||||
| <b class="caret"></b></a> | <b class="caret"></b></a> | ||||
| <ul class="dropdown-menu"> | <ul class="dropdown-menu"> | ||||
| {{range .Branches}} | {{range .Branches}} | ||||
| <li><a {{if eq . $.Branchname}}class="current" {{end}}href="{{$.BranchLink}}">{{.}}</a></li> | |||||
| <li><a {{if eq . $.Branchname}}class="current" {{end}}href="/{{$.Username}}/{{$.Reponame}}/src/{{.}}">{{.}}</a></li> | |||||
| {{end}} | {{end}} | ||||
| </ul> | </ul> | ||||
| </div> | </div> | ||||
| @@ -32,7 +32,7 @@ | |||||
| </div> | </div> | ||||
| <div class="form-group text-center" id="gogs-social-login"> | <div class="form-group text-center" id="gogs-social-login"> | ||||
| <a class="btn btn-default btn-lg">Social Login</a> | |||||
| <a class="btn btn-danger btn-lg">Register new account</a> | |||||
| </div> | </div> | ||||
| </form> | </form> | ||||
| </div> | </div> | ||||
| @@ -2,6 +2,9 @@ | |||||
| {{template "base/navbar" .}} | {{template "base/navbar" .}} | ||||
| <div class="container" id="gogs-body" data-page="user-signup"> | <div class="container" id="gogs-body" data-page="user-signup"> | ||||
| <form action="/user/sign_up" method="post" class="form-horizontal gogs-card" id="gogs-login-card"> | <form action="/user/sign_up" method="post" class="form-horizontal gogs-card" id="gogs-login-card"> | ||||
| {{if .DisenableRegisteration}} | |||||
| Sorry, registeration has been disenabled, you can only get account from administrator. | |||||
| {{else}} | |||||
| <h3>Sign Up</h3> | <h3>Sign Up</h3> | ||||
| <div class="alert alert-danger form-error{{if .HasError}}{{else}} hidden{{end}}">{{.ErrorMsg}}</div> | <div class="alert alert-danger form-error{{if .HasError}}{{else}} hidden{{end}}">{{.ErrorMsg}}</div> | ||||
| <div class="form-group {{if .Err_UserName}}has-error has-feedback{{end}}"> | <div class="form-group {{if .Err_UserName}}has-error has-feedback{{end}}"> | ||||
| @@ -43,6 +46,7 @@ | |||||
| <a href="/user/login">Already have an account? Sign in now!</a> | <a href="/user/login">Already have an account? Sign in now!</a> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| {{end}} | |||||
| </form> | </form> | ||||
| </div> | </div> | ||||
| {{template "base/footer" .}} | {{template "base/footer" .}} | ||||