diff --git a/.gitignore b/.gitignore index 0323c2972..6559d61a0 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ gogs .DS_Store *.db *.log -custom/ \ No newline at end of file +custom/ +.vendor/ diff --git a/README.md b/README.md index abb0b8ce2..0e354a9d4 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,8 @@ Gogs - Go Git Service [![wercker status](https://app.wercker.com/status/ad0bdb0b 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 diff --git a/conf/app.ini b/conf/app.ini index fcfbe9bc2..aa42129cc 100644 --- a/conf/app.ini +++ b/conf/app.ini @@ -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 = diff --git a/conf/gitignore/C b/conf/gitignore/C new file mode 100644 index 000000000..e7a909e4c --- /dev/null +++ b/conf/gitignore/C @@ -0,0 +1,18 @@ +# Object files +*.o +*.ko + +# Libraries +*.lib +*.a + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app \ No newline at end of file diff --git a/conf/license/BSD (3-Clause) License b/conf/license/BSD (3-Clause) License new file mode 100644 index 000000000..3af16b072 --- /dev/null +++ b/conf/license/BSD (3-Clause) License @@ -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. \ No newline at end of file diff --git a/gogs.go b/gogs.go index f1a702ed2..b27649028 100644 --- a/gogs.go +++ b/gogs.go @@ -20,7 +20,7 @@ import ( // Test that go1.1 tag above is included in builds. main.go refers to this definition. const go11tag = true -const APP_VER = "0.0.6.0313" +const APP_VER = "0.0.7.0314" func init() { runtime.GOMAXPROCS(runtime.NumCPU()) diff --git a/models/repo.go b/models/repo.go index a44bf69ad..c5fa899f4 100644 --- a/models/repo.go +++ b/models/repo.go @@ -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 diff --git a/models/user.go b/models/user.go index c59e4ae15..8f7a37cba 100644 --- a/models/user.go +++ b/models/user.go @@ -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 } diff --git a/modules/auth/auth.go b/modules/auth/auth.go index e4748650c..0e8716889 100644 --- a/modules/auth/auth.go +++ b/modules/auth/auth.go @@ -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(") { diff --git a/modules/auth/user.go b/modules/auth/user.go index 4059edfdd..ef595c600 100644 --- a/modules/auth/user.go +++ b/modules/auth/user.go @@ -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) +} diff --git a/modules/base/tool.go b/modules/base/tool.go index eef473f96..445833ceb 100644 --- a/modules/base/tool.go +++ b/modules/base/tool.go @@ -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() +} diff --git a/modules/log/log.go b/modules/log/log.go index 0634bde65..b3a3e0a63 100644 --- a/modules/log/log.go +++ b/modules/log/log.go @@ -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) +} diff --git a/public/css/gogs.css b/public/css/gogs.css index a44d4a938..160f561ca 100755 --- a/public/css/gogs.css +++ b/public/css/gogs.css @@ -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; } \ No newline at end of file diff --git a/public/js/app.js b/public/js/app.js index 69d21020e..0f0ecc43e 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -78,7 +78,6 @@ function initRegister() { rules: { "username": { required: true, - minlength: 5, maxlength: 30 }, "email": { diff --git a/routers/repo/repo.go b/routers/repo/repo.go index a12b45d7b..b25ce1899 100644 --- a/routers/repo/repo.go +++ b/routers/repo/repo.go @@ -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 } diff --git a/routers/repo/single.go b/routers/repo/single.go index 1d5e601aa..bc35b98e7 100644 --- a/routers/repo/single.go +++ b/routers/repo/single.go @@ -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 diff --git a/routers/user/setting.go b/routers/user/setting.go index 0669784be..8b5a2d18d 100644 --- a/routers/user/setting.go +++ b/routers/user/setting.go @@ -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) +} diff --git a/routers/user/user.go b/routers/user/user.go index 59177a47b..6095b5380 100644 --- a/routers/user/user.go +++ b/routers/user/user.go @@ -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 } } diff --git a/templates/repo/nav.tmpl b/templates/repo/nav.tmpl index 5d60d4610..1a9434e86 100644 --- a/templates/repo/nav.tmpl +++ b/templates/repo/nav.tmpl @@ -1,6 +1,17 @@
+
+ + + +
+ + +
+ + + + + + + + + + {{range .Files}} + + + + + + {{end}} + +
+ {{if .IsDir}} + {{.Name}} + {{else}} + {{.Name}} + {{end}}{{.Message}}
+
{{template "base/footer" .}} \ No newline at end of file diff --git a/templates/repo/toolbar.tmpl b/templates/repo/toolbar.tmpl index a409fcbea..4a0b60ada 100644 --- a/templates/repo/toolbar.tmpl +++ b/templates/repo/toolbar.tmpl @@ -1,40 +1,32 @@
diff --git a/templates/base/error.tmpl b/templates/status/200.tmpl similarity index 100% rename from templates/base/error.tmpl rename to templates/status/200.tmpl diff --git a/templates/user/delete.tmpl b/templates/user/delete.tmpl index af0e1fbc0..904201772 100644 --- a/templates/user/delete.tmpl +++ b/templates/user/delete.tmpl @@ -5,10 +5,10 @@

Account Setting

diff --git a/templates/user/notification.tmpl b/templates/user/notification.tmpl new file mode 100644 index 000000000..c1abc46fb --- /dev/null +++ b/templates/user/notification.tmpl @@ -0,0 +1,19 @@ +{{template "base/head" .}} +{{template "base/navbar" .}} +
+ +
+

Notification

+
+
+{{template "base/footer" .}} \ No newline at end of file diff --git a/templates/user/password.tmpl b/templates/user/password.tmpl new file mode 100644 index 000000000..a8b1e21e0 --- /dev/null +++ b/templates/user/password.tmpl @@ -0,0 +1,51 @@ +{{template "base/head" .}} +{{template "base/navbar" .}} +
+ +
+
+

Password

+
{{if .IsSuccess}} +

Password is changed successfully. You can now sign in via new password.

{{else if .HasError}}

{{.ErrorMsg}}

{{end}} +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+
+    + Forgot your password? +
+
+
+
+
+
+{{template "base/footer" .}} \ No newline at end of file diff --git a/templates/user/profile.tmpl b/templates/user/profile.tmpl index 79e6062bc..c10bfcb0f 100644 --- a/templates/user/profile.tmpl +++ b/templates/user/profile.tmpl @@ -4,16 +4,16 @@
- user-avatar + user-avatar - {{.Username}} + {{.Owner.Name}}
diff --git a/templates/user/publickey.tmpl b/templates/user/publickey.tmpl index 0bd76593e..60d2c2464 100644 --- a/templates/user/publickey.tmpl +++ b/templates/user/publickey.tmpl @@ -5,10 +5,10 @@

Account Setting

diff --git a/templates/user/publickey_add.tmpl b/templates/user/publickey_add.tmpl deleted file mode 100644 index 634b859dd..000000000 --- a/templates/user/publickey_add.tmpl +++ /dev/null @@ -1,26 +0,0 @@ -{{template "base/head" .}} -{{template "base/navbar" .}} -
-
-
- -
- -
-
- -
- -
- -
-
- -
-
- -
-
-
-
-{{template "base/footer" .}} \ No newline at end of file diff --git a/templates/user/publickey_added.tmpl b/templates/user/publickey_added.tmpl deleted file mode 100644 index f67da9ed8..000000000 --- a/templates/user/publickey_added.tmpl +++ /dev/null @@ -1,8 +0,0 @@ -{{template "base/head" .}} -{{template "base/navbar" .}} -
-
- publickey added -
-
-{{template "base/footer" .}} \ No newline at end of file diff --git a/templates/user/publickey_list.tmpl b/templates/user/publickey_list.tmpl deleted file mode 100644 index fbd640b4b..000000000 --- a/templates/user/publickey_list.tmpl +++ /dev/null @@ -1,12 +0,0 @@ -{{template "base/head" .}} -{{template "base/navbar" .}} -
-
Add publick key
- -
-{{template "base/footer" .}} \ No newline at end of file diff --git a/templates/user/security.tmpl b/templates/user/security.tmpl new file mode 100644 index 000000000..37cd5872a --- /dev/null +++ b/templates/user/security.tmpl @@ -0,0 +1,19 @@ +{{template "base/head" .}} +{{template "base/navbar" .}} +
+
+

Account Setting

+ +
+
+

Security

+
+
+{{template "base/footer" .}} \ No newline at end of file diff --git a/templates/user/setting.tmpl b/templates/user/setting.tmpl index 79de58fe7..edbeeb22a 100644 --- a/templates/user/setting.tmpl +++ b/templates/user/setting.tmpl @@ -5,15 +5,54 @@

Account Setting

- setting container +
+

Account Profile

+
{{if .IsSuccess}} +

Your profile has been successfully updated.

{{else if .HasError}}

{{.ErrorMsg}}

{{end}} +

Your Email will be public and used for Account related notifications and any web based operations made via the web.

+
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+
+ +
+
+
+
{{template "base/footer" .}} \ No newline at end of file diff --git a/templates/user/signup.tmpl b/templates/user/signup.tmpl index ba41b0c9b..2f1e82c73 100644 --- a/templates/user/signup.tmpl +++ b/templates/user/signup.tmpl @@ -7,7 +7,7 @@
- +
diff --git a/web.go b/web.go index 475b662b3..c60a262b0 100644 --- a/web.go +++ b/web.go @@ -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)