| @@ -4,4 +4,5 @@ gogs | |||||
| .DS_Store | .DS_Store | ||||
| *.db | *.db | ||||
| *.log | *.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. | 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 | ## 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. | 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. | - SSH protocal support. | ||||
| - Register/delete account. | - Register/delete account. | ||||
| - Create public repository. | |||||
| - Create/delete public repository. | |||||
| - User/repository home page. | |||||
| - Git repository manipulation. | - Git repository manipulation. | ||||
| ## Installation | ## Installation | ||||
| @@ -3,8 +3,8 @@ RUN_USER = lunny | |||||
| [repository] | [repository] | ||||
| ROOT = /Users/lunny/git/gogs-repositories | 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] | [server] | ||||
| HTTP_ADDR = | 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. | // 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.0.6.0313" | |||||
| const APP_VER = "0.0.7.0314" | |||||
| func init() { | func init() { | ||||
| runtime.GOMAXPROCS(runtime.NumCPU()) | runtime.GOMAXPROCS(runtime.NumCPU()) | ||||
| @@ -9,6 +9,7 @@ import ( | |||||
| "fmt" | "fmt" | ||||
| "io/ioutil" | "io/ioutil" | ||||
| "os" | "os" | ||||
| "path" | |||||
| "path/filepath" | "path/filepath" | ||||
| "strings" | "strings" | ||||
| "time" | "time" | ||||
| @@ -270,6 +271,7 @@ type RepoFile struct { | |||||
| Id *git.Oid | Id *git.Oid | ||||
| Type int | Type int | ||||
| Name string | Name string | ||||
| Path string | |||||
| Message string | Message string | ||||
| Created time.Time | Created time.Time | ||||
| } | } | ||||
| @@ -282,7 +284,7 @@ func (f *RepoFile) IsDir() bool { | |||||
| return f.Type == git.FilemodeTree | 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) | f := RepoPath(userName, reposName) | ||||
| repo, err := git.OpenRepository(f) | repo, err := git.OpenRepository(f) | ||||
| if err != nil { | if err != nil { | ||||
| @@ -299,8 +301,28 @@ func GetReposFiles(userName, reposName, treeName, rpath string) ([]*RepoFile, er | |||||
| if err != nil { | if err != nil { | ||||
| return nil, err | 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) | entry := tree.EntryByIndex(i) | ||||
| repofiles = append(repofiles, &RepoFile{ | repofiles = append(repofiles, &RepoFile{ | ||||
| @@ -310,7 +332,7 @@ func GetReposFiles(userName, reposName, treeName, rpath string) ([]*RepoFile, er | |||||
| lastCommit.Message(), | lastCommit.Message(), | ||||
| lastCommit.Committer().When, | lastCommit.Committer().When, | ||||
| }) | }) | ||||
| } | |||||
| }*/ | |||||
| return repofiles, nil | return repofiles, nil | ||||
| } | } | ||||
| @@ -354,6 +376,10 @@ func DeleteRepository(userId, repoId int64, userName string) (err error) { | |||||
| session.Rollback() | session.Rollback() | ||||
| return err | 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 { | if _, err = session.Exec("update user set num_repos = num_repos - 1 where id = ?", userId); err != nil { | ||||
| session.Rollback() | session.Rollback() | ||||
| return err | return err | ||||
| @@ -48,7 +48,10 @@ type User struct { | |||||
| NumFollowings int | NumFollowings int | ||||
| NumStars int | NumStars int | ||||
| NumRepos 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"` | Created time.Time `xorm:"created"` | ||||
| Updated time.Time `xorm:"updated"` | Updated time.Time `xorm:"updated"` | ||||
| } | } | ||||
| @@ -104,6 +107,7 @@ func RegisterUser(user *User) (err error) { | |||||
| 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 | |||||
| if err = user.EncodePasswd(); err != nil { | if err = user.EncodePasswd(); err != nil { | ||||
| return err | return err | ||||
| } | } | ||||
| @@ -23,7 +23,7 @@ type Form interface { | |||||
| } | } | ||||
| type RegisterForm struct { | 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)"` | Email string `form:"email" binding:"Required;Email;MaxSize(50)"` | ||||
| Password string `form:"passwd" binding:"Required;MinSize(6);MaxSize(30)"` | Password string `form:"passwd" binding:"Required;MinSize(6);MaxSize(30)"` | ||||
| RetypePasswd string `form:"retypepasswd"` | RetypePasswd string `form:"retypepasswd"` | ||||
| @@ -59,7 +59,7 @@ func (f *RegisterForm) Validate(errors *binding.Errors, req *http.Request, conte | |||||
| } | } | ||||
| type LogInForm struct { | 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)"` | 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) | validate(errors, data, f) | ||||
| } | } | ||||
| type FeedsForm struct { | |||||
| UserId int64 `form:"userid" binding:"Required"` | |||||
| Offset int64 `form:"offset"` | |||||
| } | |||||
| func getMinMaxSize(field reflect.StructField) string { | func getMinMaxSize(field reflect.StructField) string { | ||||
| for _, rule := range strings.Split(field.Tag.Get("binding"), ";") { | for _, rule := range strings.Split(field.Tag.Get("binding"), ";") { | ||||
| if strings.HasPrefix(rule, "MinSize(") || strings.HasPrefix(rule, "MaxSize(") { | if strings.HasPrefix(rule, "MinSize(") || strings.HasPrefix(rule, "MaxSize(") { | ||||
| @@ -5,10 +5,15 @@ | |||||
| package auth | package auth | ||||
| import ( | import ( | ||||
| "net/http" | |||||
| "reflect" | |||||
| "github.com/codegangsta/martini" | "github.com/codegangsta/martini" | ||||
| "github.com/martini-contrib/render" | "github.com/martini-contrib/render" | ||||
| "github.com/martini-contrib/sessions" | "github.com/martini-contrib/sessions" | ||||
| "github.com/gogits/binding" | |||||
| "github.com/gogits/gogs/models" | "github.com/gogits/gogs/models" | ||||
| "github.com/gogits/gogs/modules/base" | "github.com/gogits/gogs/modules/base" | ||||
| "github.com/gogits/gogs/modules/log" | "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 ( | import ( | ||||
| "crypto/md5" | "crypto/md5" | ||||
| "encoding/hex" | "encoding/hex" | ||||
| "fmt" | |||||
| "time" | |||||
| ) | ) | ||||
| // Encode string to md5 hex value | // Encode string to md5 hex value | ||||
| @@ -15,3 +17,64 @@ func EncodeMd5(str string) string { | |||||
| m.Write([]byte(str)) | m.Write([]byte(str)) | ||||
| return hex.EncodeToString(m.Sum(nil)) | 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 | package log | ||||
| import ( | import ( | ||||
| "fmt" | |||||
| "github.com/martini-contrib/render" | |||||
| "github.com/gogits/logs" | "github.com/gogits/logs" | ||||
| "github.com/gogits/gogs/modules/base" | |||||
| ) | ) | ||||
| var logger *logs.BeeLogger | var logger *logs.BeeLogger | ||||
| @@ -35,3 +41,9 @@ func Warn(format string, v ...interface{}) { | |||||
| func Critical(format string, v ...interface{}) { | func Critical(format string, v ...interface{}) { | ||||
| logger.Critical(format, v...) | 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; | margin-top: 50px; | ||||
| } | } | ||||
| #gogs-body .btn-default { | |||||
| background-color: #FFF; | |||||
| background-image: linear-gradient(to bottom, #FFF 0, #FAFAFA 100%); | |||||
| } | |||||
| #gogs-body-nav { | #gogs-body-nav { | ||||
| margin-top: 52px; | margin-top: 52px; | ||||
| margin-bottom: -50px; | margin-bottom: -50px; | ||||
| @@ -269,7 +274,8 @@ body { | |||||
| /* gogits user setting */ | /* 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; | padding-bottom: 18px; | ||||
| margin-bottom: 18px; | margin-bottom: 18px; | ||||
| border-bottom: 1px solid #CCC; | border-bottom: 1px solid #CCC; | ||||
| @@ -380,6 +386,7 @@ body { | |||||
| } | } | ||||
| #gogs-feed-right .repo-panel .list-group-item:hover { | #gogs-feed-right .repo-panel .list-group-item:hover { | ||||
| background-color: #eafffd; | |||||
| background-color: rgba(65, 131, 196, 0.1); | background-color: rgba(65, 131, 196, 0.1); | ||||
| } | } | ||||
| @@ -391,6 +398,12 @@ body { | |||||
| /* gogits repo single page */ | /* gogits repo single page */ | ||||
| #gogs-body-nav.gogs-repo-nav { | |||||
| padding-top: 16px; | |||||
| padding-bottom: 30px; | |||||
| height: auto; | |||||
| } | |||||
| .gogs-repo-nav h3 .fa { | .gogs-repo-nav h3 .fa { | ||||
| color: #BBB; | color: #BBB; | ||||
| } | } | ||||
| @@ -429,32 +442,82 @@ body { | |||||
| margin-bottom: 4px; | margin-bottom: 4px; | ||||
| } | } | ||||
| #gogs-repo-toolbar{ | |||||
| #gogs-repo-toolbar { | |||||
| margin-top: 51px; | margin-top: 51px; | ||||
| margin-bottom: -50px; | margin-bottom: -50px; | ||||
| border-bottom: 1px solid #BBB; | border-bottom: 1px solid #BBB; | ||||
| background-color: #FFF; | background-color: #FFF; | ||||
| height: 40px; | height: 40px; | ||||
| font-size: 14px; | |||||
| } | } | ||||
| #gogs-repo-toolbar .navbar-default{ | |||||
| #gogs-repo-toolbar .navbar-default { | |||||
| border: none; | border: none; | ||||
| height: 39px; | height: 39px; | ||||
| } | } | ||||
| #gogs-repo-toolbar .nav > li > a{ | |||||
| #gogs-repo-toolbar .nav > li > a { | |||||
| height: 39px; | 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; | border-bottom-color: #999; | ||||
| } | } | ||||
| #gogs-repo-toolbar .navbar.nav-toolbar{ | |||||
| #gogs-repo-toolbar .navbar.nav-toolbar { | |||||
| margin-bottom: 0; | margin-bottom: 0; | ||||
| } | } | ||||
| #gogs-repo-toolbar .navbar-collapse{ | |||||
| #gogs-repo-toolbar .navbar-collapse { | |||||
| padding: 0; | 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: { | rules: { | ||||
| "username": { | "username": { | ||||
| required: true, | required: true, | ||||
| minlength: 5, | |||||
| maxlength: 30 | maxlength: 30 | ||||
| }, | }, | ||||
| "email": { | "email": { | ||||
| @@ -5,9 +5,10 @@ | |||||
| package repo | package repo | ||||
| import ( | import ( | ||||
| "net/http" | |||||
| "github.com/martini-contrib/render" | "github.com/martini-contrib/render" | ||||
| "github.com/martini-contrib/sessions" | "github.com/martini-contrib/sessions" | ||||
| "net/http" | |||||
| "github.com/gogits/gogs/models" | "github.com/gogits/gogs/models" | ||||
| "github.com/gogits/gogs/modules/auth" | "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 == nil { | ||||
| if _, err = models.CreateRepository(user, | if _, err = models.CreateRepository(user, | ||||
| form.RepoName, form.Description, form.Language, form.License, | 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 { | if err == nil { | ||||
| data["RepoName"] = user.Name + "/" + form.RepoName | data["RepoName"] = user.Name + "/" + form.RepoName | ||||
| r.HTML(200, "repo/created", data) | r.HTML(200, "repo/created", data) | ||||
| @@ -63,9 +64,7 @@ func Create(form auth.CreateRepoForm, req *http.Request, r render.Render, data b | |||||
| return | 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) { | 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 { | 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 | return | ||||
| } | } | ||||
| r.Redirect("/", 200) | |||||
| r.Redirect("/", 302) | |||||
| } | } | ||||
| func List(req *http.Request, r render.Render, data base.TmplData, session sessions.Session) { | 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" | data["Title"] = "Repositories" | ||||
| repos, err := models.GetRepositories(u) | repos, err := models.GetRepositories(u) | ||||
| if err != nil { | 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 | return | ||||
| } | } | ||||
| @@ -1,6 +1,7 @@ | |||||
| package repo | package repo | ||||
| import ( | import ( | ||||
| "strings" | |||||
| "github.com/codegangsta/martini" | "github.com/codegangsta/martini" | ||||
| "github.com/martini-contrib/render" | "github.com/martini-contrib/render" | ||||
| @@ -13,15 +14,27 @@ func Single(params martini.Params, r render.Render, data base.TmplData) { | |||||
| if !data["IsRepositoryValid"].(bool) { | if !data["IsRepositoryValid"].(bool) { | ||||
| return | 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 { | 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 | 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["IsRepoToolbarSource"] = true | ||||
| data["Files"] = files | data["Files"] = files | ||||
| @@ -17,12 +17,72 @@ import ( | |||||
| "github.com/gogits/gogs/modules/log" | "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["Title"] = "Setting" | ||||
| data["PageIsUserSetting"] = true | 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) | 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) { | func SettingSSHKeys(form auth.AddSSHKeyForm, r render.Render, data base.TmplData, req *http.Request, session sessions.Session) { | ||||
| data["Title"] = "SSH Keys" | data["Title"] = "SSH Keys" | ||||
| @@ -94,3 +154,17 @@ func SettingSSHKeys(form auth.AddSSHKeyForm, r render.Render, data base.TmplData | |||||
| data["Keys"] = keys | data["Keys"] = keys | ||||
| r.HTML(200, "user/publickey", data) | 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 | data["PageIsUserDashboard"] = true | ||||
| repos, err := models.GetRepositories(&models.User{Id: auth.SignedInId(session)}) | repos, err := models.GetRepositories(&models.User{Id: auth.SignedInId(session)}) | ||||
| if err != nil { | 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 | return | ||||
| } | } | ||||
| data["MyRepos"] = repos | 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. | // TODO: Need to check view self or others. | ||||
| user, err := models.GetUserByName(params["username"]) | user, err := models.GetUserByName(params["username"]) | ||||
| if err != nil { | 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 | return | ||||
| } | } | ||||
| data["Avatar"] = user.Avatar | |||||
| data["Username"] = user.Name | |||||
| data["Owner"] = user | |||||
| r.HTML(200, "user/profile", data) | r.HTML(200, "user/profile", data) | ||||
| } | } | ||||
| @@ -71,9 +66,7 @@ func SignIn(form auth.LogInForm, data base.TmplData, req *http.Request, r render | |||||
| return | 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 | 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" | data["ErrorMsg"] = "E-mail address has been already used" | ||||
| r.HTML(200, "user/signup", data) | r.HTML(200, "user/signup", data) | ||||
| default: | 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 | return | ||||
| } | } | ||||
| @@ -156,9 +147,7 @@ func Delete(data base.TmplData, req *http.Request, session sessions.Session, r r | |||||
| case models.ErrUserOwnRepos.Error(): | case models.ErrUserOwnRepos.Error(): | ||||
| data["ErrorMsg"] = "Your account still have ownership of repository, you have to delete or transfer them first." | data["ErrorMsg"] = "Your account still have ownership of repository, you have to delete or transfer them first." | ||||
| default: | 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 | return | ||||
| } | } | ||||
| } | } | ||||
| @@ -1,6 +1,17 @@ | |||||
| <div id="gogs-body-nav" class="gogs-repo-nav"> | <div id="gogs-body-nav" class="gogs-repo-nav"> | ||||
| <div class="container"> | <div class="container"> | ||||
| <div class="gogs-repo-btns pull-right"> | <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"> | <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"><i class="fa fa-eye"></i>Watch {x}</button> | ||||
| <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"> | <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"> | ||||
| @@ -3,11 +3,58 @@ | |||||
| {{template "repo/nav" .}} | {{template "repo/nav" .}} | ||||
| {{template "repo/toolbar" .}} | {{template "repo/toolbar" .}} | ||||
| <div id="gogs-body" class="container"> | <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> | </div> | ||||
| {{template "base/footer" .}} | {{template "base/footer" .}} | ||||
| @@ -1,40 +1,32 @@ | |||||
| <div id="gogs-repo-toolbar"> | <div id="gogs-repo-toolbar"> | ||||
| <div class="container"> | <div class="container"> | ||||
| <nav class="navbar navbar-toolbar navbar-default" role="navigation"> | <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> | </div> | ||||
| </nav> | </nav> | ||||
| </div> | </div> | ||||
| @@ -5,10 +5,10 @@ | |||||
| <h4>Account Setting</h4> | <h4>Account Setting</h4> | ||||
| <ul class="list-group"> | <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">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="/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> | <li class="list-group-item list-group-item-success"><a href="/user/delete">Delete Account</a></li> | ||||
| </ul> | </ul> | ||||
| </div> | </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 id="gogs-user-profile" class="col-md-3"> | ||||
| <div class="profile-avatar text-center"> | <div class="profile-avatar text-center"> | ||||
| <a href="#" class="center-block" data-toggle="tooltip" data-placement="bottom" title="Change Avatar"> | <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> | </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> | ||||
| <div class="profile-info"> | <div class="profile-info"> | ||||
| <ul class="list-group"> | <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> | </ul> | ||||
| </div> | </div> | ||||
| </div> | </div> | ||||
| @@ -5,10 +5,10 @@ | |||||
| <h4>Account Setting</h4> | <h4>Account Setting</h4> | ||||
| <ul class="list-group"> | <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">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 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> | <li class="list-group-item"><a href="/user/delete">Delete Account</a></li> | ||||
| </ul> | </ul> | ||||
| </div> | </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> | <h4>Account Setting</h4> | ||||
| <ul class="list-group"> | <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 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> | <li class="list-group-item"><a href="/user/delete">Delete Account</a></li> | ||||
| </ul> | </ul> | ||||
| </div> | </div> | ||||
| <div id="gogs-user-setting-container" class="col-md-9"> | <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> | ||||
| </div> | </div> | ||||
| {{template "base/footer" .}} | {{template "base/footer" .}} | ||||
| @@ -7,7 +7,7 @@ | |||||
| <div class="form-group {{if .Err_UserName}}has-error has-feedback{{end}}"> | <div class="form-group {{if .Err_UserName}}has-error has-feedback{{end}}"> | ||||
| <label class="col-md-4 control-label">Username: </label> | <label class="col-md-4 control-label">Username: </label> | ||||
| <div class="col-md-6"> | <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> | ||||
| </div> | </div> | ||||
| @@ -33,6 +33,55 @@ gogs web`, | |||||
| Flags: []cli.Flag{}, | 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{}{ | var AppHelpers template.FuncMap = map[string]interface{}{ | ||||
| "AppName": func() string { | "AppName": func() string { | ||||
| return base.Cfg.MustValue("", "APP_NAME") | return base.Cfg.MustValue("", "APP_NAME") | ||||
| @@ -40,6 +89,8 @@ var AppHelpers template.FuncMap = map[string]interface{}{ | |||||
| "AppVer": func() string { | "AppVer": func() string { | ||||
| return APP_VER | return APP_VER | ||||
| }, | }, | ||||
| "TimeSince": base.TimeSince, | |||||
| "Subtract": Subtract, | |||||
| } | } | ||||
| func runWeb(*cli.Context) { | func runWeb(*cli.Context) { | ||||
| @@ -63,8 +114,11 @@ func runWeb(*cli.Context) { | |||||
| m.Any("/user/delete", auth.SignInRequire(true), user.Delete) | m.Any("/user/delete", auth.SignInRequire(true), 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", 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/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) | 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.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/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", auth.SignInRequire(false), auth.RepoAssignment(true), repo.Single) | ||||
| //m.Get("/:username/:reponame", repo.Repo) | //m.Get("/:username/:reponame", repo.Repo) | ||||