| @@ -17,6 +17,7 @@ Please see [Wiki](https://github.com/gogits/gogs/wiki) for project design, devel | |||
| ## Features | |||
| - Activity timeline | |||
| - SSH protocal support. | |||
| - Register/delete account. | |||
| - Create/delete public repository. | |||
| @@ -3,8 +3,8 @@ RUN_USER = lunny | |||
| [repository] | |||
| ROOT = /Users/%(RUN_USER)s/git/gogs-repositories | |||
| LANG_IGNS=Google Go|C|Python | |||
| LICENSES=Apache v2 License|GPL v2|BSD (3-Clause) License | |||
| LANG_IGNS=Google Go|C|Python|Ruby | |||
| LICENSES=Apache v2 License|GPL v2|MIT License|BSD (3-Clause) License | |||
| [server] | |||
| HTTP_ADDR = | |||
| @@ -0,0 +1,18 @@ | |||
| *.gem | |||
| *.rbc | |||
| .bundle | |||
| .config | |||
| coverage | |||
| InstalledFiles | |||
| lib/bundler/man | |||
| pkg | |||
| rdoc | |||
| spec/reports | |||
| test/tmp | |||
| test/version_tmp | |||
| tmp | |||
| # YARD artifacts | |||
| .yardoc | |||
| _yardoc | |||
| doc/ | |||
| @@ -0,0 +1,21 @@ | |||
| The MIT License (MIT) | |||
| Copyright (c) 2014 | |||
| Permission is hereby granted, free of charge, to any person obtaining a copy | |||
| of this software and associated documentation files (the "Software"), to deal | |||
| in the Software without restriction, including without limitation the rights | |||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
| copies of the Software, and to permit persons to whom the Software is | |||
| furnished to do so, subject to the following conditions: | |||
| The above copyright notice and this permission notice shall be included in all | |||
| copies or substantial portions of the Software. | |||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||
| SOFTWARE. | |||
| @@ -7,7 +7,7 @@ package main | |||
| import ( | |||
| "os" | |||
| "os/user" | |||
| // "os/user" | |||
| "runtime" | |||
| "github.com/codegangsta/cli" | |||
| @@ -20,21 +20,21 @@ 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.8.0315" | |||
| const APP_VER = "0.0.8.0316.1" | |||
| func init() { | |||
| base.AppVer = APP_VER | |||
| runtime.GOMAXPROCS(runtime.NumCPU()) | |||
| } | |||
| func checkRunUser() bool { | |||
| u, err := user.Current() | |||
| if err != nil { | |||
| // TODO: log | |||
| return false | |||
| } | |||
| return u.Username == base.Cfg.MustValue("", "RUN_USER") | |||
| } | |||
| // func checkRunUser() bool { | |||
| // u, err := user.Current() | |||
| // if err != nil { | |||
| // // TODO: log | |||
| // return false | |||
| // } | |||
| // return u.Username == base.Cfg.MustValue("", "RUN_USER") | |||
| // } | |||
| func main() { | |||
| /*if !checkRunUser() { | |||
| @@ -51,8 +51,6 @@ func main() { | |||
| CmdServ, | |||
| CmdUpdate, | |||
| } | |||
| app.Flags = append(app.Flags, []cli.Flag{ | |||
| cli.BoolFlag{"noterm", "disable color output"}, | |||
| }...) | |||
| app.Flags = append(app.Flags, []cli.Flag{}...) | |||
| app.Run(os.Args) | |||
| } | |||
| @@ -43,6 +43,7 @@ func (a Action) GetRepoName() string { | |||
| return a.RepoName | |||
| } | |||
| // CommitRepoAction records action for commit repository. | |||
| func CommitRepoAction(userId int64, userName string, | |||
| repoId int64, repoName string, msg string) error { | |||
| _, err := orm.InsertOne(&Action{ | |||
| @@ -57,8 +58,7 @@ func CommitRepoAction(userId int64, userName string, | |||
| return err | |||
| } | |||
| // NewRepoAction inserts action for create repository. | |||
| // NewRepoAction records action for create repository. | |||
| func NewRepoAction(user *User, repo *Repository) error { | |||
| _, err := orm.InsertOne(&Action{ | |||
| UserId: user.Id, | |||
| @@ -1,21 +1,32 @@ | |||
| // Copyright 2014 The Gogs Authors. All rights reserved. | |||
| // Use of this source code is governed by a MIT-style | |||
| // license that can be found in the LICENSE file. | |||
| package models | |||
| import ( | |||
| "bufio" | |||
| "errors" | |||
| "fmt" | |||
| "io" | |||
| "os" | |||
| "os/exec" | |||
| "path" | |||
| "path/filepath" | |||
| "strings" | |||
| "sync" | |||
| "time" | |||
| "github.com/Unknwon/com" | |||
| ) | |||
| var ( | |||
| sshOpLocker = sync.Mutex{} | |||
| //publicKeyRootPath string | |||
| sshPath string | |||
| appPath string | |||
| tmplPublicKey = "### autogenerated by gitgos, DO NOT EDIT\n" + | |||
| "command=\"%s serv key-%d\",no-port-forwarding," + | |||
| sshPath string | |||
| appPath string | |||
| // "### autogenerated by gitgos, DO NOT EDIT\n" | |||
| tmplPublicKey = "command=\"%s serv key-%d\",no-port-forwarding," + | |||
| "no-X11-forwarding,no-agent-forwarding,no-pty %s\n" | |||
| ) | |||
| @@ -47,29 +58,60 @@ func init() { | |||
| } | |||
| type PublicKey struct { | |||
| Id int64 | |||
| OwnerId int64 `xorm:"index"` | |||
| Name string `xorm:"unique not null"` | |||
| Content string `xorm:"text not null"` | |||
| Created time.Time `xorm:"created"` | |||
| Updated time.Time `xorm:"updated"` | |||
| Id int64 | |||
| OwnerId int64 `xorm:"index"` | |||
| Name string `xorm:"unique not null"` | |||
| Fingerprint string | |||
| Content string `xorm:"text not null"` | |||
| Created time.Time `xorm:"created"` | |||
| Updated time.Time `xorm:"updated"` | |||
| } | |||
| var ( | |||
| ErrKeyAlreadyExist = errors.New("Public key already exist") | |||
| ) | |||
| func GenAuthorizedKey(keyId int64, key string) string { | |||
| return fmt.Sprintf(tmplPublicKey, appPath, keyId, key) | |||
| } | |||
| func AddPublicKey(key *PublicKey) error { | |||
| _, err := orm.Insert(key) | |||
| func AddPublicKey(key *PublicKey) (err error) { | |||
| // Check if public key name has been used. | |||
| has, err := orm.Get(key) | |||
| if err != nil { | |||
| return err | |||
| } else if has { | |||
| return ErrKeyAlreadyExist | |||
| } | |||
| err = SaveAuthorizedKeyFile(key) | |||
| // Calculate fingerprint. | |||
| tmpPath := filepath.Join(os.TempDir(), fmt.Sprintf("%d", time.Now().Nanosecond()), | |||
| "id_rsa.pub") | |||
| os.MkdirAll(path.Dir(tmpPath), os.ModePerm) | |||
| f, err := os.Create(tmpPath) | |||
| if err != nil { | |||
| return | |||
| } | |||
| if _, err = f.WriteString(key.Content); err != nil { | |||
| return err | |||
| } | |||
| f.Close() | |||
| stdout, _, err := com.ExecCmd("ssh-keygen", "-l", "-f", tmpPath) | |||
| if err != nil { | |||
| _, err2 := orm.Delete(key) | |||
| if err2 != nil { | |||
| // TODO: log the error | |||
| return err | |||
| } else if len(stdout) < 2 { | |||
| return errors.New("Not enough output for calculating fingerprint") | |||
| } | |||
| key.Fingerprint = strings.Split(stdout, " ")[1] | |||
| // Save SSH key. | |||
| if _, err = orm.Insert(key); err != nil { | |||
| return err | |||
| } | |||
| if err = SaveAuthorizedKeyFile(key); err != nil { | |||
| if _, err2 := orm.Delete(key); err2 != nil { | |||
| return err2 | |||
| } | |||
| return err | |||
| } | |||
| @@ -77,9 +119,71 @@ func AddPublicKey(key *PublicKey) error { | |||
| return nil | |||
| } | |||
| func DeletePublicKey(key *PublicKey) error { | |||
| _, err := orm.Delete(key) | |||
| return err | |||
| // DeletePublicKey deletes SSH key information both in database and authorized_keys file. | |||
| func DeletePublicKey(key *PublicKey) (err error) { | |||
| has, err := orm.Id(key.Id).Get(key) | |||
| if err != nil { | |||
| return err | |||
| } else if !has { | |||
| return errors.New("Public key does not exist") | |||
| } | |||
| if _, err = orm.Delete(key); err != nil { | |||
| return err | |||
| } | |||
| sshOpLocker.Lock() | |||
| defer sshOpLocker.Unlock() | |||
| p := filepath.Join(sshPath, "authorized_keys") | |||
| tmpP := filepath.Join(sshPath, "authorized_keys.tmp") | |||
| fr, err := os.Open(p) | |||
| if err != nil { | |||
| return err | |||
| } | |||
| defer fr.Close() | |||
| fw, err := os.Create(tmpP) | |||
| if err != nil { | |||
| return err | |||
| } | |||
| defer fw.Close() | |||
| buf := bufio.NewReader(fr) | |||
| for { | |||
| line, errRead := buf.ReadString('\n') | |||
| line = strings.TrimSpace(line) | |||
| if errRead != nil { | |||
| if errRead != io.EOF { | |||
| return errRead | |||
| } | |||
| // Reached end of file, if nothing to read then break, | |||
| // otherwise handle the last line. | |||
| if len(line) == 0 { | |||
| break | |||
| } | |||
| } | |||
| // Found the line and copy rest of file. | |||
| if strings.Contains(line, fmt.Sprintf("key-%d", key.Id)) && strings.Contains(line, key.Content) { | |||
| continue | |||
| } | |||
| // Still finding the line, copy the line that currently read. | |||
| if _, err = fw.WriteString(line + "\n"); err != nil { | |||
| return err | |||
| } | |||
| if errRead == io.EOF { | |||
| break | |||
| } | |||
| } | |||
| if err = os.Remove(p); err != nil { | |||
| return err | |||
| } | |||
| return os.Rename(tmpP, p) | |||
| } | |||
| func ListPublicKey(userId int64) ([]PublicKey, error) { | |||
| @@ -89,11 +193,16 @@ func ListPublicKey(userId int64) ([]PublicKey, error) { | |||
| } | |||
| func SaveAuthorizedKeyFile(key *PublicKey) error { | |||
| sshOpLocker.Lock() | |||
| defer sshOpLocker.Unlock() | |||
| p := filepath.Join(sshPath, "authorized_keys") | |||
| f, err := os.OpenFile(p, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600) | |||
| if err != nil { | |||
| return err | |||
| } | |||
| defer f.Close() | |||
| //os.Chmod(p, 0600) | |||
| _, err = f.WriteString(GenAuthorizedKey(key.Id, key.Content)) | |||
| return err | |||
| @@ -264,7 +264,7 @@ func GetRepositoryById(id int64) (repo *Repository, err error) { | |||
| // GetRepositories returns the list of repositories of given user. | |||
| func GetRepositories(user *User) ([]Repository, error) { | |||
| repos := make([]Repository, 0, 10) | |||
| err := orm.Find(&repos, &Repository{OwnerId: user.Id}) | |||
| err := orm.Desc("updated").Find(&repos, &Repository{OwnerId: user.Id}) | |||
| return repos, err | |||
| } | |||
| @@ -142,6 +142,7 @@ func UpdateUser(user *User) (err error) { | |||
| // DeleteUser completely deletes everything of the user. | |||
| func DeleteUser(user *User) error { | |||
| // Check ownership of repository. | |||
| count, err := GetRepositoryCount(user) | |||
| if err != nil { | |||
| return errors.New("modesl.GetRepositories: " + err.Error()) | |||
| @@ -151,6 +152,22 @@ func DeleteUser(user *User) error { | |||
| // TODO: check issues, other repos' commits | |||
| // Delete all feeds. | |||
| if _, err = orm.Delete(&Action{UserId: user.Id}); err != nil { | |||
| return err | |||
| } | |||
| // Delete all SSH keys. | |||
| keys := make([]PublicKey, 0, 10) | |||
| if err = orm.Find(&keys, &PublicKey{OwnerId: user.Id}); err != nil { | |||
| return err | |||
| } | |||
| for _, key := range keys { | |||
| if err = DeletePublicKey(&key); err != nil { | |||
| return err | |||
| } | |||
| } | |||
| _, err = orm.Delete(user) | |||
| // TODO: delete and update follower information. | |||
| return err | |||
| @@ -158,8 +175,8 @@ func DeleteUser(user *User) error { | |||
| // EncodePasswd encodes password to safe format. | |||
| func (user *User) EncodePasswd() error { | |||
| newPasswd, err := scrypt.Key([]byte(user.Passwd), []byte(UserPasswdSalt), 16384, 8, 1, 64) | |||
| user.Passwd = fmt.Sprintf("%x", newPasswd) | |||
| var err error | |||
| user.Passwd, err = EncodePasswd(user.Passwd) | |||
| return err | |||
| } | |||
| @@ -167,6 +184,14 @@ func UserPath(userName string) string { | |||
| return filepath.Join(RepoRootPath, userName) | |||
| } | |||
| func EncodePasswd(rawPasswd string) (string, error) { | |||
| newPasswd, err := scrypt.Key([]byte(rawPasswd), []byte(UserPasswdSalt), 16384, 8, 1, 64) | |||
| if err != nil { | |||
| return "", err | |||
| } | |||
| return fmt.Sprintf("%x", newPasswd), nil | |||
| } | |||
| func GetUserByKeyId(keyId int64) (*User, error) { | |||
| user := new(User) | |||
| has, err := orm.Sql("select a.* from user as a, public_key as b where a.id = b.owner_id and b.id=?", keyId).Get(user) | |||
| @@ -5,6 +5,8 @@ | |||
| package middleware | |||
| import ( | |||
| "errors" | |||
| "github.com/codegangsta/martini" | |||
| "github.com/gogits/gogs/models" | |||
| @@ -31,9 +33,7 @@ func RepoAssignment(redirect bool) martini.Handler { | |||
| ctx.Render.Redirect("/") | |||
| return | |||
| } | |||
| //data["ErrorMsg"] = err | |||
| //log.Error("repo.Single: %v", err) | |||
| //r.HTML(200, "base/error", data) | |||
| ctx.Handle(200, "RepoAssignment", err) | |||
| return | |||
| } | |||
| } else { | |||
| @@ -45,9 +45,7 @@ func RepoAssignment(redirect bool) martini.Handler { | |||
| ctx.Render.Redirect("/") | |||
| return | |||
| } | |||
| //data["ErrorMsg"] = "invliad user account for single repository" | |||
| //log.Error("repo.Single: %v", err) | |||
| //r.HTML(200, "base/error", data) | |||
| ctx.Handle(200, "RepoAssignment", errors.New("invliad user account for single repository")) | |||
| return | |||
| } | |||
| @@ -60,9 +58,7 @@ func RepoAssignment(redirect bool) martini.Handler { | |||
| ctx.Render.Redirect("/") | |||
| return | |||
| } | |||
| //data["ErrorMsg"] = err | |||
| //log.Error("repo.Single: %v", err) | |||
| //r.HTML(200, "base/error", data) | |||
| ctx.Handle(200, "RepoAssignment", err) | |||
| return | |||
| } | |||
| @@ -302,16 +302,16 @@ html, body { | |||
| /* gogits user ssh keys */ | |||
| #gogs-ssh-keys .list-group-item { | |||
| line-height: 48px; | |||
| padding: 15px 0; | |||
| border-bottom: 1px solid #DDD; | |||
| } | |||
| #gogs-ssh-keys .list-group-item:after { | |||
| clear: both; | |||
| #gogs-ssh-keys .list-group-item .delete { | |||
| margin: -5px 50px 0; | |||
| } | |||
| #gogs-ssh-keys .list-group-item:hover a.delete { | |||
| display: block; | |||
| #gogs-ssh-keys .list-group-item:after { | |||
| clear: both; | |||
| } | |||
| #gogs-ssh-keys .name { | |||
| @@ -319,15 +319,6 @@ html, body { | |||
| font-weight: bold; | |||
| } | |||
| #gogs-ssh-keys .list-group-item a.delete { | |||
| float: right; | |||
| color: white; | |||
| cursor: pointer; | |||
| margin-top: 10px; | |||
| border-radius: 3px; | |||
| display: none; | |||
| } | |||
| #gogs-ssh-keys .print { | |||
| padding-left: 1em; | |||
| color: #888; | |||
| @@ -472,56 +463,6 @@ html, body { | |||
| 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; | |||
| } | |||
| .activity-list { | |||
| font-size: 14px; | |||
| } | |||
| @@ -583,6 +524,70 @@ html, body { | |||
| color: #999; | |||
| } | |||
| /* #gogs-source */ | |||
| #gogs-source .source-toolbar:after { | |||
| clear: both; | |||
| } | |||
| #gogs-source .source-toolbar .branch-switch { | |||
| display: inline-block; | |||
| } | |||
| #gogs-source .source-toolbar .breadcrumb { | |||
| margin: 0 .5em; | |||
| padding: 6px 15px; | |||
| font-size: 16px; | |||
| vertical-align: middle; | |||
| display: inline-block; | |||
| background-color: transparent; | |||
| } | |||
| #gogs-source .source-toolbar, | |||
| #gogs-source .info-box, | |||
| #gogs-source .file-content { | |||
| margin: 0 0 10px; | |||
| } | |||
| .info-box .info-head, | |||
| .info-box .info-content { | |||
| padding: 9px 20px; | |||
| } | |||
| .file-list { | |||
| background-color: #fafafa; | |||
| } | |||
| .file-list .icon { | |||
| font-size: 17px; | |||
| padding: 5px 0 4px 10px; | |||
| width: 40px; | |||
| } | |||
| .file-list .wrap { | |||
| display: inline-block; | |||
| overflow: hidden; | |||
| text-overflow: ellipsis; | |||
| vertical-align: top; | |||
| white-space: nowrap; | |||
| } | |||
| .file-list .name .wrap { | |||
| max-width: 180px; | |||
| } | |||
| .file-list .text .wrap { | |||
| max-width: 450px; | |||
| } | |||
| .file-list .date .wrap { | |||
| max-width: 100px; | |||
| padding: 0 20px 0 0; | |||
| } | |||
| .file-list .date { | |||
| text-align: right; | |||
| } | |||
| #wrapper { | |||
| min-height: 100%; | |||
| height: auto !important; | |||
| @@ -99,15 +99,16 @@ function initRegister() { | |||
| } | |||
| function initUserSetting(){ | |||
| $('#gogs-ssh-keys').on("click",".delete",function(){ | |||
| var $this = $(this); | |||
| Gogits.ajaxDelete("",{"id":$this.data("del")},function(json){ | |||
| if(json.ok){ | |||
| window.location.reload(); | |||
| }else{ | |||
| alert(json.err); | |||
| } | |||
| }); | |||
| return false; | |||
| $('#gogs-ssh-keys .delete').confirmation({ | |||
| singleton: true, | |||
| onConfirm: function(e, $this){ | |||
| Gogits.ajaxDelete("",{"id":$this.data("del")},function(json){ | |||
| if(json.ok){ | |||
| window.location.reload(); | |||
| }else{ | |||
| alert(json.err); | |||
| } | |||
| }); | |||
| } | |||
| }); | |||
| } | |||
| @@ -17,3 +17,7 @@ func Home(ctx *middleware.Context) { | |||
| ctx.Data["PageIsHome"] = true | |||
| ctx.Render.HTML(200, "home", ctx.Data) | |||
| } | |||
| func Help(ctx *middleware.Context) string { | |||
| return "This is help page" | |||
| } | |||
| @@ -1,3 +1,7 @@ | |||
| // Copyright 2014 The Gogs Authors. All rights reserved. | |||
| // Use of this source code is governed by a MIT-style | |||
| // license that can be found in the LICENSE file. | |||
| package repo | |||
| import ( | |||
| @@ -60,3 +64,15 @@ func Setting(ctx *middleware.Context) { | |||
| ctx.Data["IsRepoToolbarSetting"] = true | |||
| ctx.Render.HTML(200, "repo/setting", ctx.Data) | |||
| } | |||
| func Commits(ctx *middleware.Context) string { | |||
| return "This is commits page" | |||
| } | |||
| func Issues(ctx *middleware.Context) string { | |||
| return "This is issues page" | |||
| } | |||
| func Pulls(ctx *middleware.Context) string { | |||
| return "This is pulls page" | |||
| } | |||
| @@ -128,6 +128,10 @@ func SettingSSHKeys(ctx *middleware.Context, form auth.AddSSHKeyForm) { | |||
| } | |||
| if err := models.AddPublicKey(k); err != nil { | |||
| if err.Error() == models.ErrKeyAlreadyExist.Error() { | |||
| ctx.RenderWithErr("Public key name has been used", "user/publickey", &form) | |||
| return | |||
| } | |||
| ctx.Handle(200, "ssh.AddPublicKey", err) | |||
| return | |||
| } else { | |||
| @@ -157,13 +157,23 @@ func Delete(ctx *middleware.Context) { | |||
| return | |||
| } | |||
| if err := models.DeleteUser(ctx.User); err != nil { | |||
| rawPasswd := ctx.Query("password") | |||
| encodedPwd, _ := models.EncodePasswd(rawPasswd) | |||
| if len(encodedPwd) == 0 || encodedPwd != ctx.User.Passwd { | |||
| ctx.Data["HasError"] = true | |||
| switch err.Error() { | |||
| case models.ErrUserOwnRepos.Error(): | |||
| ctx.Data["ErrorMsg"] = "Your account still have ownership of repository, you have to delete or transfer them first." | |||
| default: | |||
| ctx.Handle(200, "user.Delete", err) | |||
| ctx.Data["ErrorMsg"] = "Your password error. Make sure you are owner of this account." | |||
| } else { | |||
| if err := models.DeleteUser(ctx.User); err != nil { | |||
| ctx.Data["HasError"] = true | |||
| switch err { | |||
| case models.ErrUserOwnRepos: | |||
| ctx.Data["ErrorMsg"] = "Your account still have ownership of repository, you have to delete or transfer them first." | |||
| default: | |||
| ctx.Handle(200, "user.Delete", err) | |||
| return | |||
| } | |||
| } else { | |||
| ctx.Render.Redirect("/") | |||
| return | |||
| } | |||
| } | |||
| @@ -189,3 +199,15 @@ func Feeds(ctx *middleware.Context, form auth.FeedsForm) { | |||
| } | |||
| ctx.Render.JSON(200, &feeds) | |||
| } | |||
| func Issues(ctx *middleware.Context) string { | |||
| return "This is issues page" | |||
| } | |||
| func Pulls(ctx *middleware.Context) string { | |||
| return "This is pulls page" | |||
| } | |||
| func Stars(ctx *middleware.Context) string { | |||
| return "This is stars page" | |||
| } | |||
| @@ -58,7 +58,7 @@ func runServ(*cli.Context) { | |||
| cmd := os.Getenv("SSH_ORIGINAL_COMMAND") | |||
| if cmd == "" { | |||
| println("Hi ", user.Name, "! You've successfully authenticated, but Gogs does not provide shell access.") | |||
| println("Hi", user.Name, "! You've successfully authenticated, but Gogs does not provide shell access.") | |||
| return | |||
| } | |||
| @@ -3,8 +3,7 @@ | |||
| <nav class="gogs-nav"> | |||
| <a id="gogs-nav-logo" class="gogs-nav-item{{if .PageIsHome}} active{{end}}" href="/"><img src="/img/favicon.png" alt="Gogs Logo" id="gogs-logo"></a> | |||
| <a class="gogs-nav-item{{if .PageIsUserDashboard}} active{{end}}" href="/">Dashboard</a> | |||
| <a class="gogs-nav-item" href="#">Explore</a> | |||
| <a class="gogs-nav-item" href="#">Help</a>{{if .IsSigned}} | |||
| <a class="gogs-nav-item" href="/help">Help</a>{{if .IsSigned}} | |||
| <a id="gogs-nav-out" class="gogs-nav-item navbar-right navbar-btn btn btn-danger" href="/user/logout/"><i class="fa fa-power-off fa-lg"></i></a> | |||
| <a id="gogs-nav-avatar" class="gogs-nav-item navbar-right" href="{{.SignedUser.HomeLink}}" data-toggle="tooltip" data-placement="bottom" title="{{.SignedUserName}}"> | |||
| <img src="{{.SignedUser.AvatarLink}}?s=28" alt="user-avatar" title="username"/> | |||
| @@ -59,7 +59,7 @@ | |||
| <div class="col-md-8 col-md-offset-2"> | |||
| <div class="checkbox"> | |||
| <label> | |||
| <input type="checkbox" name="initReadme"> | |||
| <input type="checkbox" name="initReadme" {{if .initReadme}}checked{{end}}> | |||
| <strong>Initialize this repository with a README</strong> | |||
| </label> | |||
| </div> | |||
| @@ -14,7 +14,7 @@ | |||
| </div> | |||
| <div id="gogs-repo-setting-container" class="col-md-9 tab-content"> | |||
| <div id="options" class="tab-pane"> | |||
| repo-options | |||
| <h4>Repository Options</h4> | |||
| </div> | |||
| <div id="delete" class="tab-pane"> | |||
| <h4>Delete Repository</h4> | |||
| @@ -4,14 +4,14 @@ | |||
| {{template "repo/toolbar" .}} | |||
| <div id="gogs-body" class="container"> | |||
| <div id="gogs-source"> | |||
| <div id="gogs-source-toolbar"> | |||
| <div class="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> | |||
| <li><a class="current" href="/{{.RepositoryLink}}/tree/master">master</a></li> | |||
| <li><a href="/{{.RepositoryLink}}/tree/develop">develop</a></li> | |||
| </ul> | |||
| </div> | |||
| {{$paths := .Paths}} | |||
| @@ -32,29 +32,60 @@ | |||
| {{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}} - {{FileSize .Size}}</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 class="panel panel-default info-box"> | |||
| <div class="panel-heading info-head"> | |||
| Merge branch 'release/1.1.1' | |||
| </div> | |||
| <div class="panel-body info-content"> | |||
| slene authored 4 days ago | |||
| </div> | |||
| <table class="panel-footer table file-list"> | |||
| <thead class="hidden"> | |||
| <tr> | |||
| <th class="icon"></th> | |||
| <th class="name">Filename</th> | |||
| <th class="text">Message</th> | |||
| <th class="date">Date modified</th> | |||
| </tr> | |||
| </thead> | |||
| <tbody> | |||
| {{range .Files}} | |||
| <tr {{if .IsDir}}class="is-dir"{{end}}> | |||
| <td class="icon"> | |||
| <i class="fa {{if .IsDir}}fa-folder{{else}}fa-file-text-o{{end}}"></i> | |||
| </td> | |||
| <td class="name"> | |||
| <span class="wrap"> | |||
| {{if .IsDir}} | |||
| <a href="/{{$username}}/{{$reponame}}/tree/{{$branchname}}/{{.Path}}">{{.Name}}</a> | |||
| {{else}} | |||
| <a href="/{{$username}}/{{$reponame}}/blob/{{$branchname}}/{{.Name}}">{{.Name}}</a> | |||
| {{end}} | |||
| </span> | |||
| </td> | |||
| <td class="text"> | |||
| <span class="wrap"> | |||
| {{.Message}} | |||
| </span> | |||
| </td> | |||
| <td class="date"> | |||
| <span class="wrap"> | |||
| {{TimeSince .Created}} | |||
| </span> | |||
| </td> | |||
| </tr> | |||
| {{end}} | |||
| </tbody> | |||
| </table> | |||
| </div> | |||
| <div class="panel panel-default file-content"> | |||
| <div class="panel-heading"> | |||
| README.md | |||
| </div> | |||
| <div class="panel-body markdown"> | |||
| httplib | |||
| </div> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| {{template "base/footer" .}} | |||
| @@ -13,22 +13,33 @@ | |||
| </ul> | |||
| </div> | |||
| <div id="gogs-user-setting-container" class="col-md-9"> | |||
| <form action="/user/delete" method="post" class="form-horizontal" id="gogs-user-delete"> | |||
| <h4>Delete Account</h4> | |||
| <p class="alert alert-danger">{{if not .HasError}}The operation will delete your account permanently. Sorry to see you go, but we know you'll back soon.{{else}}{{.ErrorMsg}}{{end}}</p> | |||
| <div class="form-group"> | |||
| <div class="col-md-3"> | |||
| <button type="submit" class="btn btn-danger btn-lg">Delete Account</button> | |||
| <h4>Delete Account</h4> | |||
| <p class="alert alert-danger">{{if not .HasError}}The operation will delete your account permanently. Sorry to see you go, but we know you'll back soon.{{else}}{{.ErrorMsg}}{{end}}</p> | |||
| <div class="form-group"> | |||
| <button type="submit" class="btn btn-danger btn-lg" href="#delete-account-modal" id="gogs-delete-account" data-toggle="modal">Delete Account</button> | |||
| </div> | |||
| </div> | |||
| <div class="modal fade" id="delete-account-modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> | |||
| <div class="modal-dialog"> | |||
| <form action="/user/delete" method="post" class="modal-content" id="gogs-user-delete"> | |||
| <div class="modal-header"> | |||
| <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> | |||
| <h4 class="modal-title" id="myModalLabel">Delete Account</h4> | |||
| </div> | |||
| <div class="modal-body"> | |||
| <div class="form-group"> | |||
| <label>Make sure your are owner of this account. Please enter your password.<strong class="text-danger">*</strong></label> | |||
| <input name="password" class="form-control" type="password" placeholder="Type your account password" required="required"> | |||
| </div> | |||
| </div> | |||
| <div class="modal-footer"> | |||
| <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button> | |||
| <button type="submit" class="btn btn-danger">Delete</button> | |||
| </div> | |||
| </div> | |||
| </form> | |||
| </form> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <script> | |||
| $(function(){ | |||
| $('#gogs-user-delete').on('submit',function(){ | |||
| return confirm("Are you sure ?"); | |||
| }) | |||
| }); | |||
| </script> | |||
| {{template "base/footer" .}} | |||
| @@ -5,7 +5,7 @@ | |||
| <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/password">Password</a></li> | |||
| <li class="list-group-item"><a href="/user/setting/notification">Notifications</a></li> | |||
| <li class="list-group-item list-group-item-success"><a href="/user/setting/ssh/">SSH Keys</a></li> | |||
| <li class="list-group-item"><a href="/user/setting/security">Security</a></li> | |||
| @@ -18,12 +18,14 @@ | |||
| <h4>SSH Keys</h4>{{if .AddSSHKeySuccess}} | |||
| <p class="alert alert-success">New SSH Key has been added !</p>{{else if .HasError}}<p class="alert alert-danger">{{.ErrorMsg}}</p>{{end}} | |||
| <ul id="gogs-ssh-keys-list" class="list-group"> | |||
| <li class="list-group-item"><span class="name">SSH Key's name</span></li>{{range .Keys}} | |||
| <li class="list-group-item"><span class="name">SSH Key's name</span></li> | |||
| {{range .Keys}} | |||
| <li class="list-group-item"> | |||
| <span class="name">{{.Name}}</span> | |||
| <span class="print">(print code)</span> | |||
| <a href="#" class="btn btn-link btn-danger right delete" rel="{{.Id}}" data-del="{{.Id}}">Delete</a> | |||
| </li>{{end}} | |||
| <span class="print">({{.Fingerprint}})</span> | |||
| <button href="#" class="btn btn-danger delete pull-right" rel="{{.Id}}" data-del="{{.Id}}">Delete</button> | |||
| </li> | |||
| {{end}} | |||
| <li class="list-group-item"> | |||
| <a class="btn btn-link btn-primary" href="#ssh-add-modal" id="gogs-ssh-add" data-toggle="modal">Add SSH Key</a> | |||
| </li> | |||
| @@ -1,13 +1,19 @@ | |||
| // Copyright 2014 The Gogs Authors. All rights reserved. | |||
| // Use of this source code is governed by a MIT-style | |||
| // license that can be found in the LICENSE file. | |||
| package main | |||
| import ( | |||
| "os" | |||
| "strconv" | |||
| "github.com/gogits/gogs/models" | |||
| "github.com/codegangsta/cli" | |||
| git "github.com/gogits/git" | |||
| "github.com/gogits/gogs/models" | |||
| "github.com/gogits/gogs/modules/log" | |||
| ) | |||
| var CmdUpdate = cli.Command{ | |||
| @@ -41,11 +47,18 @@ func runUpdate(*cli.Context) { | |||
| if err != nil { | |||
| return | |||
| } | |||
| sUserId, _ := strconv.Atoi(userId) | |||
| sRepoId, _ := strconv.Atoi(repoId) | |||
| err = models.CommitRepoAction(int64(sUserId), userName, | |||
| int64(sRepoId), repoName, lastCommit.Message()) | |||
| sUserId, err := strconv.Atoi(userId) | |||
| if err != nil { | |||
| //TODO: log | |||
| log.Error("runUpdate.Parse userId: %v", err) | |||
| return | |||
| } | |||
| sRepoId, err := strconv.Atoi(repoId) | |||
| if err != nil { | |||
| log.Error("runUpdate.Parse repoId: %v", err) | |||
| return | |||
| } | |||
| if err = models.CommitRepoAction(int64(sUserId), userName, | |||
| int64(sRepoId), repoName, lastCommit.Message()); err != nil { | |||
| log.Error("runUpdate.models.CommitRepoAction: %v", err) | |||
| } | |||
| } | |||
| @@ -50,6 +50,9 @@ func runWeb(*cli.Context) { | |||
| // Routers. | |||
| m.Get("/", middleware.SignInRequire(false), routers.Home) | |||
| m.Get("/issues", middleware.SignInRequire(true), user.Issues) | |||
| m.Get("/pulls", middleware.SignInRequire(true), user.Pulls) | |||
| m.Get("/stars", middleware.SignInRequire(true), user.Stars) | |||
| m.Any("/user/login", middleware.SignOutRequire(), binding.BindIgnErr(auth.LogInForm{}), user.SignIn) | |||
| m.Any("/user/logout", middleware.SignInRequire(true), user.SignOut) | |||
| m.Any("/user/sign_up", middleware.SignOutRequire(), binding.BindIgnErr(auth.RegisterForm{}), user.SignUp) | |||
| @@ -67,15 +70,18 @@ func runWeb(*cli.Context) { | |||
| m.Any("/repo/create", middleware.SignInRequire(true), binding.BindIgnErr(auth.CreateRepoForm{}), repo.Create) | |||
| m.Any("/repo/delete", middleware.SignInRequire(true), binding.Bind(auth.DeleteRepoForm{}), repo.Delete) | |||
| m.Get("/help", routers.Help) | |||
| m.Get("/:username/:reponame/settings", middleware.SignInRequire(false), middleware.RepoAssignment(true), repo.Setting) | |||
| m.Get("/:username/:reponame/commits", middleware.SignInRequire(false), middleware.RepoAssignment(true), repo.Commits) | |||
| m.Get("/:username/:reponame/issues", middleware.SignInRequire(false), middleware.RepoAssignment(true), repo.Issues) | |||
| m.Get("/:username/:reponame/pulls", middleware.SignInRequire(false), middleware.RepoAssignment(true), repo.Pulls) | |||
| m.Get("/:username/:reponame/tree/:branchname/**", | |||
| middleware.SignInRequire(false), middleware.RepoAssignment(true), repo.Single) | |||
| m.Get("/:username/:reponame/tree/:branchname", | |||
| middleware.SignInRequire(false), middleware.RepoAssignment(true), repo.Single) | |||
| m.Get("/:username/:reponame", middleware.SignInRequire(false), middleware.RepoAssignment(true), repo.Single) | |||
| //m.Get("/:username/:reponame", repo.Repo) | |||
| listenAddr := fmt.Sprintf("%s:%s", | |||
| base.Cfg.MustValue("server", "HTTP_ADDR"), | |||
| base.Cfg.MustValue("server", "HTTP_PORT", "3000")) | |||