| @@ -4,4 +4,5 @@ gogs | |||
| .DS_Store | |||
| *.db | |||
| *.log | |||
| custom/ | |||
| custom/ | |||
| .vendor/ | |||
| @@ -3,6 +3,8 @@ Gogs - Go Git Service [ is a GitHub-like clone in the Go Programming Language, it currently supports Linux and Max OS X, but Windows has **NOT** supported yet due to installation problem with [libgit2](http://libgit2.github.com/) in Windows. | |||
| ##### Current version: 0.0.7 Alpha | |||
| ## Purpose | |||
| There are some very good products in this category such as [gitlab](http://gitlab.com), but the environment setup steps often make us crazy. So our goal of Gogs is to build a GitHub-like clone with very easy setup steps, which take advantages of the Go Programming Language. | |||
| @@ -15,7 +17,8 @@ Please see [Wiki](https://github.com/gogits/gogs/wiki) for project design, devel | |||
| - SSH protocal support. | |||
| - Register/delete account. | |||
| - Create public repository. | |||
| - Create/delete public repository. | |||
| - User/repository home page. | |||
| - Git repository manipulation. | |||
| ## Installation | |||
| @@ -3,8 +3,8 @@ RUN_USER = lunny | |||
| [repository] | |||
| ROOT = /Users/lunny/git/gogs-repositories | |||
| LANG_IGNS=Google Go | |||
| LICENSES=Apache v2 License | |||
| LANG_IGNS=Google Go|C | |||
| LICENSES=Apache v2 License|BSD (3-Clause) License | |||
| [server] | |||
| HTTP_ADDR = | |||
| @@ -0,0 +1,18 @@ | |||
| # Object files | |||
| *.o | |||
| *.ko | |||
| # Libraries | |||
| *.lib | |||
| *.a | |||
| # Shared objects (inc. Windows DLLs) | |||
| *.dll | |||
| *.so | |||
| *.so.* | |||
| *.dylib | |||
| # Executables | |||
| *.exe | |||
| *.out | |||
| *.app | |||
| @@ -0,0 +1,27 @@ | |||
| Copyright (c) 2014 | |||
| All rights reserved. | |||
| Redistribution and use in source and binary forms, with or without | |||
| modification, are permitted provided that the following conditions are met: | |||
| * Redistributions of source code must retain the above copyright notice, this | |||
| list of conditions and the following disclaimer. | |||
| * Redistributions in binary form must reproduce the above copyright notice, | |||
| this list of conditions and the following disclaimer in the documentation | |||
| and/or other materials provided with the distribution. | |||
| * Neither the name of the {organization} nor the names of its | |||
| contributors may be used to endorse or promote products derived from | |||
| this software without specific prior written permission. | |||
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |||
| AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |||
| IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
| DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE | |||
| FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |||
| DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |||
| SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | |||
| CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |||
| OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |||
| OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| @@ -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.0.6.0313" | |||
| const APP_VER = "0.0.7.0314" | |||
| func init() { | |||
| runtime.GOMAXPROCS(runtime.NumCPU()) | |||
| @@ -9,6 +9,7 @@ import ( | |||
| "fmt" | |||
| "io/ioutil" | |||
| "os" | |||
| "path" | |||
| "path/filepath" | |||
| "strings" | |||
| "time" | |||
| @@ -270,6 +271,7 @@ type RepoFile struct { | |||
| Id *git.Oid | |||
| Type int | |||
| Name string | |||
| Path string | |||
| Message string | |||
| Created time.Time | |||
| } | |||
| @@ -282,7 +284,7 @@ func (f *RepoFile) IsDir() bool { | |||
| return f.Type == git.FilemodeTree | |||
| } | |||
| func GetReposFiles(userName, reposName, treeName, rpath string) ([]*RepoFile, error) { | |||
| func GetReposFiles(userName, reposName, branchName, rpath string) ([]*RepoFile, error) { | |||
| f := RepoPath(userName, reposName) | |||
| repo, err := git.OpenRepository(f) | |||
| if err != nil { | |||
| @@ -299,8 +301,28 @@ func GetReposFiles(userName, reposName, treeName, rpath string) ([]*RepoFile, er | |||
| if err != nil { | |||
| return nil, err | |||
| } | |||
| var i uint64 = 0 | |||
| for ; i < tree.EntryCount(); i++ { | |||
| //var i uint64 = 0 | |||
| if rpath != "" { | |||
| rpath = rpath + "/" | |||
| } | |||
| fmt.Println("...", rpath, "...") | |||
| tree.Walk(func(dirname string, entry *git.TreeEntry) int { | |||
| if dirname == rpath { | |||
| fmt.Println("====", dirname, "==", entry.Name) | |||
| repofiles = append(repofiles, &RepoFile{ | |||
| entry.Id, | |||
| entry.Filemode, | |||
| entry.Name, | |||
| path.Join(dirname, entry.Name), | |||
| lastCommit.Message(), | |||
| lastCommit.Committer().When, | |||
| }) | |||
| } | |||
| return 0 | |||
| }) | |||
| /*for ; i < tree.EntryCount(); i++ { | |||
| entry := tree.EntryByIndex(i) | |||
| repofiles = append(repofiles, &RepoFile{ | |||
| @@ -310,7 +332,7 @@ func GetReposFiles(userName, reposName, treeName, rpath string) ([]*RepoFile, er | |||
| lastCommit.Message(), | |||
| lastCommit.Committer().When, | |||
| }) | |||
| } | |||
| }*/ | |||
| return repofiles, nil | |||
| } | |||
| @@ -354,6 +376,10 @@ func DeleteRepository(userId, repoId int64, userName string) (err error) { | |||
| session.Rollback() | |||
| return err | |||
| } | |||
| if _, err := session.Delete(&Access{UserName: userName, RepoName: repo.Name}); err != nil { | |||
| session.Rollback() | |||
| return err | |||
| } | |||
| if _, err = session.Exec("update user set num_repos = num_repos - 1 where id = ?", userId); err != nil { | |||
| session.Rollback() | |||
| return err | |||
| @@ -48,7 +48,10 @@ type User struct { | |||
| NumFollowings int | |||
| NumStars int | |||
| NumRepos int | |||
| Avatar string `xorm:"varchar(2048) not null"` | |||
| Avatar string `xorm:"varchar(2048) not null"` | |||
| AvatarEmail string `xorm:"not null"` | |||
| Location string | |||
| Website string | |||
| Created time.Time `xorm:"created"` | |||
| Updated time.Time `xorm:"updated"` | |||
| } | |||
| @@ -104,6 +107,7 @@ func RegisterUser(user *User) (err error) { | |||
| user.LowerName = strings.ToLower(user.Name) | |||
| user.Avatar = base.EncodeMd5(user.Email) | |||
| user.AvatarEmail = user.Email | |||
| if err = user.EncodePasswd(); err != nil { | |||
| return err | |||
| } | |||
| @@ -23,7 +23,7 @@ type Form interface { | |||
| } | |||
| type RegisterForm struct { | |||
| UserName string `form:"username" binding:"Required;AlphaDash;MinSize(5);MaxSize(30)"` | |||
| UserName string `form:"username" binding:"Required;AlphaDash;MaxSize(30)"` | |||
| Email string `form:"email" binding:"Required;Email;MaxSize(50)"` | |||
| Password string `form:"passwd" binding:"Required;MinSize(6);MaxSize(30)"` | |||
| RetypePasswd string `form:"retypepasswd"` | |||
| @@ -59,7 +59,7 @@ func (f *RegisterForm) Validate(errors *binding.Errors, req *http.Request, conte | |||
| } | |||
| type LogInForm struct { | |||
| UserName string `form:"username" binding:"Required;AlphaDash;MinSize(5);MaxSize(30)"` | |||
| UserName string `form:"username" binding:"Required;AlphaDash;MaxSize(30)"` | |||
| Password string `form:"passwd" binding:"Required;MinSize(6);MaxSize(30)"` | |||
| } | |||
| @@ -90,11 +90,6 @@ func (f *LogInForm) Validate(errors *binding.Errors, req *http.Request, context | |||
| validate(errors, data, f) | |||
| } | |||
| type FeedsForm struct { | |||
| UserId int64 `form:"userid" binding:"Required"` | |||
| Offset int64 `form:"offset"` | |||
| } | |||
| func getMinMaxSize(field reflect.StructField) string { | |||
| for _, rule := range strings.Split(field.Tag.Get("binding"), ";") { | |||
| if strings.HasPrefix(rule, "MinSize(") || strings.HasPrefix(rule, "MaxSize(") { | |||
| @@ -5,10 +5,15 @@ | |||
| package auth | |||
| import ( | |||
| "net/http" | |||
| "reflect" | |||
| "github.com/codegangsta/martini" | |||
| "github.com/martini-contrib/render" | |||
| "github.com/martini-contrib/sessions" | |||
| "github.com/gogits/binding" | |||
| "github.com/gogits/gogs/models" | |||
| "github.com/gogits/gogs/modules/base" | |||
| "github.com/gogits/gogs/modules/log" | |||
| @@ -83,3 +88,76 @@ func SignOutRequire() martini.Handler { | |||
| } | |||
| } | |||
| } | |||
| type FeedsForm struct { | |||
| UserId int64 `form:"userid" binding:"Required"` | |||
| Offset int64 `form:"offset"` | |||
| } | |||
| type UpdateProfileForm struct { | |||
| Email string `form:"email" binding:"Required;Email;MaxSize(50)"` | |||
| Website string `form:"website" binding:"MaxSize(50)"` | |||
| Location string `form:"location" binding:"MaxSize(50)"` | |||
| Avatar string `form:"avatar" binding:"Required;Email;MaxSize(50)"` | |||
| } | |||
| func (f *UpdateProfileForm) Name(field string) string { | |||
| names := map[string]string{ | |||
| "Email": "Email address", | |||
| "Website": "Website", | |||
| "Location": "Location", | |||
| "Avatar": "Gravatar Email", | |||
| } | |||
| return names[field] | |||
| } | |||
| func (f *UpdateProfileForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) { | |||
| if req.Method == "GET" || errors.Count() == 0 { | |||
| return | |||
| } | |||
| data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) | |||
| data["HasError"] = true | |||
| if len(errors.Overall) > 0 { | |||
| for _, err := range errors.Overall { | |||
| log.Error("UpdateProfileForm.Validate: %v", err) | |||
| } | |||
| return | |||
| } | |||
| validate(errors, data, f) | |||
| } | |||
| type UpdatePasswdForm struct { | |||
| OldPasswd string `form:"oldpasswd" binding:"Required;MinSize(6);MaxSize(30)"` | |||
| NewPasswd string `form:"newpasswd" binding:"Required;MinSize(6);MaxSize(30)"` | |||
| RetypePasswd string `form:"retypepasswd"` | |||
| } | |||
| func (f *UpdatePasswdForm) Name(field string) string { | |||
| names := map[string]string{ | |||
| "OldPasswd": "Old password", | |||
| "NewPasswd": "New password", | |||
| "RetypePasswd": "Re-type password", | |||
| } | |||
| return names[field] | |||
| } | |||
| func (f *UpdatePasswdForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) { | |||
| if req.Method == "GET" || errors.Count() == 0 { | |||
| return | |||
| } | |||
| data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) | |||
| data["HasError"] = true | |||
| if len(errors.Overall) > 0 { | |||
| for _, err := range errors.Overall { | |||
| log.Error("UpdatePasswdForm.Validate: %v", err) | |||
| } | |||
| return | |||
| } | |||
| validate(errors, data, f) | |||
| } | |||
| @@ -7,6 +7,8 @@ package base | |||
| import ( | |||
| "crypto/md5" | |||
| "encoding/hex" | |||
| "fmt" | |||
| "time" | |||
| ) | |||
| // Encode string to md5 hex value | |||
| @@ -15,3 +17,64 @@ func EncodeMd5(str string) string { | |||
| m.Write([]byte(str)) | |||
| return hex.EncodeToString(m.Sum(nil)) | |||
| } | |||
| // Seconds-based time units | |||
| const ( | |||
| Minute = 60 | |||
| Hour = 60 * Minute | |||
| Day = 24 * Hour | |||
| Week = 7 * Day | |||
| Month = 30 * Day | |||
| Year = 12 * Month | |||
| ) | |||
| // TimeSince calculates the time interval and generate user-friendly string. | |||
| func TimeSince(then time.Time) string { | |||
| now := time.Now() | |||
| lbl := "ago" | |||
| diff := now.Unix() - then.Unix() | |||
| if then.After(now) { | |||
| lbl = "from now" | |||
| diff = then.Unix() - now.Unix() | |||
| } | |||
| switch { | |||
| case diff <= 0: | |||
| return "now" | |||
| case diff <= 2: | |||
| return fmt.Sprintf("1 second %s", lbl) | |||
| case diff < 1*Minute: | |||
| return fmt.Sprintf("%d seconds %s", diff, lbl) | |||
| case diff < 2*Minute: | |||
| return fmt.Sprintf("1 minute %s", lbl) | |||
| case diff < 1*Hour: | |||
| return fmt.Sprintf("%d minutes %s", diff/Minute, lbl) | |||
| case diff < 2*Hour: | |||
| return fmt.Sprintf("1 hour %s", lbl) | |||
| case diff < 1*Day: | |||
| return fmt.Sprintf("%d hours %s", diff/Hour, lbl) | |||
| case diff < 2*Day: | |||
| return fmt.Sprintf("1 day %s", lbl) | |||
| case diff < 1*Week: | |||
| return fmt.Sprintf("%d days %s", diff/Day, lbl) | |||
| case diff < 2*Week: | |||
| return fmt.Sprintf("1 week %s", lbl) | |||
| case diff < 1*Month: | |||
| return fmt.Sprintf("%d weeks %s", diff/Week, lbl) | |||
| case diff < 2*Month: | |||
| return fmt.Sprintf("1 month %s", lbl) | |||
| case diff < 1*Year: | |||
| return fmt.Sprintf("%d months %s", diff/Month, lbl) | |||
| case diff < 18*Month: | |||
| return fmt.Sprintf("1 year %s", lbl) | |||
| } | |||
| return then.String() | |||
| } | |||
| @@ -6,7 +6,13 @@ | |||
| package log | |||
| import ( | |||
| "fmt" | |||
| "github.com/martini-contrib/render" | |||
| "github.com/gogits/logs" | |||
| "github.com/gogits/gogs/modules/base" | |||
| ) | |||
| var logger *logs.BeeLogger | |||
| @@ -35,3 +41,9 @@ func Warn(format string, v ...interface{}) { | |||
| func Critical(format string, v ...interface{}) { | |||
| logger.Critical(format, v...) | |||
| } | |||
| func Handle(status int, title string, data base.TmplData, r render.Render, err error) { | |||
| data["ErrorMsg"] = err | |||
| Error("%s: %v", title, err) | |||
| r.HTML(status, fmt.Sprintf("status/%d", status), data) | |||
| } | |||
| @@ -137,6 +137,11 @@ body { | |||
| margin-top: 50px; | |||
| } | |||
| #gogs-body .btn-default { | |||
| background-color: #FFF; | |||
| background-image: linear-gradient(to bottom, #FFF 0, #FAFAFA 100%); | |||
| } | |||
| #gogs-body-nav { | |||
| margin-top: 52px; | |||
| margin-bottom: -50px; | |||
| @@ -269,7 +274,8 @@ body { | |||
| /* gogits user setting */ | |||
| #gogs-user-setting-nav > h4, #gogs-user-setting-container > h4, #gogs-ssh-keys > h4, #gogs-user-delete > h4 ,#gogs-repo-setting-container .tab-pane > h4{ | |||
| #gogs-user-setting-nav > h4, #gogs-user-setting-container > h4, #gogs-user-setting-container > div > h4, | |||
| #gogs-ssh-keys > h4, #gogs-user-delete > h4, #gogs-repo-setting-container .tab-pane > h4 { | |||
| padding-bottom: 18px; | |||
| margin-bottom: 18px; | |||
| border-bottom: 1px solid #CCC; | |||
| @@ -380,6 +386,7 @@ body { | |||
| } | |||
| #gogs-feed-right .repo-panel .list-group-item:hover { | |||
| background-color: #eafffd; | |||
| background-color: rgba(65, 131, 196, 0.1); | |||
| } | |||
| @@ -391,6 +398,12 @@ body { | |||
| /* gogits repo single page */ | |||
| #gogs-body-nav.gogs-repo-nav { | |||
| padding-top: 16px; | |||
| padding-bottom: 30px; | |||
| height: auto; | |||
| } | |||
| .gogs-repo-nav h3 .fa { | |||
| color: #BBB; | |||
| } | |||
| @@ -429,32 +442,82 @@ body { | |||
| margin-bottom: 4px; | |||
| } | |||
| #gogs-repo-toolbar{ | |||
| #gogs-repo-toolbar { | |||
| margin-top: 51px; | |||
| margin-bottom: -50px; | |||
| border-bottom: 1px solid #BBB; | |||
| background-color: #FFF; | |||
| height: 40px; | |||
| font-size: 14px; | |||
| } | |||
| #gogs-repo-toolbar .navbar-default{ | |||
| #gogs-repo-toolbar .navbar-default { | |||
| border: none; | |||
| height: 39px; | |||
| } | |||
| #gogs-repo-toolbar .nav > li > a{ | |||
| #gogs-repo-toolbar .nav > li > a { | |||
| height: 39px; | |||
| } | |||
| #gogs-repo-toolbar .navbar-toolbar.navbar-default .navbar-nav>.active>a:after{ | |||
| #gogs-repo-toolbar .navbar-toolbar.navbar-default .navbar-nav > .active > a:after { | |||
| border-bottom-color: #999; | |||
| } | |||
| #gogs-repo-toolbar .navbar.nav-toolbar{ | |||
| #gogs-repo-toolbar .navbar.nav-toolbar { | |||
| margin-bottom: 0; | |||
| } | |||
| #gogs-repo-toolbar .navbar-collapse{ | |||
| #gogs-repo-toolbar .navbar-collapse { | |||
| padding: 0; | |||
| } | |||
| /* #gogs-source */ | |||
| #gogs-source-toolbar:after { | |||
| clear: both; | |||
| } | |||
| #gogs-source-toolbar .branch-switch { | |||
| display: inline-block; | |||
| } | |||
| #gogs-source-toolbar .breadcrumb { | |||
| margin: 0 .5em; | |||
| font-size: 16px; | |||
| vertical-align: middle; | |||
| display: inline-block; | |||
| background-color: transparent; | |||
| } | |||
| #gogs-source-table { | |||
| margin-top: 1.5em; | |||
| font-size: 14px; | |||
| } | |||
| #gogs-source-table .fa{ | |||
| font-size: 15px; | |||
| width: 16px; | |||
| text-align: center; | |||
| color: #666; | |||
| } | |||
| #gogs-source-table .name{ | |||
| width: 160px; | |||
| } | |||
| #gogs-source-table .size{ | |||
| width: 80px; | |||
| } | |||
| #gogs-source-table .date{ | |||
| width: 120px; | |||
| } | |||
| #gogs-source-table .is-dir .name { | |||
| font-weight: bold; | |||
| } | |||
| #gogs-source-table.table-hover > tbody > tr:hover > td { | |||
| background-color: #FEFEFE; | |||
| } | |||
| @@ -78,7 +78,6 @@ function initRegister() { | |||
| rules: { | |||
| "username": { | |||
| required: true, | |||
| minlength: 5, | |||
| maxlength: 30 | |||
| }, | |||
| "email": { | |||
| @@ -5,9 +5,10 @@ | |||
| package repo | |||
| import ( | |||
| "net/http" | |||
| "github.com/martini-contrib/render" | |||
| "github.com/martini-contrib/sessions" | |||
| "net/http" | |||
| "github.com/gogits/gogs/models" | |||
| "github.com/gogits/gogs/modules/auth" | |||
| @@ -46,7 +47,7 @@ func Create(form auth.CreateRepoForm, req *http.Request, r render.Render, data b | |||
| if err == nil { | |||
| if _, err = models.CreateRepository(user, | |||
| form.RepoName, form.Description, form.Language, form.License, | |||
| form.Visibility == "private", form.InitReadme == "true"); err == nil { | |||
| form.Visibility == "private", form.InitReadme == "on"); err == nil { | |||
| if err == nil { | |||
| data["RepoName"] = user.Name + "/" + form.RepoName | |||
| r.HTML(200, "repo/created", data) | |||
| @@ -63,9 +64,7 @@ func Create(form auth.CreateRepoForm, req *http.Request, r render.Render, data b | |||
| return | |||
| } | |||
| data["ErrorMsg"] = err | |||
| log.Error("repo.Create: %v", err) | |||
| r.HTML(200, "base/error", data) | |||
| log.Handle(200, "repo.Create", data, r, err) | |||
| } | |||
| func Delete(form auth.DeleteRepoForm, req *http.Request, r render.Render, data base.TmplData, session sessions.Session) { | |||
| @@ -77,13 +76,11 @@ func Delete(form auth.DeleteRepoForm, req *http.Request, r render.Render, data b | |||
| } | |||
| if err := models.DeleteRepository(form.UserId, form.RepoId, form.UserName); err != nil { | |||
| data["ErrorMsg"] = err | |||
| log.Error("repo.Delete: %v", err) | |||
| r.HTML(200, "base/error", data) | |||
| log.Handle(200, "repo.Delete", data, r, err) | |||
| return | |||
| } | |||
| r.Redirect("/", 200) | |||
| r.Redirect("/", 302) | |||
| } | |||
| func List(req *http.Request, r render.Render, data base.TmplData, session sessions.Session) { | |||
| @@ -96,9 +93,7 @@ func List(req *http.Request, r render.Render, data base.TmplData, session sessio | |||
| data["Title"] = "Repositories" | |||
| repos, err := models.GetRepositories(u) | |||
| if err != nil { | |||
| data["ErrorMsg"] = err | |||
| log.Error("repo.List: %v", err) | |||
| r.HTML(200, "base/error", data) | |||
| log.Handle(200, "repo.List", data, r, err) | |||
| return | |||
| } | |||
| @@ -1,6 +1,7 @@ | |||
| package repo | |||
| import ( | |||
| "strings" | |||
| "github.com/codegangsta/martini" | |||
| "github.com/martini-contrib/render" | |||
| @@ -13,15 +14,27 @@ func Single(params martini.Params, r render.Render, data base.TmplData) { | |||
| if !data["IsRepositoryValid"].(bool) { | |||
| return | |||
| } | |||
| files, err := models.GetReposFiles(params["username"], params["reponame"], "HEAD", "/") | |||
| if params["branchname"] == "" { | |||
| params["branchname"] = "master" | |||
| } | |||
| treename := params["_1"] | |||
| files, err := models.GetReposFiles(params["username"], params["reponame"], | |||
| params["branchname"], treename) | |||
| if err != nil { | |||
| data["ErrorMsg"] = err | |||
| log.Error("repo.List: %v", err) | |||
| r.HTML(200, "base/error", data) | |||
| log.Handle(200, "repo.Single", data, r, err) | |||
| return | |||
| } | |||
| data["Username"] = params["username"] | |||
| data["Reponame"] = params["reponame"] | |||
| data["Branchname"] = params["branchname"] | |||
| treenames := strings.Split(treename, "/") | |||
| Paths := make([]string, 0) | |||
| for i, _ := range treenames { | |||
| Paths = append(Paths, strings.Join(treenames[0:i+1], "/")) | |||
| } | |||
| data["Paths"] = Paths | |||
| data["Treenames"] = treenames | |||
| data["IsRepoToolbarSource"] = true | |||
| data["Files"] = files | |||
| @@ -17,12 +17,72 @@ import ( | |||
| "github.com/gogits/gogs/modules/log" | |||
| ) | |||
| func Setting(r render.Render, data base.TmplData, session sessions.Session) { | |||
| func Setting(form auth.UpdateProfileForm, r render.Render, data base.TmplData, req *http.Request, session sessions.Session) { | |||
| data["Title"] = "Setting" | |||
| data["PageIsUserSetting"] = true | |||
| user := auth.SignedInUser(session) | |||
| data["Owner"] = user | |||
| if req.Method == "GET" { | |||
| r.HTML(200, "user/setting", data) | |||
| return | |||
| } | |||
| if hasErr, ok := data["HasError"]; ok && hasErr.(bool) { | |||
| r.HTML(200, "user/setting", data) | |||
| return | |||
| } | |||
| user.Email = form.Email | |||
| user.Website = form.Website | |||
| user.Location = form.Location | |||
| user.Avatar = base.EncodeMd5(form.Avatar) | |||
| user.AvatarEmail = form.Avatar | |||
| if err := models.UpdateUser(user); err != nil { | |||
| log.Handle(200, "setting.Setting", data, r, err) | |||
| return | |||
| } | |||
| data["IsSuccess"] = true | |||
| r.HTML(200, "user/setting", data) | |||
| } | |||
| func SettingPassword(form auth.UpdatePasswdForm, r render.Render, data base.TmplData, session sessions.Session, req *http.Request) { | |||
| data["Title"] = "Password" | |||
| data["PageIsUserSetting"] = true | |||
| if req.Method == "GET" { | |||
| r.HTML(200, "user/password", data) | |||
| return | |||
| } | |||
| user := auth.SignedInUser(session) | |||
| newUser := &models.User{Passwd: form.NewPasswd} | |||
| if err := newUser.EncodePasswd(); err != nil { | |||
| log.Handle(200, "setting.SettingPassword", data, r, err) | |||
| return | |||
| } | |||
| if user.Passwd != newUser.Passwd { | |||
| data["HasError"] = true | |||
| data["ErrorMsg"] = "Old password is not correct" | |||
| } else if form.NewPasswd != form.RetypePasswd { | |||
| data["HasError"] = true | |||
| data["ErrorMsg"] = "New password and re-type password are not same" | |||
| } else { | |||
| user.Passwd = newUser.Passwd | |||
| if err := models.UpdateUser(user); err != nil { | |||
| log.Handle(200, "setting.SettingPassword", data, r, err) | |||
| return | |||
| } | |||
| data["IsSuccess"] = true | |||
| } | |||
| data["Owner"] = user | |||
| r.HTML(200, "user/password", data) | |||
| } | |||
| func SettingSSHKeys(form auth.AddSSHKeyForm, r render.Render, data base.TmplData, req *http.Request, session sessions.Session) { | |||
| data["Title"] = "SSH Keys" | |||
| @@ -94,3 +154,17 @@ func SettingSSHKeys(form auth.AddSSHKeyForm, r render.Render, data base.TmplData | |||
| data["Keys"] = keys | |||
| r.HTML(200, "user/publickey", data) | |||
| } | |||
| func SettingNotification(r render.Render, data base.TmplData) { | |||
| // todo user setting notification | |||
| data["Title"] = "Notification" | |||
| data["PageIsUserSetting"] = true | |||
| r.HTML(200, "user/notification", data) | |||
| } | |||
| func SettingSecurity(r render.Render, data base.TmplData) { | |||
| // todo user setting security | |||
| data["Title"] = "Security" | |||
| data["PageIsUserSetting"] = true | |||
| r.HTML(200, "user/security", data) | |||
| } | |||
| @@ -22,9 +22,7 @@ func Dashboard(r render.Render, data base.TmplData, session sessions.Session) { | |||
| data["PageIsUserDashboard"] = true | |||
| repos, err := models.GetRepositories(&models.User{Id: auth.SignedInId(session)}) | |||
| if err != nil { | |||
| data["ErrorMsg"] = err | |||
| log.Error("dashboard: %v", err) | |||
| r.HTML(200, "base/error", data) | |||
| log.Handle(200, "user.Dashboard", data, r, err) | |||
| return | |||
| } | |||
| data["MyRepos"] = repos | |||
| @@ -37,14 +35,11 @@ func Profile(params martini.Params, r render.Render, data base.TmplData, session | |||
| // TODO: Need to check view self or others. | |||
| user, err := models.GetUserByName(params["username"]) | |||
| if err != nil { | |||
| data["ErrorMsg"] = err | |||
| log.Error("user.Profile: %v", err) | |||
| r.HTML(200, "base/error", data) | |||
| log.Handle(200, "user.Profile", data, r, err) | |||
| return | |||
| } | |||
| data["Avatar"] = user.Avatar | |||
| data["Username"] = user.Name | |||
| data["Owner"] = user | |||
| r.HTML(200, "user/profile", data) | |||
| } | |||
| @@ -71,9 +66,7 @@ func SignIn(form auth.LogInForm, data base.TmplData, req *http.Request, r render | |||
| return | |||
| } | |||
| data["ErrorMsg"] = err | |||
| log.Error("user.SignIn: %v", err) | |||
| r.HTML(200, "base/error", data) | |||
| log.Handle(200, "user.SignIn", data, r, err) | |||
| return | |||
| } | |||
| @@ -130,9 +123,7 @@ func SignUp(form auth.RegisterForm, data base.TmplData, req *http.Request, r ren | |||
| data["ErrorMsg"] = "E-mail address has been already used" | |||
| r.HTML(200, "user/signup", data) | |||
| default: | |||
| data["ErrorMsg"] = err | |||
| log.Error("user.SignUp: %v", data) | |||
| r.HTML(200, "base/error", nil) | |||
| log.Handle(200, "user.SignUp", data, r, err) | |||
| } | |||
| return | |||
| } | |||
| @@ -156,9 +147,7 @@ func Delete(data base.TmplData, req *http.Request, session sessions.Session, r r | |||
| case models.ErrUserOwnRepos.Error(): | |||
| data["ErrorMsg"] = "Your account still have ownership of repository, you have to delete or transfer them first." | |||
| default: | |||
| data["ErrorMsg"] = err | |||
| log.Error("user.Delete: %v", data) | |||
| r.HTML(200, "base/error", nil) | |||
| log.Handle(200, "user.Delete", data, r, err) | |||
| return | |||
| } | |||
| } | |||
| @@ -1,6 +1,17 @@ | |||
| <div id="gogs-body-nav" class="gogs-repo-nav"> | |||
| <div class="container"> | |||
| <div class="gogs-repo-btns pull-right"> | |||
| <div class="btn-group" id="gogs-repo-clone"> | |||
| <button type="button" class="btn btn-default"><i class="fa fa-download"></i>Clone</button> | |||
| <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"> | |||
| <span class="caret"></span> | |||
| <span class="sr-only">Toggle Dropdown</span> | |||
| </button> | |||
| <div class="dropdown-menu" role="menu"> | |||
| <div data-val="down-http">http link</div> | |||
| <div data-val="down-git">git link</div> | |||
| </div> | |||
| </div> | |||
| <div class="btn-group" id="gogs-repo-watching"> | |||
| <button type="button" class="btn btn-default"><i class="fa fa-eye"></i>Watch {x}</button> | |||
| <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"> | |||
| @@ -3,11 +3,58 @@ | |||
| {{template "repo/nav" .}} | |||
| {{template "repo/toolbar" .}} | |||
| <div id="gogs-body" class="container"> | |||
| <h4>Source Files:</h4> | |||
| <ul> | |||
| {{range .Files}} | |||
| <li>{{.Name}} - {{.Id}} - {{.Message}} - {{.Created}} - {{.IsFile}} - {{.IsDir}}</li> | |||
| {{end}} | |||
| </ul> | |||
| <div id="gogs-source"> | |||
| <div id="gogs-source-toolbar"> | |||
| <button class="btn btn-default pull-right"><i class="fa fa-plus-square"></i>Add File</button> | |||
| <div class="dropdown branch-switch"> | |||
| <a href="#" class="btn btn-success dropdown-toggle" data-toggle="dropdown"><i class="fa fa-chain"></i>master | |||
| <b class="caret"></b></a> | |||
| <ul class="dropdown-menu"> | |||
| <li><a class="current" href="/{{.RepositoryLink}}/branch/master">master</a></li> | |||
| <li><a href="//{{.RepositoryLink}}/branch/develop">develop</a></li> | |||
| </ul> | |||
| </div> | |||
| <ol class="breadcrumb"> | |||
| <li class="root dir">{{.Repository.Name}}</li> | |||
| {{$paths := .Paths}} | |||
| {{ $username := .Username}} | |||
| {{ $reponame := .Reponame}} | |||
| {{ $branchname := .Branchname}} | |||
| {{ $treenames := .Treenames}} | |||
| {{ $n := len $treenames}} | |||
| {{ $l := Subtract $n 1}} | |||
| {{range $i, $v := $treenames}} | |||
| <li class="dir"> | |||
| {{if eq $i $l}}{{$v}} | |||
| {{else}} | |||
| <a href="/{{$username}}/{{$reponame}}/tree/{{$branchname}}/{{index $paths $i}}">{{$v}}</a> | |||
| {{end}}</li> | |||
| {{end}} | |||
| </ol> | |||
| </div> | |||
| <table id="gogs-source-table" class="table table-hover"> | |||
| <thead class="hidden"> | |||
| <tr> | |||
| <th class="name">Filename</th> | |||
| <th class="date">Date modified</th> | |||
| <th class="text">Message</th> | |||
| </tr> | |||
| </thead> | |||
| <tbody> | |||
| {{range .Files}} | |||
| <tr {{if .IsDir}}class="is-dir"{{end}}> | |||
| <td class="name"><i class="fa {{if .IsDir}}fa-folder{{else}}fa-file{{end}}"></i> | |||
| {{if .IsDir}} | |||
| <a href="/{{$username}}/{{$reponame}}/tree/{{$branchname}}/{{.Path}}">{{.Name}}</a> | |||
| {{else}} | |||
| <a href="#">{{.Name}}</a> | |||
| {{end}}</td> | |||
| <td class="date"><time datetime="{{.Created}}" data-title="true" title="{{.Created}}">{{TimeSince .Created}}</time></td> | |||
| <td class="text">{{.Message}}</td> | |||
| </tr> | |||
| {{end}} | |||
| </tbody> | |||
| </table> | |||
| </div> | |||
| </div> | |||
| {{template "base/footer" .}} | |||
| @@ -1,40 +1,32 @@ | |||
| <div id="gogs-repo-toolbar"> | |||
| <div class="container"> | |||
| <nav class="navbar navbar-toolbar navbar-default" role="navigation"> | |||
| <div class="container-fluid"> | |||
| <div class="collapse navbar-collapse"> | |||
| <ul class="nav navbar-nav"> | |||
| <li class="dropdown"> | |||
| <a href="#" class="dropdown-toggle" data-toggle="dropdown">Branches <b class="caret"></b></a> | |||
| <ul class="dropdown-menu"> | |||
| <li><a href="#">master</a></li> | |||
| <li><a href="#">develop</a></li> | |||
| </ul> | |||
| </li> | |||
| <li class="{{if .IsRepoToolbarSource}}active{{end}}"><a href="/{{.RepositoryLink}}">Source</a></li> | |||
| <li><a href="#">Commits</a></li> | |||
| <li><a href="#">Issues <span class="badge">42</span></a></li> | |||
| <li><a href="#">Pull Requests</a></li> | |||
| <li class="dropdown"> | |||
| <a href="#" class="dropdown-toggle" data-toggle="dropdown">More <b class="caret"></b></a> | |||
| <ul class="dropdown-menu"> | |||
| <li><a href="#">Release</a></li> | |||
| <li><a href="#">Wiki</a></li> | |||
| </ul> | |||
| </li> | |||
| </ul> | |||
| <ul class="nav navbar-nav navbar-right"> | |||
| <li class="dropdown"> | |||
| <a href="#" class="dropdown-toggle" data-toggle="dropdown">Statistic <b class="caret"></b></a> | |||
| <ul class="dropdown-menu"> | |||
| <li><a href="#">Graphic</a></li> | |||
| <li><a href="#">Pulse</a></li> | |||
| <li><a href="#">Network</a></li> | |||
| </ul> | |||
| </li>{{if .IsRepositoryOwner}} | |||
| <li class="{{if .IsRepoToolbarSetting}}active{{end}}"><a href="/{{.RepositoryLink}}/settings">Settings</a></li>{{end}} | |||
| </ul> | |||
| </div> | |||
| <div class="collapse navbar-collapse"> | |||
| <ul class="nav navbar-nav"> | |||
| <li class="{{if .IsRepoToolbarSource}}active{{end}}"><a href="/{{.RepositoryLink}}">Source</a></li> | |||
| <li><a href="/{{.RepositoryLink}}/commits">Commits</a></li> | |||
| <li><a href="/{{.RepositoryLink}}/issues">Issues <!--<span class="badge">42</span>--></a></li> | |||
| <li><a href="/{{.RepositoryLink}}/pulls">Pull Requests</a></li> | |||
| <li class="dropdown"> | |||
| <a href="#" class="dropdown-toggle" data-toggle="dropdown">More <b class="caret"></b></a> | |||
| <ul class="dropdown-menu"> | |||
| <li><a href="/{{.RepositoryLink}}/release">Release</a></li> | |||
| <li><a href="//{{.RepositoryLink}}/wiki">Wiki</a></li> | |||
| </ul> | |||
| </li> | |||
| </ul> | |||
| <ul class="nav navbar-nav navbar-right"> | |||
| <li class="dropdown"> | |||
| <a href="#" class="dropdown-toggle" data-toggle="dropdown">Statistic <b class="caret"></b></a> | |||
| <ul class="dropdown-menu"> | |||
| <li><a href="#">Graphic</a></li> | |||
| <li><a href="#">Pulse</a></li> | |||
| <li><a href="#">Network</a></li> | |||
| </ul> | |||
| </li>{{if .IsRepositoryOwner}} | |||
| <li class="{{if .IsRepoToolbarSetting}}active{{end}}"><a href="/{{.RepositoryLink}}/settings">Settings</a> | |||
| </li>{{end}} | |||
| </ul> | |||
| </div> | |||
| </nav> | |||
| </div> | |||
| @@ -5,10 +5,10 @@ | |||
| <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="#">Emails and Password</a></li> | |||
| <li class="list-group-item"><a href="#">Notifications</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="#">Security</a></li> | |||
| <li class="list-group-item"><a href="/user/setting/security">Security</a></li> | |||
| <li class="list-group-item list-group-item-success"><a href="/user/delete">Delete Account</a></li> | |||
| </ul> | |||
| </div> | |||
| @@ -0,0 +1,19 @@ | |||
| {{template "base/head" .}} | |||
| {{template "base/navbar" .}} | |||
| <div id="gogs-body" class="container"> | |||
| <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> | |||
| <div id="gogs-user-setting-container" class="col-md-9"> | |||
| <h4>Notification</h4> | |||
| </div> | |||
| </div> | |||
| {{template "base/footer" .}} | |||
| @@ -0,0 +1,51 @@ | |||
| {{template "base/head" .}} | |||
| {{template "base/navbar" .}} | |||
| <div id="gogs-body" class="container"> | |||
| <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> | |||
| <div id="gogs-user-setting-container" class="col-md-9"> | |||
| <div id="gogs-setting-pwd"> | |||
| <h4>Password</h4> | |||
| <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}} | |||
| <div class="form-group"> | |||
| <label class="col-md-2 control-label">Old Password<strong class="text-danger">*</strong></label> | |||
| <div class="col-md-8"> | |||
| <input type="password" name="oldpasswd" class="form-control" placeholder="Type your current password" required="required"> | |||
| </div> | |||
| </div> | |||
| <div class="form-group"> | |||
| <label class="col-md-2 control-label">New Password<strong class="text-danger">*</strong></label> | |||
| <div class="col-md-8"> | |||
| <input type="password" name="newpasswd" class="form-control" placeholder="Type your new password" required="required"> | |||
| </div> | |||
| </div> | |||
| <div class="form-group"> | |||
| <label class="col-md-2 control-label">Re-Type<strong class="text-danger">*</strong></label> | |||
| <div class="col-md-8"> | |||
| <input type="password" name="retypepasswd" class="form-control" placeholder="Re-type your new password" required="required"> | |||
| </div> | |||
| </div> | |||
| <div class="form-group"> | |||
| <div class="col-md-offset-2 col-md-8"> | |||
| <button type="submit" class="btn btn-primary">Change Password</button> | |||
| <a href="/forget-password/">Forgot your password?</a> | |||
| </div> | |||
| </div> | |||
| </form> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| {{template "base/footer" .}} | |||
| @@ -4,16 +4,16 @@ | |||
| <div id="gogs-user-profile" class="col-md-3"> | |||
| <div class="profile-avatar text-center"> | |||
| <a href="#" class="center-block" data-toggle="tooltip" data-placement="bottom" title="Change Avatar"> | |||
| <img id="gogs-user-avatar" src="http://1.gravatar.com/avatar/{{.Avatar}}?s=200" alt="user-avatar" title="username"/> | |||
| <img id="gogs-user-avatar" src="http://1.gravatar.com/avatar/{{.Owner.Avatar}}?s=200" alt="user-avatar" title="username"/> | |||
| </a> | |||
| <span id="gogs-user-name" class="center-block" href="#">{{.Username}}</span> | |||
| <span id="gogs-user-name" class="center-block" href="#">{{.Owner.Name}}</span> | |||
| </div> | |||
| <div class="profile-info"> | |||
| <ul class="list-group"> | |||
| <li class="list-group-item"><i class="fa fa-thumb-tack"></i>City, County, State, Nation</li> | |||
| <li class="list-group-item"><i class="fa fa-envelope"></i><a href="#">Email@EmailAddress.com</a></li> | |||
| <li class="list-group-item"><i class="fa fa-link"></i><a href="#">http://yousite/</a></li> | |||
| <li class="list-group-item"><i class="fa fa-clock-o"></i>Joined At 03.02, 2014</li> | |||
| <li class="list-group-item"><i class="fa fa-thumb-tack"></i>{{.Owner.Location}}</li> | |||
| <li class="list-group-item"><i class="fa fa-envelope"></i><a href="mailto:{{.Owner.Email}}">{{.Owner.Email}}</a></li> | |||
| <li class="list-group-item"><i class="fa fa-link"></i><a target="_blank" href="{{.Owner.Website}}">{{.Owner.Website}}</a></li> | |||
| <li class="list-group-item"><i class="fa fa-clock-o"></i>{{.Owner.Created}}</li> | |||
| </ul> | |||
| </div> | |||
| </div> | |||
| @@ -5,10 +5,10 @@ | |||
| <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="#">Emails and Password</a></li> | |||
| <li class="list-group-item"><a href="#">Notifications</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="#">Security</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> | |||
| @@ -1,26 +0,0 @@ | |||
| {{template "base/head" .}} | |||
| {{template "base/navbar" .}} | |||
| <div class="container" id="gogs-body"> | |||
| <form action="/user/publickey/add" method="post" class="form-horizontal"> | |||
| <div class="form-group"> | |||
| <label class="col-md-4 control-label">Name of this public key: </label> | |||
| <div class="col-md-3"> | |||
| <input name="keyname" class="form-control" placeholder="Type your preferred name" value="{{.KeyName}}"> | |||
| </div> | |||
| </div> | |||
| <div class="form-group"> | |||
| <label class="col-md-4 control-label">Paste your key here: </label> | |||
| <div class="col-md-3"> | |||
| <textarea name="key_content" cols="30" rows="10" class="form-control">{{.KeyContent}}</textarea> | |||
| </div> | |||
| </div> | |||
| <div class="form-group"> | |||
| <div class="col-md-offset-4 col-md-3"> | |||
| <button type="submit" class="btn btn-info">Add public key</button> | |||
| </div> | |||
| </div> | |||
| </form> | |||
| </div> | |||
| {{template "base/footer" .}} | |||
| @@ -1,8 +0,0 @@ | |||
| {{template "base/head" .}} | |||
| {{template "base/navbar" .}} | |||
| <div class="container"> | |||
| <div class="form-group"> | |||
| publickey added | |||
| </div> | |||
| </div> | |||
| {{template "base/footer" .}} | |||
| @@ -1,12 +0,0 @@ | |||
| {{template "base/head" .}} | |||
| {{template "base/navbar" .}} | |||
| <div class="container" id="gogs-body"> | |||
| <div><a href="/user/publickey/add">Add publick key</a></div> | |||
| <ul> | |||
| {{range .Keys}} | |||
| <li>{{.Name}}</li> | |||
| <li>{{.Content}}</li> | |||
| {{end}} | |||
| </ul> | |||
| </div> | |||
| {{template "base/footer" .}} | |||
| @@ -0,0 +1,19 @@ | |||
| {{template "base/head" .}} | |||
| {{template "base/navbar" .}} | |||
| <div id="gogs-body" class="container"> | |||
| <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> | |||
| <div id="gogs-user-setting-container" class="col-md-9"> | |||
| <h4>Security</h4> | |||
| </div> | |||
| </div> | |||
| {{template "base/footer" .}} | |||
| @@ -5,15 +5,54 @@ | |||
| <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="#">Emails and Password</a></li> | |||
| <li class="list-group-item"><a href="#">Notifications</a></li> | |||
| <li class="list-group-item"><a href="/user/setting/ssh">SSH Keys</a></li> | |||
| <li class="list-group-item"><a href="#">Security</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> | |||
| <div id="gogs-user-setting-container" class="col-md-9"> | |||
| setting container | |||
| <div id="gogs-setting-pwd"> | |||
| <h4>Account Profile</h4> | |||
| <form class="form-horizontal" id="gogs-password-form" method="post" action="/user/setting">{{if .IsSuccess}} | |||
| <p class="alert alert-success">Your profile has been successfully updated.</p>{{else if .HasError}}<p class="alert alert-danger form-error">{{.ErrorMsg}}</p>{{end}} | |||
| <p>Your Email will be public and used for Account related notifications and any web based operations made via the web.</p> | |||
| <div class="form-group"> | |||
| <label class="col-md-2 control-label">Email</label> | |||
| <div class="col-md-8"> | |||
| <input type="text" name="email" class="form-control" placeholder="Type your e-mail address" value="{{.Owner.Email}}"> | |||
| </div> | |||
| </div> | |||
| <div class="form-group"> | |||
| <label class="col-md-2 control-label">Website</label> | |||
| <div class="col-md-8"> | |||
| <input type="text" name="website" class="form-control" placeholder="Type your website URL" value="{{.Owner.Website}}"> | |||
| </div> | |||
| </div> | |||
| <div class="form-group"> | |||
| <label class="col-md-2 control-label">Location</label> | |||
| <div class="col-md-8"> | |||
| <input type="text" name="location" class="form-control" placeholder="Type your current location" value="{{.Owner.Location}}"> | |||
| </div> | |||
| </div> | |||
| <div class="form-group"> | |||
| <label class="col-md-2 control-label">Gravatar Email<strong class="text-danger">*</strong></label> | |||
| <div class="col-md-8"> | |||
| <input type="text" name="avatar" class="form-control" placeholder="Type your Gravatar e-mail address" required="required" value="{{.Owner.AvatarEmail}}"> | |||
| </div> | |||
| </div> | |||
| <div class="form-group"> | |||
| <div class="col-md-offset-2 col-md-8"> | |||
| <button type="submit" class="btn btn-primary">Update Profile</button> | |||
| </div> | |||
| </div> | |||
| </form> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| {{template "base/footer" .}} | |||
| @@ -7,7 +7,7 @@ | |||
| <div class="form-group {{if .Err_UserName}}has-error has-feedback{{end}}"> | |||
| <label class="col-md-4 control-label">Username: </label> | |||
| <div class="col-md-6"> | |||
| <input name="username" class="form-control" placeholder="Type your username" value="{{.username}}" required="required" title="Username must contain at least 5 characters"> | |||
| <input name="username" class="form-control" placeholder="Type your username" value="{{.username}}" required="required"> | |||
| </div> | |||
| </div> | |||
| @@ -33,6 +33,55 @@ gogs web`, | |||
| Flags: []cli.Flag{}, | |||
| } | |||
| func Subtract(left interface{}, right interface{}) interface{} { | |||
| var rleft, rright int64 | |||
| var fleft, fright float64 | |||
| var isInt bool = true | |||
| switch left.(type) { | |||
| case int: | |||
| rleft = int64(left.(int)) | |||
| case int8: | |||
| rleft = int64(left.(int8)) | |||
| case int16: | |||
| rleft = int64(left.(int16)) | |||
| case int32: | |||
| rleft = int64(left.(int32)) | |||
| case int64: | |||
| rleft = left.(int64) | |||
| case float32: | |||
| fleft = float64(left.(float32)) | |||
| isInt = false | |||
| case float64: | |||
| fleft = left.(float64) | |||
| isInt = false | |||
| } | |||
| switch right.(type) { | |||
| case int: | |||
| rright = int64(right.(int)) | |||
| case int8: | |||
| rright = int64(right.(int8)) | |||
| case int16: | |||
| rright = int64(right.(int16)) | |||
| case int32: | |||
| rright = int64(right.(int32)) | |||
| case int64: | |||
| rright = right.(int64) | |||
| case float32: | |||
| fright = float64(left.(float32)) | |||
| isInt = false | |||
| case float64: | |||
| fleft = left.(float64) | |||
| isInt = false | |||
| } | |||
| if isInt { | |||
| return rleft - rright | |||
| } else { | |||
| return fleft + float64(rleft) - (fright + float64(rright)) | |||
| } | |||
| } | |||
| var AppHelpers template.FuncMap = map[string]interface{}{ | |||
| "AppName": func() string { | |||
| return base.Cfg.MustValue("", "APP_NAME") | |||
| @@ -40,6 +89,8 @@ var AppHelpers template.FuncMap = map[string]interface{}{ | |||
| "AppVer": func() string { | |||
| return APP_VER | |||
| }, | |||
| "TimeSince": base.TimeSince, | |||
| "Subtract": Subtract, | |||
| } | |||
| func runWeb(*cli.Context) { | |||
| @@ -63,8 +114,11 @@ func runWeb(*cli.Context) { | |||
| m.Any("/user/delete", auth.SignInRequire(true), user.Delete) | |||
| m.Get("/user/feeds", binding.Bind(auth.FeedsForm{}), user.Feeds) | |||
| m.Any("/user/setting", auth.SignInRequire(true), user.Setting) | |||
| m.Any("/user/setting", auth.SignInRequire(true), binding.BindIgnErr(auth.UpdateProfileForm{}), user.Setting) | |||
| m.Any("/user/setting/password", auth.SignInRequire(true), binding.BindIgnErr(auth.UpdatePasswdForm{}), user.SettingPassword) | |||
| m.Any("/user/setting/ssh", auth.SignInRequire(true), binding.BindIgnErr(auth.AddSSHKeyForm{}), user.SettingSSHKeys) | |||
| m.Any("/user/setting/notification", auth.SignInRequire(true), user.SettingNotification) | |||
| m.Any("/user/setting/security", auth.SignInRequire(true), user.SettingSecurity) | |||
| m.Get("/user/:username", auth.SignInRequire(false), user.Profile) | |||
| @@ -73,6 +127,10 @@ func runWeb(*cli.Context) { | |||
| m.Any("/repo/list", auth.SignInRequire(false), repo.List) | |||
| m.Get("/:username/:reponame/settings", auth.SignInRequire(false), auth.RepoAssignment(true), repo.Setting) | |||
| m.Get("/:username/:reponame/tree/:branchname/**", | |||
| auth.SignInRequire(false), auth.RepoAssignment(true), repo.Single) | |||
| m.Get("/:username/:reponame/tree/:branchname", | |||
| auth.SignInRequire(false), auth.RepoAssignment(true), repo.Single) | |||
| m.Get("/:username/:reponame", auth.SignInRequire(false), auth.RepoAssignment(true), repo.Single) | |||
| //m.Get("/:username/:reponame", repo.Repo) | |||