diff options
author | Lunny Xiao <xiaolunwen@gmail.com> | 2014-03-28 10:51:42 +0800 |
---|---|---|
committer | Lunny Xiao <xiaolunwen@gmail.com> | 2014-03-28 10:51:42 +0800 |
commit | 89258e868b070ff3046de912e1d9a3009f923b62 (patch) | |
tree | 96d582c68daf862d0328e0734ede3da88e308fb8 | |
parent | 922a189f4061796b0d4afeeb45e508c36cc5e7fc (diff) | |
parent | 5344a0300383c4921e4a5810dff58c7686412f0c (diff) | |
download | gitea-89258e868b070ff3046de912e1d9a3009f923b62.tar.gz gitea-89258e868b070ff3046de912e1d9a3009f923b62.zip |
Merge branch 'master' of github.com:gogits/gogs
-rw-r--r-- | README.md | 10 | ||||
-rw-r--r-- | README_ZH.md | 12 | ||||
-rw-r--r-- | conf/app.ini | 1 | ||||
-rw-r--r-- | gogs.go | 2 | ||||
-rw-r--r-- | models/access.go | 4 | ||||
-rw-r--r-- | models/action.go | 31 | ||||
-rw-r--r-- | models/git.go | 41 | ||||
-rw-r--r-- | models/issue.go | 57 | ||||
-rw-r--r-- | models/models_test.go | 23 | ||||
-rw-r--r-- | models/repo.go | 99 | ||||
-rw-r--r-- | modules/avatar/avatar.go | 19 | ||||
-rw-r--r-- | modules/base/markdown.go | 8 | ||||
-rw-r--r-- | modules/base/tool.go | 17 | ||||
-rw-r--r-- | modules/middleware/repo.go | 4 | ||||
-rwxr-xr-x | public/css/gogs.css | 21 | ||||
-rw-r--r-- | public/js/app.js | 62 | ||||
-rw-r--r-- | routers/repo/commit.go | 61 | ||||
-rw-r--r-- | routers/repo/issue.go | 67 | ||||
-rw-r--r-- | routers/repo/repo.go | 72 | ||||
-rw-r--r-- | routers/user/user.go | 79 | ||||
-rw-r--r-- | templates/install.tmpl | 195 | ||||
-rw-r--r-- | templates/issue/list.tmpl | 11 | ||||
-rw-r--r-- | templates/issue/user.tmpl | 52 | ||||
-rw-r--r-- | templates/issue/view.tmpl | 24 | ||||
-rw-r--r-- | templates/repo/commits.tmpl | 4 | ||||
-rw-r--r-- | templates/repo/diff.tmpl | 13 | ||||
-rw-r--r-- | templates/repo/single.tmpl | 4 | ||||
-rw-r--r-- | templates/repo/single_file.tmpl | 8 | ||||
-rw-r--r-- | templates/repo/toolbar.tmpl | 2 | ||||
-rw-r--r-- | web.go | 8 |
30 files changed, 732 insertions, 279 deletions
@@ -5,7 +5,7 @@ Gogs(Go Git Service) is a Self Hosted Git Service in the Go Programming Language ![Demo](http://gowalker.org/public/gogs_demo.gif) -##### Current version: 0.1.8 Alpha +##### Current version: 0.1.9 Alpha #### Other language version @@ -19,7 +19,7 @@ More importantly, Gogs only needs one binary to setup your own project hosting o ## Overview -- Please see [Wiki](https://github.com/gogits/gogs/wiki) for project design, develop specification, change log and road map. +- Please see [Wiki](https://github.com/gogits/gogs/wiki) for project design, known issues, change log and road map. - See [Trello Board](https://trello.com/b/uxAoeLUl/gogs-go-git-service) to follow the develop team. - Try it before anything? Do it [online](http://try.gogits.org/Unknown/gogs) or go down to **Installation -> Install from binary** section! - Having troubles? Get help from [Troubleshooting](https://github.com/gogits/gogs/wiki/Troubleshooting). @@ -27,7 +27,7 @@ More importantly, Gogs only needs one binary to setup your own project hosting o ## Features - Activity timeline -- SSH/HTTPS protocol support. +- SSH/HTTPS(Clone only) protocol support. - Register/delete account. - Create/delete/watch public repository. - User profile page. @@ -58,3 +58,7 @@ There are two ways to install Gogs: ## Contributors This project was launched by [Unknown](https://github.com/Unknwon) and [lunny](https://github.com/lunny); [fuxiaohei](https://github.com/fuxiaohei) and [slene](https://github.com/slene) joined the team soon after. See [contributors page](https://github.com/gogits/gogs/graphs/contributors) for full list of contributors. + +## License + +Gogs is under the MIT License. See the [LICENSE](https://github.com/gogits/gogs/blob/master/LICENSE) file for the full license text. diff --git a/README_ZH.md b/README_ZH.md index 9c9f4b3980..8e187c7364 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -5,7 +5,7 @@ Gogs(Go Git Service) 是一个由 Go 语言编写的自助 Git 托管服务。 ![Demo](http://gowalker.org/public/gogs_demo.gif) -##### 当前版本:0.1.8 Alpha +##### 当前版本:0.1.9 Alpha ## 开发目的 @@ -15,7 +15,7 @@ Gogs 完全使用 Go 语言来实现对 Git 数据的操作,实现 **零** 依 ## 项目概览 -- 有关项目设计、开发说明、变更日志和路线图,请通过 [Wiki](https://github.com/gogits/gogs/wiki) 查看。 +- 有关项目设计、已知问题、变更日志和路线图,请通过 [Wiki](https://github.com/gogits/gogs/wiki) 查看。 - 您可以到 [Trello Board](https://trello.com/b/uxAoeLUl/gogs-go-git-service) 跟随开发团队的脚步。 - 想要先睹为快?通过 [在线体验](http://try.gogits.org/Unknown/gogs) 或查看 **安装部署 -> 二进制安装** 小节。 - 使用过程中遇到问题?尝试从 [故障排查](https://github.com/gogits/gogs/wiki/Troubleshooting) 页面获取帮助。 @@ -23,7 +23,7 @@ Gogs 完全使用 Go 语言来实现对 Git 数据的操作,实现 **零** 依 ## 功能特性 - 活动时间线 -- SSH/HTTPS 协议支持 +- SSH/HTTPS(仅限 Clone) 协议支持 - 注册/删除用户 - 创建/删除/关注公开仓库 - 用户个人信息页面 @@ -53,4 +53,8 @@ Gogs 完全使用 Go 语言来实现对 Git 数据的操作,实现 **零** 依 ## 贡献成员 -本项目最初由 [Unknown](https://github.com/Unknwon) 和 [lunny](https://github.com/lunny) 发起,随后 [fuxiaohei](https://github.com/fuxiaohei) 与 [slene](https://github.com/slene) 加入到开发团队。您可以通过查看 [贡献者页面](https://github.com/gogits/gogs/graphs/contributors) 获取完整的贡献者列表。
\ No newline at end of file +本项目最初由 [Unknown](https://github.com/Unknwon) 和 [lunny](https://github.com/lunny) 发起,随后 [fuxiaohei](https://github.com/fuxiaohei) 与 [slene](https://github.com/slene) 加入到开发团队。您可以通过查看 [贡献者页面](https://github.com/gogits/gogs/graphs/contributors) 获取完整的贡献者列表。 + +## 授权许可 + +Gogs 采用 MIT 开源授权许可证,完整的授权说明已放置在 [LICENSE](https://github.com/gogits/gogs/blob/master/LICENSE) 文件中。
\ No newline at end of file diff --git a/conf/app.ini b/conf/app.ini index 1a96ebeab6..d988b4acbc 100644 --- a/conf/app.ini +++ b/conf/app.ini @@ -32,6 +32,7 @@ PATH = data/gogs.db [admin] [security] +INSTALL_LOCK = false ; Use HTTPS to clone repository, otherwise use HTTP. ENABLE_HTTPS_CLONE = false ; !!CHANGE THIS TO KEEP YOUR USER DATA SAFE!! @@ -19,7 +19,7 @@ import ( // Test that go1.2 tag above is included in builds. main.go refers to this definition. const go12tag = true -const APP_VER = "0.1.8.0326 Alpha" +const APP_VER = "0.1.9.0327 Alpha" func init() { base.AppVer = APP_VER diff --git a/models/access.go b/models/access.go index 36d9405f52..84cad17a3f 100644 --- a/models/access.go +++ b/models/access.go @@ -15,7 +15,7 @@ const ( AU_WRITABLE ) -// Access represents the accessibility of user and repository. +// Access represents the accessibility of user to repository. type Access struct { Id int64 UserName string `xorm:"unique(s)"` @@ -30,7 +30,7 @@ func AddAccess(access *Access) error { return err } -// HasAccess returns true if someone can read or write given repository. +// HasAccess returns true if someone can read or write to given repository. func HasAccess(userName, repoName string, mode int) (bool, error) { return orm.Get(&Access{ Id: 0, diff --git a/models/action.go b/models/action.go index edf1bf58f9..9d99df8546 100644 --- a/models/action.go +++ b/models/action.go @@ -23,7 +23,8 @@ const ( OP_PULL_REQUEST ) -// Action represents user operation type and information to the repository. +// Action represents user operation type and other information to repository., +// it implemented interface base.Actioner so that can be used in template render. type Action struct { Id int64 UserId int64 // Receiver user id. @@ -57,23 +58,24 @@ func (a Action) GetContent() string { return a.Content } -// CommitRepoAction records action for commit repository. +// CommitRepoAction adds new action for committing repository. func CommitRepoAction(userId int64, userName string, - repoId int64, repoName string, refName string, commits *base.PushCommits) error { + repoId int64, repoName string, refName string, commit *base.PushCommits) error { log.Trace("action.CommitRepoAction(start): %d/%s", userId, repoName) - bs, err := json.Marshal(commits) + bs, err := json.Marshal(commit) if err != nil { log.Error("action.CommitRepoAction(json): %d/%s", userId, repoName) return err } - if err = NotifyWatchers(userId, repoId, OP_COMMIT_REPO, userName, repoName, refName, string(bs)); err != nil { + if err = NotifyWatchers(&Action{ActUserId: userId, ActUserName: userName, OpType: OP_COMMIT_REPO, + Content: string(bs), RepoId: repoId, RepoName: repoName, RefName: refName}); err != nil { log.Error("action.CommitRepoAction(notify watchers): %d/%s", userId, repoName) return err } - // Update repository last update time. + // Change repository bare status and update last updated time. repo, err := GetRepositoryByName(userId, repoName) if err != nil { log.Error("action.CommitRepoAction(GetRepositoryByName): %d/%s", userId, repoName) @@ -89,16 +91,13 @@ func CommitRepoAction(userId int64, userName string, return nil } -// NewRepoAction records action for create repository. -func NewRepoAction(user *User, repo *Repository) error { - _, err := orm.InsertOne(&Action{ - UserId: user.Id, - ActUserId: user.Id, - ActUserName: user.Name, - OpType: OP_CREATE_REPO, - RepoId: repo.Id, - RepoName: repo.Name, - }) +// NewRepoAction adds new action for creating repository. +func NewRepoAction(user *User, repo *Repository) (err error) { + if err = NotifyWatchers(&Action{ActUserId: user.Id, ActUserName: user.Name, OpType: OP_CREATE_REPO, + RepoId: repo.Id, RepoName: repo.Name}); err != nil { + log.Error("action.NewRepoAction(notify watchers): %d/%s", user.Id, repo.Name) + return err + } log.Trace("action.NewRepoAction: %s/%s", user.LowerName, repo.LowerName) return err diff --git a/models/git.go b/models/git.go index 8e1bc4e32c..e2ee52083d 100644 --- a/models/git.go +++ b/models/git.go @@ -38,8 +38,8 @@ func (file *RepoFile) LookupBlob() (*git.Blob, error) { } // GetBranches returns all branches of given repository. -func GetBranches(userName, reposName string) ([]string, error) { - repo, err := git.OpenRepository(RepoPath(userName, reposName)) +func GetBranches(userName, repoName string) ([]string, error) { + repo, err := git.OpenRepository(RepoPath(userName, repoName)) if err != nil { return nil, err } @@ -56,8 +56,16 @@ func GetBranches(userName, reposName string) ([]string, error) { return brs, nil } -func GetTargetFile(userName, reposName, branchName, commitId, rpath string) (*RepoFile, error) { - repo, err := git.OpenRepository(RepoPath(userName, reposName)) +func IsBranchExist(userName, repoName, branchName string) bool { + repo, err := git.OpenRepository(RepoPath(userName, repoName)) + if err != nil { + return false + } + return repo.IsBranchExist(branchName) +} + +func GetTargetFile(userName, repoName, branchName, commitId, rpath string) (*RepoFile, error) { + repo, err := git.OpenRepository(RepoPath(userName, repoName)) if err != nil { return nil, err } @@ -102,8 +110,8 @@ func GetTargetFile(userName, reposName, branchName, commitId, rpath string) (*Re } // GetReposFiles returns a list of file object in given directory of repository. -func GetReposFiles(userName, reposName, branchName, commitId, rpath string) ([]*RepoFile, error) { - repo, err := git.OpenRepository(RepoPath(userName, reposName)) +func GetReposFiles(userName, repoName, branchName, commitId, rpath string) ([]*RepoFile, error) { + repo, err := git.OpenRepository(RepoPath(userName, repoName)) if err != nil { return nil, err } @@ -217,13 +225,26 @@ func GetCommit(userName, repoName, branchname, commitid string) (*git.Commit, er return repo.GetCommit(branchname, commitid) } -// GetCommits returns all commits of given branch of repository. -func GetCommits(userName, reposName, branchname string) (*list.List, error) { - repo, err := git.OpenRepository(RepoPath(userName, reposName)) +// GetCommitsByBranch returns all commits of given branch of repository. +func GetCommitsByBranch(userName, repoName, branchName string) (*list.List, error) { + repo, err := git.OpenRepository(RepoPath(userName, repoName)) + if err != nil { + return nil, err + } + r, err := repo.LookupReference(fmt.Sprintf("refs/heads/%s", branchName)) + if err != nil { + return nil, err + } + return r.AllCommits() +} + +// GetCommitsByCommitId returns all commits of given commitId of repository. +func GetCommitsByCommitId(userName, repoName, commitId string) (*list.List, error) { + repo, err := git.OpenRepository(RepoPath(userName, repoName)) if err != nil { return nil, err } - r, err := repo.LookupReference(fmt.Sprintf("refs/heads/%s", branchname)) + r, err := repo.LookupReference(commitId) if err != nil { return nil, err } diff --git a/models/issue.go b/models/issue.go index 97e51a0c58..39558ae225 100644 --- a/models/issue.go +++ b/models/issue.go @@ -21,7 +21,8 @@ type Issue struct { Id int64 Index int64 // Index in one repository. Name string - RepoId int64 `xorm:"index"` + RepoId int64 `xorm:"index"` + Repo *Repository `xorm:"-"` PosterId int64 Poster *User `xorm:"-"` MilestoneId int64 @@ -37,17 +38,16 @@ type Issue struct { } // CreateIssue creates new issue for repository. -func CreateIssue(userId, repoId, milestoneId, assigneeId int64, name, labels, content string, isPull bool) (*Issue, error) { - count, err := GetIssueCount(repoId) - if err != nil { - return nil, err - } - +func CreateIssue(userId, repoId, milestoneId, assigneeId int64, issueCount int, name, labels, content string, isPull bool) (issue *Issue, err error) { // TODO: find out mentions mentions := "" - issue := &Issue{ - Index: count + 1, + sess := orm.NewSession() + defer sess.Close() + sess.Begin() + + issue = &Issue{ + Index: int64(issueCount) + 1, Name: name, RepoId: repoId, PosterId: userId, @@ -58,13 +58,23 @@ func CreateIssue(userId, repoId, milestoneId, assigneeId int64, name, labels, co Mentions: mentions, Content: content, } - _, err = orm.Insert(issue) - return issue, err -} + if _, err = sess.Insert(issue); err != nil { + sess.Rollback() + return nil, err + } -// GetIssueCount returns count of issues in the repository. -func GetIssueCount(repoId int64) (int64, error) { - return orm.Count(&Issue{RepoId: repoId}) + rawSql := "UPDATE `repository` SET num_issues = num_issues + 1 WHERE id = ?" + if _, err = sess.Exec(rawSql, repoId); err != nil { + sess.Rollback() + return nil, err + } + + if err = sess.Commit(); err != nil { + sess.Rollback() + return nil, err + } + + return issue, nil } // GetIssueById returns issue object by given id. @@ -127,18 +137,18 @@ func GetIssues(userId, repoId, posterId, milestoneId int64, page int, isClosed, return issues, err } +// GetUserIssueCount returns the number of issues that were created by given user in repository. +func GetUserIssueCount(userId, repoId int64) int64 { + count, _ := orm.Where("poster_id=?", userId).And("repo_id=?", repoId).Count(new(Issue)) + return count +} + // UpdateIssue updates information of issue. func UpdateIssue(issue *Issue) error { - _, err := orm.Update(issue, &Issue{RepoId: issue.RepoId, Index: issue.Index}) + _, err := orm.Id(issue.Id).AllCols().Update(issue) return err } -func CloseIssue() { -} - -func ReopenIssue() { -} - // Label represents a list of labels of repository for issues. type Label struct { Id int64 @@ -178,8 +188,7 @@ func CreateComment(userId, issueId, commitId, line int64, content string) error sess.Begin() if _, err := orm.Insert(&Comment{PosterId: userId, IssueId: issueId, - CommitId: commitId, Line: line, Content: content, - }); err != nil { + CommitId: commitId, Line: line, Content: content}); err != nil { sess.Rollback() return err } diff --git a/models/models_test.go b/models/models_test.go index d0f734d678..b808f41d21 100644 --- a/models/models_test.go +++ b/models/models_test.go @@ -10,12 +10,12 @@ import ( "github.com/lunny/xorm" _ "github.com/mattn/go-sqlite3" + . "github.com/smartystreets/goconvey/convey" + + "github.com/gogits/gogs/modules/base" ) func init() { - LoadModelsConfig() - NewEngine() - var err error orm, err = xorm.NewEngine("sqlite3", "./test.db") if err != nil { @@ -25,26 +25,31 @@ func init() { orm.ShowSQL = true orm.ShowDebug = true - err = orm.Sync(&User{}, &Repo{}) + err = orm.Sync(&User{}, &Repository{}) if err != nil { fmt.Println(err) } - root = "test" + base.RepoRootPath = "test" } func TestCreateRepository(t *testing.T) { - user := User{Id: 1, Type: Individual} - _, err := CreateRepository(&user, "test") + user := User{Id: 1, Name: "foobar", Type: UT_INDIVIDUAL} + _, err := CreateRepository(&user, "test", "", "", "test repo desc", false, false) if err != nil { t.Error(err) } } func TestDeleteRepository(t *testing.T) { - user := User{Id: 1, Type: Individual} - err := DeleteRepository(&user, "test") + err := DeleteRepository(1, 1, "foobar") if err != nil { t.Error(err) } } + +func TestCommitRepoAction(t *testing.T) { + Convey("Create a commit repository action", t, func() { + + }) +} diff --git a/models/repo.go b/models/repo.go index 0ef049cc6e..726d435d33 100644 --- a/models/repo.go +++ b/models/repo.go @@ -72,20 +72,23 @@ func NewRepoContext() { // Repository represents a git repository. type Repository struct { - Id int64 - OwnerId int64 `xorm:"unique(s)"` - ForkId int64 - LowerName string `xorm:"unique(s) index not null"` - Name string `xorm:"index not null"` - Description string - Website string - NumWatches int - NumStars int - NumForks int - IsPrivate bool - IsBare bool - Created time.Time `xorm:"created"` - Updated time.Time `xorm:"updated"` + Id int64 + OwnerId int64 `xorm:"unique(s)"` + ForkId int64 + LowerName string `xorm:"unique(s) index not null"` + Name string `xorm:"index not null"` + Description string + Website string + NumWatches int + NumStars int + NumForks int + NumIssues int + NumClosedIssues int + NumOpenIssues int `xorm:"-"` + IsPrivate bool + IsBare bool + Created time.Time `xorm:"created"` + Updated time.Time `xorm:"updated"` } // IsRepositoryExist returns true if the repository with given name under user has already existed. @@ -94,17 +97,16 @@ func IsRepositoryExist(user *User, repoName string) (bool, error) { has, err := orm.Where("lower_name = ?", strings.ToLower(repoName)).Get(&repo) if err != nil { return has, err + } else if !has { + return false, nil } - s, err := os.Stat(RepoPath(user.Name, repoName)) - if err != nil { - return false, nil // Error simply means does not exist, but we don't want to show up. - } - return s.IsDir(), nil + + return com.IsDir(RepoPath(user.Name, repoName)), nil } var ( // Define as all lower case!! - illegalPatterns = []string{"[.][Gg][Ii][Tt]", "raw", "user", "help", "stars", "issues", "pulls", "commits", "admin", "repo", "template", "admin"} + illegalPatterns = []string{"[.][Gg][Ii][Tt]", "raw", "user", "help", "stars", "issues", "pulls", "commits", "repo", "template", "admin"} ) // IsLegalName returns false if name contains illegal characters. @@ -222,16 +224,24 @@ func initRepoCommit(tmpPath string, sig *git.Signature) (err error) { if _, stderr, err = com.ExecCmdDir(tmpPath, "git", "add", "--all"); err != nil { return err } - log.Trace("stderr(1): %s", stderr) + if len(stderr) > 0 { + log.Trace("stderr(1): %s", stderr) + } + if _, stderr, err = com.ExecCmdDir(tmpPath, "git", "commit", fmt.Sprintf("--author='%s <%s>'", sig.Name, sig.Email), "-m", "Init commit"); err != nil { return err } - log.Trace("stderr(2): %s", stderr) + if len(stderr) > 0 { + log.Trace("stderr(2): %s", stderr) + } + if _, stderr, err = com.ExecCmdDir(tmpPath, "git", "push", "origin", "master"); err != nil { return err } - log.Trace("stderr(3): %s", stderr) + if len(stderr) > 0 { + log.Trace("stderr(3): %s", stderr) + } return nil } @@ -241,10 +251,9 @@ func createHookUpdate(hookPath, content string) error { return err } defer pu.Close() - if _, err = pu.WriteString(content); err != nil { - return err - } - return nil + + _, err = pu.WriteString(content) + return err } // InitRepository initializes README and .gitignore if needed. @@ -320,10 +329,7 @@ func initRepository(f string, user *User, repo *Repository, initReadme bool, rep } // Apply changes and commit. - if err := initRepoCommit(tmpDir, user.NewGitSig()); err != nil { - return err - } - return nil + return initRepoCommit(tmpDir, user.NewGitSig()) } // UserRepo reporesents a repository with user name. @@ -430,7 +436,8 @@ func GetRepositoryByName(userId int64, repoName string) (*Repository, error) { } // GetRepositoryById returns the repository by given id if exists. -func GetRepositoryById(id int64) (repo *Repository, err error) { +func GetRepositoryById(id int64) (*Repository, error) { + repo := &Repository{} has, err := orm.Id(id).Get(repo) if err != nil { return nil, err @@ -485,30 +492,26 @@ func GetWatches(repoId int64) ([]Watch, error) { } // NotifyWatchers creates batch of actions for every watcher. -func NotifyWatchers(userId, repoId int64, opType int, userName, repoName, refName, content string) error { +func NotifyWatchers(act *Action) error { // Add feeds for user self and all watchers. - watches, err := GetWatches(repoId) + watches, err := GetWatches(act.RepoId) if err != nil { return errors.New("repo.NotifyWatchers(get watches): " + err.Error()) } - watches = append(watches, Watch{UserId: userId}) + + // Add feed for actioner. + act.UserId = act.ActUserId + if _, err = orm.InsertOne(act); err != nil { + return errors.New("repo.NotifyWatchers(create action): " + err.Error()) + } for i := range watches { - if userId == watches[i].UserId && i > 0 { - continue // Do not add twice in case author watches his/her repository. + if act.ActUserId == watches[i].UserId { + continue } - _, err = orm.InsertOne(&Action{ - UserId: watches[i].UserId, - ActUserId: userId, - ActUserName: userName, - OpType: opType, - Content: content, - RepoId: repoId, - RepoName: repoName, - RefName: refName, - }) - if err != nil { + act.UserId = watches[i].UserId + if _, err = orm.InsertOne(act); err != nil { return errors.New("repo.NotifyWatchers(create action): " + err.Error()) } } diff --git a/modules/avatar/avatar.go b/modules/avatar/avatar.go index 06e2c1385b..edeb256ffe 100644 --- a/modules/avatar/avatar.go +++ b/modules/avatar/avatar.go @@ -47,6 +47,7 @@ func HashEmail(email string) string { return hex.EncodeToString(h.Sum(nil)) } +// Avatar represents the avatar object. type Avatar struct { Hash string AlterImage string // image path @@ -96,8 +97,8 @@ func (this *Avatar) Encode(wr io.Writer, size int) (err error) { return } defer fd.Close() - img, err = jpeg.Decode(fd) - if err != nil { + + if img, err = jpeg.Decode(fd); err != nil { fd.Seek(0, os.SEEK_SET) img, err = png.Decode(fd) } @@ -110,8 +111,8 @@ func (this *Avatar) Encode(wr io.Writer, size int) (err error) { } imgPath = this.AlterImage } - img, err = decodeImageFile(imgPath) - if err != nil { + + if img, err = decodeImageFile(imgPath); err != nil { return } m := resize.Resize(uint(size), 0, img, resize.Lanczos3) @@ -124,8 +125,7 @@ func (this *Avatar) Update() { this.imagePath) } -func (this *Avatar) UpdateTimeout(timeout time.Duration) error { - var err error +func (this *Avatar) UpdateTimeout(timeout time.Duration) (err error) { select { case <-time.After(timeout): err = fmt.Errorf("get gravatar image %s timeout", this.Hash) @@ -140,8 +140,7 @@ type service struct { altImage string } -func (this *service) mustInt(r *http.Request, defaultValue int, keys ...string) int { - var v int +func (this *service) mustInt(r *http.Request, defaultValue int, keys ...string) (v int) { for _, k := range keys { if _, err := fmt.Sscanf(r.FormValue(k), "%d", &v); err == nil { defaultValue = v @@ -176,8 +175,8 @@ func (this *service) ServeHTTP(w http.ResponseWriter, r *http.Request) { w.Header().Set("ETag", etag) } w.Header().Set("Content-Type", "image/jpeg") - err := avatar.Encode(w, size) - if err != nil { + + if err := avatar.Encode(w, size); err != nil { log.Warn("avatar encode error: %v", err) w.WriteHeader(500) } diff --git a/modules/base/markdown.go b/modules/base/markdown.go index c722f04b2e..962e1ae1e9 100644 --- a/modules/base/markdown.go +++ b/modules/base/markdown.go @@ -51,6 +51,14 @@ func IsTextFile(data []byte) (string, bool) { return contentType, false } +func IsImageFile(data []byte) (string, bool) { + contentType := http.DetectContentType(data) + if strings.Index(contentType, "image/") != -1 { + return contentType, true + } + return contentType, false +} + func IsReadmeFile(name string) bool { name = strings.ToLower(name) if len(name) < 6 { diff --git a/modules/base/tool.go b/modules/base/tool.go index 6f4fbe8366..9ddb90f721 100644 --- a/modules/base/tool.go +++ b/modules/base/tool.go @@ -412,6 +412,11 @@ func (f StrTo) Int() (int, error) { return int(v), err } +func (f StrTo) Int64() (int64, error) { + v, err := strconv.ParseInt(f.String(), 10, 64) + return int64(v), err +} + func (f StrTo) String() string { if f.Exist() { return string(f) @@ -541,16 +546,10 @@ func ActionDesc(act Actioner, avatarLink string) string { } func DiffTypeToStr(diffType int) string { - switch diffType { - case 1: - return "add" - case 2: - return "modify" - case 3: - return "del" - default: - return "unknown" + diffTypes := map[int]string{ + 1: "add", 2: "modify", 3: "del", } + return diffTypes[diffType] } func DiffLineTypeToStr(diffType int) string { diff --git a/modules/middleware/repo.go b/modules/middleware/repo.go index bc90c05cc7..cb4a8632a2 100644 --- a/modules/middleware/repo.go +++ b/modules/middleware/repo.go @@ -56,7 +56,9 @@ func RepoAssignment(redirect bool) martini.Handler { // get repository repo, err := models.GetRepositoryByName(user.Id, params["reponame"]) if err != nil { - if redirect { + if err == models.ErrRepoNotExist { + ctx.Handle(404, "RepoAssignment", err) + } else if redirect { ctx.Redirect("/") return } diff --git a/public/css/gogs.css b/public/css/gogs.css index 436067ed8c..d4976460e6 100755 --- a/public/css/gogs.css +++ b/public/css/gogs.css @@ -854,6 +854,10 @@ html, body { min-width: 180px; } +.commit-list .sha a { + font-family: Consolas, Menlo, Monaco, "Lucida Console", monospace; +} + .guide-box pre, .guide-box .input-group { margin-top: 20px; margin-bottom: 30px; @@ -1119,7 +1123,7 @@ html, body { #issue .issue-head .info { width: 99%; margin-top: 10px; - padding-left: 64px; + padding-left: 74px; margin-bottom: 16px; padding-bottom: 20px; border-bottom: 1px solid #CCC; @@ -1169,6 +1173,21 @@ html, body { border-color: #CCC; } +#issue .issue-head .info .btn { + margin-top: -8px; + margin-left: 8px; +} + +#issue .issue-action { + padding-left: 8px; + color: #888; + width: 24px; +} + +#issue-edit-title { + width: 60%; +} + /* wrapper and footer */ #wrapper { diff --git a/public/js/app.js b/public/js/app.js index 8b0e5cd62c..9299a6b7aa 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -50,6 +50,14 @@ var Gogits = { } } }); + $.fn.extend({ + toggleHide: function () { + $(this).addClass("hidden"); + }, + toggleShow: function () { + $(this).removeClass("hidden"); + } + }) }(jQuery)); (function ($) { @@ -352,6 +360,54 @@ function initRepository() { }()); } +function initInstall() { + // database type change + $('#install-database').on("change", function () { + var val = $(this).val(); + if (val != "sqlite") { + $('.server-sql').show(); + $('.sqlite-setting').addClass("hide"); + if (val == "pgsql") { + $('.pgsql-setting').removeClass("hide"); + } else { + $('.pgsql-setting').addClass("hide"); + } + } else { + $('.server-sql').hide(); + $('.sqlite-setting').removeClass("hide"); + } + }); +} + +function initIssue() { + // close button + (function () { + var $closeBtn = $('#issue-close-btn'); + var $openBtn = $('#issue-open-btn'); + $('#issue-reply-content').on("keyup", function () { + if ($(this).val().length) { + $closeBtn.text($closeBtn.data("text")); + $openBtn.text($openBtn.data("text")); + } else { + $closeBtn.text($closeBtn.data("origin")); + $openBtn.text($openBtn.data("origin")); + } + }); + }()); + + // issue edit mode + (function () { + $("#issue-edit-btn").on("click", function () { + $('#issue h1.title,#issue .issue-main > .issue-content .content,#issue-edit-btn').toggleHide(); + $('#issue-edit-title,#issue-edit-content,.issue-edit-cancel,.issue-edit-save').toggleShow(); + }); + $('.issue-edit-cancel').on("click", function () { + $('#issue h1.title,#issue .issue-main > .issue-content .content,#issue-edit-btn').toggleShow(); + $('#issue-edit-title,#issue-edit-content,.issue-edit-cancel,.issue-edit-save').toggleHide(); + }) + }()); +} + (function ($) { $(function () { initCore(); @@ -365,5 +421,11 @@ function initRepository() { if ($('.repo-nav').length) { initRepository(); } + if ($('#install-card').length) { + initInstall(); + } + if ($('#issue').length) { + initIssue(); + } }); })(jQuery); diff --git a/routers/repo/commit.go b/routers/repo/commit.go index 3d00f8d747..afc1ffda29 100644 --- a/routers/repo/commit.go +++ b/routers/repo/commit.go @@ -5,13 +5,22 @@ package repo import ( + "container/list" + "path" + "github.com/codegangsta/martini" + "github.com/gogits/gogs/models" + "github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/middleware" ) func Commits(ctx *middleware.Context, params martini.Params) { - brs, err := models.GetBranches(params["username"], params["reponame"]) + userName := params["username"] + repoName := params["reponame"] + branchName := params["branchname"] + + brs, err := models.GetBranches(userName, repoName) if err != nil { ctx.Handle(200, "repo.Commits", err) return @@ -20,38 +29,70 @@ func Commits(ctx *middleware.Context, params martini.Params) { return } - ctx.Data["IsRepoToolbarCommits"] = true - commits, err := models.GetCommits(params["username"], - params["reponame"], params["branchname"]) + var commits *list.List + if models.IsBranchExist(userName, repoName, branchName) { + commits, err = models.GetCommitsByBranch(userName, repoName, branchName) + } else { + commits, err = models.GetCommitsByCommitId(userName, repoName, branchName) + } + if err != nil { - ctx.Handle(404, "repo.Commits", nil) + ctx.Handle(404, "repo.Commits", err) return } - ctx.Data["Username"] = params["username"] - ctx.Data["Reponame"] = params["reponame"] + + ctx.Data["Username"] = userName + ctx.Data["Reponame"] = repoName ctx.Data["CommitCount"] = commits.Len() ctx.Data["Commits"] = commits + ctx.Data["IsRepoToolbarCommits"] = true ctx.HTML(200, "repo/commits") } func Diff(ctx *middleware.Context, params martini.Params) { - commit, err := models.GetCommit(params["username"], params["reponame"], params["branchname"], params["commitid"]) + userName := params["username"] + repoName := params["reponame"] + branchName := params["branchname"] + commitId := params["commitid"] + + commit, err := models.GetCommit(userName, repoName, branchName, commitId) if err != nil { ctx.Handle(404, "repo.Diff", err) return } - diff, err := models.GetDiff(models.RepoPath(params["username"], params["reponame"]), params["commitid"]) + diff, err := models.GetDiff(models.RepoPath(userName, repoName), commitId) if err != nil { ctx.Handle(404, "repo.Diff", err) return } - shortSha := params["commitid"][:7] + isImageFile := func(name string) bool { + repoFile, err := models.GetTargetFile(userName, repoName, + branchName, commitId, name) + + if err != nil { + return false + } + + blob, err := repoFile.LookupBlob() + if err != nil { + return false + } + + data := blob.Contents() + _, isImage := base.IsImageFile(data) + return isImage + } + + shortSha := params["commitid"][:10] + ctx.Data["IsImageFile"] = isImageFile ctx.Data["Title"] = commit.Message() + " · " + shortSha ctx.Data["Commit"] = commit ctx.Data["ShortSha"] = shortSha ctx.Data["Diff"] = diff ctx.Data["IsRepoToolbarCommits"] = true + ctx.Data["SourcePath"] = "/" + path.Join(userName, repoName, "src", commitId) + ctx.Data["RawPath"] = "/" + path.Join(userName, repoName, "raw", commitId) ctx.HTML(200, "repo/diff") } diff --git a/routers/repo/issue.go b/routers/repo/issue.go index e53aebf636..77e35bbae6 100644 --- a/routers/repo/issue.go +++ b/routers/repo/issue.go @@ -6,6 +6,7 @@ package repo import ( "fmt" + "net/url" "github.com/codegangsta/martini" @@ -17,23 +18,41 @@ import ( "github.com/gogits/gogs/modules/middleware" ) -func Issues(ctx *middleware.Context, params martini.Params) { +func Issues(ctx *middleware.Context) { + if !ctx.Repo.IsValid { + ctx.Handle(404, "issue.Issues(invalid repo):", nil) + } + ctx.Data["Title"] = "Issues" ctx.Data["IsRepoToolbarIssues"] = true ctx.Data["IsRepoToolbarIssuesList"] = true + ctx.Data["ViewType"] = "all" + + milestoneId, _ := base.StrTo(ctx.Query("milestone")).Int() + page, _ := base.StrTo(ctx.Query("page")).Int() - milestoneId, _ := base.StrTo(params["milestone"]).Int() - page, _ := base.StrTo(params["page"]).Int() + ctx.Data["IssueCreatedCount"] = 0 + + var posterId int64 = 0 + if ctx.Query("type") == "created_by" { + if !ctx.IsSigned { + ctx.SetCookie("redirect_to", "/"+url.QueryEscape(ctx.Req.RequestURI)) + ctx.Redirect("/user/login/", 302) + return + } + posterId = ctx.User.Id + ctx.Data["ViewType"] = "created_by" + ctx.Data["IssueCreatedCount"] = models.GetUserIssueCount(posterId, ctx.Repo.Repository.Id) + } // Get issues. - issues, err := models.GetIssues(0, ctx.Repo.Repository.Id, 0, - int64(milestoneId), page, params["state"] == "closed", false, params["labels"], params["sortType"]) + issues, err := models.GetIssues(0, ctx.Repo.Repository.Id, posterId, int64(milestoneId), page, + ctx.Query("state") == "closed", false, ctx.Query("labels"), ctx.Query("sortType")) if err != nil { ctx.Handle(200, "issue.Issues: %v", err) return } - var closedCount int // Get posters. for i := range issues { u, err := models.GetUserById(issues[i].PosterId) @@ -41,21 +60,22 @@ func Issues(ctx *middleware.Context, params martini.Params) { ctx.Handle(200, "issue.Issues(get poster): %v", err) return } - - if issues[i].IsClosed { - closedCount++ - } issues[i].Poster = u } ctx.Data["Issues"] = issues - ctx.Data["IssueCount"] = len(issues) - ctx.Data["OpenCount"] = len(issues) - closedCount - ctx.Data["ClosedCount"] = closedCount + ctx.Data["IssueCount"] = ctx.Repo.Repository.NumIssues + ctx.Data["OpenCount"] = ctx.Repo.Repository.NumIssues - ctx.Repo.Repository.NumClosedIssues + ctx.Data["ClosedCount"] = ctx.Repo.Repository.NumClosedIssues + ctx.Data["IsShowClosed"] = ctx.Query("state") == "closed" ctx.HTML(200, "issue/list") } func CreateIssue(ctx *middleware.Context, params martini.Params, form auth.CreateIssueForm) { + if !ctx.Repo.IsValid { + ctx.Handle(404, "issue.CreateIssue(invalid repo):", nil) + } + ctx.Data["Title"] = "Create issue" ctx.Data["IsRepoToolbarIssues"] = true ctx.Data["IsRepoToolbarIssuesList"] = false @@ -71,15 +91,16 @@ func CreateIssue(ctx *middleware.Context, params martini.Params, form auth.Creat } issue, err := models.CreateIssue(ctx.User.Id, ctx.Repo.Repository.Id, form.MilestoneId, form.AssigneeId, - form.IssueName, form.Labels, form.Content, false) + ctx.Repo.Repository.NumIssues, form.IssueName, form.Labels, form.Content, false) if err != nil { ctx.Handle(200, "issue.CreateIssue", err) return } // Notify watchers. - if err = models.NotifyWatchers(ctx.User.Id, ctx.Repo.Repository.Id, models.OP_CREATE_ISSUE, - ctx.User.Name, ctx.Repo.Repository.Name, "", fmt.Sprintf("%d|%s", issue.Index, issue.Name)); err != nil { + if err = models.NotifyWatchers(&models.Action{ActUserId: ctx.User.Id, ActUserName: ctx.User.Name, + OpType: models.OP_CREATE_ISSUE, Content: fmt.Sprintf("%d|%s", issue.Index, issue.Name), + RepoId: ctx.Repo.Repository.Id, RepoName: ctx.Repo.Repository.Name, RefName: ""}); err != nil { ctx.Handle(200, "issue.CreateIssue", err) return } @@ -97,6 +118,10 @@ func CreateIssue(ctx *middleware.Context, params martini.Params, form auth.Creat } func ViewIssue(ctx *middleware.Context, params martini.Params) { + if !ctx.Repo.IsValid { + ctx.Handle(404, "issue.ViewIssue(invalid repo):", nil) + } + index, err := base.StrTo(params["index"]).Int() if err != nil { ctx.Handle(404, "issue.ViewIssue", err) @@ -120,6 +145,7 @@ func ViewIssue(ctx *middleware.Context, params martini.Params) { return } issue.Poster = u + issue.Content = string(base.RenderMarkdown([]byte(issue.Content), "")) // Get comments. comments, err := models.GetIssueComments(issue.Id) @@ -136,6 +162,7 @@ func ViewIssue(ctx *middleware.Context, params martini.Params) { return } comments[i].Poster = u + comments[i].Content = string(base.RenderMarkdown([]byte(comments[i].Content), "")) } ctx.Data["Title"] = issue.Name @@ -147,6 +174,10 @@ func ViewIssue(ctx *middleware.Context, params martini.Params) { } func UpdateIssue(ctx *middleware.Context, params martini.Params, form auth.CreateIssueForm) { + if !ctx.Repo.IsValid { + ctx.Handle(404, "issue.UpdateIssue(invalid repo):", nil) + } + index, err := base.StrTo(params["index"]).Int() if err != nil { ctx.Handle(404, "issue.UpdateIssue", err) @@ -183,6 +214,10 @@ func UpdateIssue(ctx *middleware.Context, params martini.Params, form auth.Creat } func Comment(ctx *middleware.Context, params martini.Params) { + if !ctx.Repo.IsValid { + ctx.Handle(404, "issue.Comment(invalid repo):", nil) + } + index, err := base.StrTo(ctx.Query("issueIndex")).Int() if err != nil { ctx.Handle(404, "issue.Comment", err) diff --git a/routers/repo/repo.go b/routers/repo/repo.go index 435587472a..e7107ad1cd 100644 --- a/routers/repo/repo.go +++ b/routers/repo/repo.go @@ -57,19 +57,23 @@ func Single(ctx *middleware.Context, params martini.Params) { return } + branchName := params["branchname"] + userName := params["username"] + repoName := params["reponame"] + // Get tree path treename := params["_1"] if len(treename) > 0 && treename[len(treename)-1] == '/' { ctx.Redirect("/" + ctx.Repo.Owner.LowerName + "/" + - ctx.Repo.Repository.Name + "/src/" + params["branchname"] + "/" + treename[:len(treename)-1]) + ctx.Repo.Repository.Name + "/src/" + branchName + "/" + treename[:len(treename)-1]) return } ctx.Data["IsRepoToolbarSource"] = true // Branches. - brs, err := models.GetBranches(params["username"], params["reponame"]) + brs, err := models.GetBranches(userName, repoName) if err != nil { ctx.Handle(404, "repo.Single(GetBranches)", err) return @@ -80,15 +84,22 @@ func Single(ctx *middleware.Context, params martini.Params) { } ctx.Data["Branches"] = brs - repoFile, err := models.GetTargetFile(params["username"], params["reponame"], - params["branchname"], params["commitid"], treename) + var commitId string + isViewBranch := models.IsBranchExist(userName, repoName, branchName) + if !isViewBranch { + commitId = branchName + } + ctx.Data["IsViewBranch"] = isViewBranch + + repoFile, err := models.GetTargetFile(userName, repoName, + branchName, commitId, treename) if err != nil && err != models.ErrRepoFileNotExist { ctx.Handle(404, "repo.Single(GetTargetFile)", err) return } - branchLink := "/" + ctx.Repo.Owner.LowerName + "/" + ctx.Repo.Repository.Name + "/src/" + params["branchname"] - rawLink := "/" + ctx.Repo.Owner.LowerName + "/" + ctx.Repo.Repository.Name + "/raw/" + params["branchname"] + branchLink := "/" + ctx.Repo.Owner.LowerName + "/" + ctx.Repo.Repository.Name + "/src/" + branchName + rawLink := "/" + ctx.Repo.Owner.LowerName + "/" + ctx.Repo.Repository.Name + "/raw/" + branchName if len(treename) != 0 && repoFile == nil { ctx.Handle(404, "repo.Single", nil) @@ -111,23 +122,28 @@ func Single(ctx *middleware.Context, params martini.Params) { data := blob.Contents() _, isTextFile := base.IsTextFile(data) + _, isImageFile := base.IsImageFile(data) ctx.Data["FileIsText"] = isTextFile - readmeExist := base.IsMarkdownFile(repoFile.Name) || base.IsReadmeFile(repoFile.Name) - ctx.Data["ReadmeExist"] = readmeExist - if readmeExist { - ctx.Data["FileContent"] = string(base.RenderMarkdown(data, "")) + if isImageFile { + ctx.Data["IsImageFile"] = true } else { - if isTextFile { - ctx.Data["FileContent"] = string(data) + readmeExist := base.IsMarkdownFile(repoFile.Name) || base.IsReadmeFile(repoFile.Name) + ctx.Data["ReadmeExist"] = readmeExist + if readmeExist { + ctx.Data["FileContent"] = string(base.RenderMarkdown(data, "")) + } else { + if isTextFile { + ctx.Data["FileContent"] = string(data) + } } } } } else { // Directory and file list. - files, err := models.GetReposFiles(params["username"], params["reponame"], - params["branchname"], params["commitid"], treename) + files, err := models.GetReposFiles(userName, repoName, + branchName, commitId, treename) if err != nil { ctx.Handle(404, "repo.Single(GetReposFiles)", err) return @@ -166,8 +182,8 @@ func Single(ctx *middleware.Context, params martini.Params) { } } - ctx.Data["Username"] = params["username"] - ctx.Data["Reponame"] = params["reponame"] + ctx.Data["Username"] = userName + ctx.Data["Reponame"] = repoName var treenames []string Paths := make([]string, 0) @@ -185,8 +201,8 @@ func Single(ctx *middleware.Context, params martini.Params) { } // Get latest commit according username and repo name. - commit, err := models.GetCommit(params["username"], params["reponame"], - params["branchname"], params["commitid"]) + commit, err := models.GetCommit(userName, repoName, + branchName, commitId) if err != nil { log.Error("repo.Single(GetCommit): %v", err) ctx.Handle(404, "repo.Single(GetCommit)", err) @@ -194,6 +210,8 @@ func Single(ctx *middleware.Context, params martini.Params) { } ctx.Data["LastCommit"] = commit + ctx.Data["CommitId"] = commitId + ctx.Data["Paths"] = Paths ctx.Data["Treenames"] = treenames ctx.Data["BranchLink"] = branchLink @@ -209,8 +227,18 @@ func SingleDownload(ctx *middleware.Context, params martini.Params) { // Get tree path treename := params["_1"] - repoFile, err := models.GetTargetFile(params["username"], params["reponame"], - params["branchname"], params["commitid"], treename) + branchName := params["branchname"] + userName := params["username"] + repoName := params["reponame"] + + var commitId string + if !models.IsBranchExist(userName, repoName, branchName) { + commitId = branchName + branchName = "" + } + + repoFile, err := models.GetTargetFile(userName, repoName, + branchName, commitId, treename) if err != nil { ctx.Handle(404, "repo.SingleDownload(GetTargetFile)", err) @@ -225,9 +253,9 @@ func SingleDownload(ctx *middleware.Context, params martini.Params) { data := blob.Contents() contentType, isTextFile := base.IsTextFile(data) + _, isImageFile := base.IsImageFile(data) ctx.Res.Header().Set("Content-Type", contentType) - if !isTextFile { - ctx.Res.Header().Set("Content-Type", contentType) + if !isTextFile && !isImageFile { ctx.Res.Header().Set("Content-Disposition", "attachment; filename="+filepath.Base(treename)) ctx.Res.Header().Set("Content-Transfer-Encoding", "binary") } diff --git a/routers/user/user.go b/routers/user/user.go index d3ef96211e..b0fc583978 100644 --- a/routers/user/user.go +++ b/routers/user/user.go @@ -286,6 +286,85 @@ func Feeds(ctx *middleware.Context, form auth.FeedsForm) { func Issues(ctx *middleware.Context) { ctx.Data["Title"] = "Your Issues" + ctx.Data["ViewType"] = "all" + + page, _ := base.StrTo(ctx.Query("page")).Int() + repoId, _ := base.StrTo(ctx.Query("repoid")).Int64() + + ctx.Data["RepoId"] = repoId + + var posterId int64 = 0 + if ctx.Query("type") == "created_by" { + posterId = ctx.User.Id + ctx.Data["ViewType"] = "created_by" + } + + // Get all repositories. + repos, err := models.GetRepositories(ctx.User) + if err != nil { + ctx.Handle(200, "user.Issues(get repositories)", err) + return + } + + showRepos := make([]models.Repository, 0, len(repos)) + + var closedIssueCount, createdByCount int + + // Get all issues. + allIssues := make([]models.Issue, 0, 5*len(repos)) + for i, repo := range repos { + issues, err := models.GetIssues(0, repo.Id, posterId, 0, page, false, false, "", "") + if err != nil { + ctx.Handle(200, "user.Issues(get issues)", err) + return + } + + closedIssueCount += repo.NumClosedIssues + + // Set repository information to issues. + for j := range issues { + issues[j].Repo = &repos[i] + } + allIssues = append(allIssues, issues...) + + repos[i].NumOpenIssues = repo.NumIssues - repo.NumClosedIssues + if repos[i].NumOpenIssues > 0 { + showRepos = append(showRepos, repos[i]) + + } + } + + showIssues := make([]models.Issue, 0, len(allIssues)) + isShowClosed := ctx.Query("state") == "closed" + ctx.Data["IsShowClosed"] = isShowClosed + + // Get posters and filter issues. + for i := range allIssues { + u, err := models.GetUserById(allIssues[i].PosterId) + if err != nil { + ctx.Handle(200, "user.Issues(get poster): %v", err) + return + } + allIssues[i].Poster = u + if u.Id == ctx.User.Id { + createdByCount++ + } + + if repoId > 0 && repoId != allIssues[i].Repo.Id { + continue + } + + if isShowClosed == allIssues[i].IsClosed { + showIssues = append(showIssues, allIssues[i]) + } + } + + ctx.Data["Repos"] = showRepos + ctx.Data["Issues"] = showIssues + ctx.Data["AllIssueCount"] = len(allIssues) + ctx.Data["ClosedIssueCount"] = closedIssueCount + ctx.Data["OpenIssueCount"] = len(allIssues) - closedIssueCount + ctx.Data["CreatedByCount"] = createdByCount ctx.HTML(200, "issue/user") } diff --git a/templates/install.tmpl b/templates/install.tmpl index 849491f898..4fbef3cba0 100644 --- a/templates/install.tmpl +++ b/templates/install.tmpl @@ -2,69 +2,198 @@ <div id="body" class="container"> <form action="/install" method="post" class="form-horizontal card" id="install-card"> {{.CsrfTokenHtml}} - <h3>Install Steps</h3> + <h3>Install Steps For First-time Run</h3> + <div class="alert alert-danger form-error{{if .HasError}}{{else}} hidden{{end}}">{{.ErrorMsg}}</div> - <p class="help-block text-center">GoGits need MySQL or PostgreSQL server</p> - <div class="form-group {{if .Err_User}}has-error has-feedback{{end}}"> - <label class="col-md-3 control-label"><strong>MySQL </strong>Host: </label> + <p class="help-block text-center">Gogs requires MySQL or PostgreSQL based on your choice</p> + <div class="form-group"> + <label class="col-md-3 control-label">Database Type: </label> <div class="col-md-8"> - <input name="host" class="form-control" placeholder="Type mysql server ip or domain" value="localhost" required="required"> + <select name="database" id="install-database" class="form-control"> + <option value="mysql">MySQL</option> + <option value="pgsql">PostgreSQL</option> + <option value="sqlite">SQLite</option> + </select> </div> </div> - <div class="form-group {{if .Err_User}}has-error has-feedback{{end}}"> - <label class="col-md-3 control-label">Port: </label> - <div class="col-md-8"> - <input name="port" class="form-control" placeholder="Type mysql server port" value="3306" required="required"> + <div class="server-sql"> + <div class="form-group"> + <label class="col-md-3 control-label">Host: </label> + + <div class="col-md-8"> + <input name="host" class="form-control" placeholder="Type mysql server ip or domain" value="localhost" required="required"> + </div> </div> - </div> - <div class="form-group {{if .Err_User}}has-error has-feedback{{end}}"> - <label class="col-md-3 control-label">User: </label> - <div class="col-md-8"> - <input name="user" class="form-control" placeholder="Type mysql username" required="required"> + <div class="form-group"> + <label class="col-md-3 control-label">Port: </label> + + <div class="col-md-8"> + <input name="port" class="form-control" placeholder="Type mysql server port" value="3306" required="required"> + </div> </div> - </div> - <div class="form-group {{if .Err_Password}}has-error has-feedback{{end}}"> - <label class="col-md-3 control-label">Password: </label> - <div class="col-md-8"> - <input name="passwd" type="password" class="form-control" placeholder="Type mysql password" required="required"> + <div class="form-group"> + <label class="col-md-3 control-label">User: </label> + + <div class="col-md-8"> + <input name="user" class="form-control" placeholder="Type mysql username" required="required"> + </div> + </div> + <div class="form-group"> + <label class="col-md-3 control-label">Password: </label> + + <div class="col-md-8"> + <input name="passwd" type="password" class="form-control" placeholder="Type mysql password" required="required"> + </div> + </div> + + <div class="form-group"> + <label class="col-md-3 control-label">Database Name: </label> + + <div class="col-md-8"> + <input name="database" type="text" class="form-control" placeholder="Type mysql database name" value="gogs" required="required"> + <p class="help-block">Recommend use INNODB engine with utf8_general_ci charset.</p> + </div> + </div> + + <div class="form-group pgsql-setting hide"> + <label class="col-md-3 control-label">SSL Mode: </label> + <div class="col-md-8"> + <select name="ssl_mode" class="form-control"> + <option value="disable">Disable</option> + <option value="require">Require</option> + <option value="verify-full">Verify Full</option> + </select> + </div> </div> </div> - <div class="form-group {{if .Err_Password}}has-error has-feedback{{end}}"> - <label class="col-md-3 control-label">Database: </label> - <div class="col-md-8"> - <input name="database" type="text" class="form-control" placeholder="Type mysql database name" value="gogs" required="required"> - <p class="help-block">Recommend use INNODB engine with utf8_general_ci charset.</p> + <div class="sqlite-setting hide"> + <div class="form-group"> + <label class="col-md-3 control-label">Path: </label> + + <div class="col-md-8"> + <input name="path" class="form-control" placeholder="Type sqlite file path" value="xxx/file.db"> + <p class="help-block">The file path of SQLite database.</p> + </div> </div> </div> - <div class="form-group"> + <!-- <div class="form-group"> <div class="col-md-8 col-md-offset-3"> - <button class="btn btn-sm btn-info">Test Connection</button> + <button class="btn btn-sm btn-info">Test Connection</button> </div> - </div> + </div> --> <hr/> - <p class="help-block text-center">General settings for GoGits</p> + <p class="help-block text-center">General Settings of Gogs</p> - <div class="form-group {{if .Err_Password}}has-error has-feedback{{end}}"> + <div class="form-group"> <label class="col-md-3 control-label">Repository Path: </label> + <div class="col-md-8"> <input name="repo-path" type="text" class="form-control" placeholder="Type your repository directory" value="/var/gogs/repostiory" required="required"> + <p class="help-block">The git copy of each repository is saved in this directory.</p> </div> </div> - <div class="form-group {{if .Err_Password}}has-error has-feedback{{end}}"> - <label class="col-md-3 control-label">System User: </label> + <div class="form-group"> + <label class="col-md-3 control-label">Run User: </label> + <div class="col-md-8"> <input name="system-user" type="text" class="form-control" placeholder="Type mysql password" value="root" required="required"> - <p class="help-block">The user has access to visit and run GoGits.</p> + <p class="help-block">The user has access to visit and run Gogs.</p> </div> </div> + <hr/> + + <p class="help-block text-center">Admin Account Settings</p> + + <div class="form-group"> + <label class="col-md-3 control-label">Username: </label> + + <div class="col-md-8"> + <input name="repo-path" type="text" class="form-control" placeholder="Type admin user name" value="admin" required="required"> + </div> + </div> + <div class="form-group"> + <label class="col-md-3 control-label">Password: </label> + + <div class="col-md-8"> + <input name="system-user" type="password" class="form-control" placeholder="Type admin user password" required="required"> + </div> + </div> + + <hr/> + <div class="form-group text-center"> - <a class="btn btn-danger btn-lg">Install GoGits</a> + <button class="btn btn-danger btn-lg">Install Gogs</button> + <button class="btn btn-default btn-sm" type="button" data-toggle="modal" data-target="#advance-options-modal"> + Advanced Options + </button> </div> + + <div class="modal fade" id="advance-options-modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> + <div class="modal-dialog"> + <div class="modal-content"> + <div class="modal-header"><h4 class="modal-title">Advanced Options</h4></div> + <div class="modal-body"> + <p class="help-block text-center">Email Service Settings</p> + + <div class="form-group"> + <label class="col-md-3 control-label">SMTP Host: </label> + + <div class="col-md-8"> + <input name="repo-path" type="text" class="form-control" placeholder="Type admin user name"> + </div> + </div> + <div class="form-group"> + <label class="col-md-3 control-label">Email: </label> + + <div class="col-md-8"> + <input name="repo-path" type="text" class="form-control" placeholder="Type admin user name"> + </div> + </div> + <div class="form-group"> + <label class="col-md-3 control-label">Password: </label> + + <div class="col-md-8"> + <input name="system-user" type="password" class="form-control" placeholder="Type admin user password"> + </div> + </div> + <hr/> + <p class="text-center help-block">Notification Settings</p> + + <div class="form-group"> + <div class="col-md-offset-3 col-md-7"> + <div class="checkbox"> + <label> + <input name="system-user" type="checkbox"> + <strong>Enable Register Confirmation</strong> + </label> + </div> + </div> + </div> + + <div class="form-group"> + <div class="col-md-offset-3 col-md-7"> + <div class="checkbox"> + <label> + <input name="system-user" type="checkbox"> + <strong>Enable Mail Notification</strong> + </label> + </div> + </div> + </div> + </div> + + <div class="modal-footer"> + <button type="button" class="btn btn-success" data-dismiss="modal">Confirm</button> + </div> + </div> + </div> + </div> + </form> </div> {{template "base/footer" .}}
\ No newline at end of file diff --git a/templates/issue/list.tmpl b/templates/issue/list.tmpl index 60f22a0d31..7622a4c172 100644 --- a/templates/issue/list.tmpl +++ b/templates/issue/list.tmpl @@ -6,16 +6,17 @@ <div id="issue"> <div class="col-md-3 filter-list"> <ul class="list-unstyled"> - <li><a href="#" class="active">All Issues <strong class="pull-right">{{.IssueCount}}</strong></a></li> - <li><a href="#">My Issues</a></li> - <li><a href="#">Mentioned</a></li> + <li><a href="/{{.RepositoryLink}}/issues"{{if eq .ViewType "all"}} class="active"{{end}}>All Issues <strong class="pull-right">{{.IssueCount}}</strong></a></li> + <!-- <li><a href="#">Assigned to you</a></li> --> + <li><a href="/{{.RepositoryLink}}/issues?type=created_by"{{if eq .ViewType "created_by"}} class="active"{{end}}>Created by you <strong class="pull-right">{{.IssueCreatedCount}}</strong></a></li> + <!-- <li><a href="#">Mentioned</a></li> --> </ul> </div> <div class="col-md-9"> <div class="filter-option"> <div class="btn-group"> - <a class="btn btn-default active issue-open" href="#">{{.OpenCount}} Open</a> - <a class="btn btn-default issue-close" href="#">{{.ClosedCount}} Closed</a> + <a class="btn btn-default issue-open{{if not .IsShowClosed}} active{{end}}" href="/{{.RepositoryLink}}/issues?type={{.ViewType}}">{{.OpenCount}} Open</a> + <a class="btn btn-default issue-close{{if .IsShowClosed}} active{{end}}" href="/{{.RepositoryLink}}/issues?state=closed&type={{.ViewType}}">{{.ClosedCount}} Closed</a> </div> </div> <div class="issues list-group"> diff --git a/templates/issue/user.tmpl b/templates/issue/user.tmpl index 2cf9571306..1d49395cb9 100644 --- a/templates/issue/user.tmpl +++ b/templates/issue/user.tmpl @@ -16,53 +16,35 @@ <div id="issue"> <div class="col-md-3 filter-list"> <ul class="list-unstyled"> - <li><a href="#" class="active">In your repositories <strong class="pull-right">10</strong></a></li> - <li><a href="#">Created by you</a></li> - <li><a href="#">Assigned to you</a></li> + <li><a href="/issues"{{if eq .ViewType "all"}} class="active"{{end}}>In your repositories <strong class="pull-right">{{.AllIssueCount}}</strong></a></li> + <!-- <li><a href="#">Assigned to you</a></li> --> + <li><a href="/issues?type=created_by"{{if eq .ViewType "created_by"}} class="active"{{end}}>Created by you <strong class="pull-right">{{.CreatedByCount}}</strong></a></li> <li><hr/></li> - <li><a href="" class="sm">gogits/gogs <strong class="pull-right">12</strong></a></li> - <li><a href="" class="sm">gogits/session <strong class="pull-right">8</strong></a></li> - <li><a href="" class="sm">gogits/git <strong class="pull-right">2</strong></a></li> + {{range .Repos}} + <li><a href="/issues?type={{$.ViewType}}{{if eq $.RepoId .Id}}{{else}}&repoid={{.Id}}{{end}}" class="sm{{if eq $.RepoId .Id}} active{{end}}">{{$.SignedUser.Name}}/{{.Name}} <strong class="pull-right">{{.NumOpenIssues}}</strong></a></li> + {{end}} </ul> </div> <div class="col-md-9"> <div class="filter-option"> <div class="btn-group"> - <a class="btn btn-default active issue-open" href="#">27 Open</a> - <a class="btn btn-default issue-close" href="#">Close 128</a> + <a class="btn btn-default issue-open{{if not .IsShowClosed}} active{{end}}" href="/issues?type={{.ViewType}}&repoid={{.RepoId}}">{{.OpenIssueCount}} Open</a> + <a class="btn btn-default issue-close{{if .IsShowClosed}} active{{end}}" href="/issues?state=closed&type={{.ViewType}}&repoid={{.RepoId}}">{{.ClosedIssueCount}} Close</a> </div> </div> <div class="issues list-group"> - <div class="list-group-item unread issue-item" id="issue-id"> - <span class="number pull-right">#123</span> - <h5 class="title"><a href="#">Bug: When running tests after generating a beego app, templates do not load.</a></h5> + {{range .Issues}} + <div class="list-group-item issue-item" id="issue-{{.Id}}"> + <span class="number pull-right">#{{.Index}}</span> + <h5 class="title"><a href="/{{$.SignedUser.Name}}/{{.Repo.Name}}/issues/{{.Index}}">{{.Name}}</a></h5> <p class="info"> - <span class="author"><img class="avatar" src="http://tp2.sinaimg.cn/5068084885/50/40050297589/1" alt="" width="20"/> - <a href="#">Obama</a></span> - <span class="time">3 days ago</span> - <span class="comment"><i class="fa fa-comments"></i> 3</span> - </p> - </div> - <div class="list-group-item issue-item" id="issue-id2"> - <span class="number pull-right">#123</span> - <h5 class="title"><a href="#">Bug: When running tests after generating a beego app, templates do not load.</a></h5> - <p class="info"> - <span class="author"><img class="avatar" src="http://tp2.sinaimg.cn/5068084885/50/40050297589/1" alt="" width="20"/> - <a href="#">Obama</a></span> - <span class="time">3 days ago</span> - <span class="comment"><i class="fa fa-comments"></i> 3</span> - </p> - </div> - <div class="list-group-item issue-item" id="issue-id3"> - <span class="number pull-right">#123</span> - <h5 class="title"><a href="#">Bug: When running tests after generating a beego app, templates do not load.</a></h5> - <p class="info"> - <span class="author"><img class="avatar" src="http://tp2.sinaimg.cn/5068084885/50/40050297589/1" alt="" width="20"/> - <a href="#">Obama</a></span> - <span class="time">3 days ago</span> - <span class="comment"><i class="fa fa-comments"></i> 3</span> + <span class="author"><img class="avatar" src="{{.Poster.AvatarLink}}" alt="" width="20"/> + <a href="/user/{{.Poster.Name}}">{{.Poster.Name}}</a></span> + <span class="time">{{TimeSince .Created}}</span> + <span class="comment"><i class="fa fa-comments"></i> {{.NumComments}}</span> </p> </div> + {{end}} </div> </div> </div> diff --git a/templates/issue/view.tmpl b/templates/issue/view.tmpl index 6b282513aa..4d053930b9 100644 --- a/templates/issue/view.tmpl +++ b/templates/issue/view.tmpl @@ -4,12 +4,16 @@ {{template "repo/toolbar" .}} <div id="body" class="container"> <div id="issue"> - <div id="issue-id" class="issue-whole"> + <div id="issue-{issue.id}" class="issue-whole"> <div class="issue-head clearfix"> <div class="number pull-right">#{{.Issue.Index}}</div> <a class="author pull-left" href="/user/{{.Issue.Poster.Name}}"><img class="avatar" src="{{.Issue.Poster.AvatarLink}}" alt="" width="30"/></a> <h1 class="title pull-left">{{.Issue.Name}}</h1> + <input id="issue-edit-title" class="form-control input-lg pull-left hidden" type="text" value="{issue.title}" data-ajax-rel="issue-save"/> <p class="info pull-left"> + <a class="btn btn-default pull-right issue-edit" href="#" id="issue-edit-btn">Edit</a> + <a class="btn btn-danger pull-right issue-edit-cancel hidden" href="#">Cancel</a> + <a class="btn btn-primary pull-right issue-edit-save hidden" href="#" data-ajax="{issue.save.link}" data-ajax-name="issue-save">Save</a> <span class="status label label-{{if .Issue.IsClosed}}danger{{else}}success{{end}}">{{if .Issue.IsClosed}}Closed{{else}}Open{{end}}</span> <a href="/user/{{.Issue.Poster.Name}}" class="author"><strong>{{.Issue.Poster.Name}}</strong></a> opened this issue <span class="time">{{TimeSince .Issue.Created}}</span> · {{.Issue.NumComments}} comments @@ -18,18 +22,24 @@ <div class="issue-main"> <div class="panel panel-default issue-content"> <div class="panel-body markdown"> - <p>{{.Issue.Content}}</p> + <div class="content"> + {{str2html .Issue.Content}} + </div> + <textarea class="form-control hidden" name="content" id="issue-edit-content" rows="10" data-ajax-rel="issue-save">content</textarea> </div> </div> {{range .Comments}} - <div class="issue-child"> + <div class="issue-child" id="issue-comment-{issue.comment.id}"> <a class="user pull-left" href="/user/{{.Poster.Name}}"><img class="avatar" src="{{.Poster.AvatarLink}}" alt=""/></a> <div class="issue-content panel panel-default"> <div class="panel-heading"> <a href="/user/{{.Poster.Name}}" class="user">{{.Poster.Name}}</a> commented <span class="time">{{TimeSince .Created}}</span> + <a class="issue-comment-del pull-right issue-action" href="#" title="Edit Comment"><i class="fa fa-times-circle"></i></a> + <a class="issue-comment-edit pull-right issue-action" href="#" title="Remove Comment" data-url="{remove-link}"><i class="fa fa-edit"></i></a> + <span class="role label label-default pull-right">Owner</span> </div> <div class="panel-body markdown"> - <p>{{.Content}}</p> + {{str2html .Content}} </div> </div> </div> @@ -52,7 +62,7 @@ <div class="tab-pane" id="issue-textarea"> <div class="form-group"> <input type="hidden" value="{{.Issue.Index}}" name="issueIndex"/> - <textarea class="form-control" name="content" id="issue-content" rows="10" placeholder="Write some content">{{.content}}</textarea> + <textarea class="form-control" name="content" id="issue-reply-content" rows="10" placeholder="Write some content">{{.content}}</textarea> </div> </div> <div class="tab-pane" id="issue-preview">preview</div> @@ -61,7 +71,9 @@ <div class="text-right"> <div class="form-group"> <input type="hidden" value="id" name="repo-id"/> - <button class="btn-success btn">Comment</button> + <button class="btn-default btn issue-open" id="issue-open-btn" data-origin="Open" data-text="Open & Comment">Open</button> + <button class="btn-default btn issue-close" id="issue-close-btn" data-origin="Close" data-text="Close & Comment">Close</button> + <button class="btn-success btn" id="issue-reply-btn">Comment</button> </div> </div> </div> diff --git a/templates/repo/commits.tmpl b/templates/repo/commits.tmpl index 2f308a108d..efe35936c4 100644 --- a/templates/repo/commits.tmpl +++ b/templates/repo/commits.tmpl @@ -27,7 +27,7 @@ {{range $r}} <tr> <td class="author"><img class="avatar" src="{{AvatarLink .Committer.Email}}" alt=""/><a href="/user/{{.Committer.Name}}">{{.Committer.Name}}</a></td> - <td class="sha"><a class="label label-success" href="/{{$username}}/{{$reponame}}/commit/{{.Id}} ">{{SubStr .Id.String 0 7}} </a></td> + <td class="sha"><a class="label label-success" href="/{{$username}}/{{$reponame}}/commit/{{.Id}} ">{{SubStr .Id.String 0 10}} </a></td> <td class="message">{{.Message}} </td> <td class="date">{{TimeSince .Committer.When}}</td> </tr> @@ -37,4 +37,4 @@ </div> </div> </div> -{{template "base/footer" .}}
\ No newline at end of file +{{template "base/footer" .}} diff --git a/templates/repo/diff.tmpl b/templates/repo/diff.tmpl index 38f19b36f5..e58f2d664e 100644 --- a/templates/repo/diff.tmpl +++ b/templates/repo/diff.tmpl @@ -6,7 +6,7 @@ <div id="source"> <div class="panel panel-info diff-box diff-head-box"> <div class="panel-heading"> - <a class="pull-right btn btn-primary btn-sm" href="#commit-source">Browse Source</a> + <a class="pull-right btn btn-primary btn-sm" href="{{.SourcePath}}">Browse Source</a> <h4>{{.Commit.Message}}</h4> </div> <div class="panel-body"> @@ -57,10 +57,16 @@ </span> <span class="del" data-line="{{.Deletion}}">- {{.Deletion}}</span> </div> - <a class="btn btn-default btn-sm pull-right" href="#">View File</a> + <a class="btn btn-default btn-sm pull-right" href="{{$.SourcePath}}/{{.Name}}">View File</a> <span class="file">{{.Name}}</span> </div> + {{$isImage := (call $.IsImageFile .Name)}} <div class="panel-body file-body file-code code-view code-diff"> + {{if $isImage}} + <div class="text-center"> + <img src="{{$.RawPath}}/{{.Name}}"> + </div> + {{else}} <table> <tbody> {{range .Sections}} @@ -201,6 +207,7 @@ </tr> --> </tbody> </table> + {{end}} </div> </div> {{end}} @@ -411,4 +418,4 @@ </div> --> </div> </div> -{{template "base/footer" .}}
\ No newline at end of file +{{template "base/footer" .}} diff --git a/templates/repo/single.tmpl b/templates/repo/single.tmpl index ed04be0779..4c9406762c 100644 --- a/templates/repo/single.tmpl +++ b/templates/repo/single.tmpl @@ -11,7 +11,7 @@ {{ $n := len .Treenames}} {{if not .IsFile}}<button class="btn btn-default pull-right hidden"><i class="fa fa-plus-square"></i>Add File</button>{{end}} <div class="dropdown branch-switch"> - <a href="#" class="btn btn-success dropdown-toggle" data-toggle="dropdown"><i class="fa fa-chain"></i>{{.Branchname}} + <a href="#" class="btn btn-success dropdown-toggle" data-toggle="dropdown"><i class="fa fa-chain"></i>{{if .CommitId}}{{SubStr .CommitId 0 10}}{{else}}{{.Branchname}}{{end}} <b class="caret"></b></a> <ul class="dropdown-menu"> {{range .Branches}} @@ -41,4 +41,4 @@ {{end}} </div> </div> -{{template "base/footer" .}}
\ No newline at end of file +{{template "base/footer" .}} diff --git a/templates/repo/single_file.tmpl b/templates/repo/single_file.tmpl index cf398595e5..9199ca91f7 100644 --- a/templates/repo/single_file.tmpl +++ b/templates/repo/single_file.tmpl @@ -23,7 +23,11 @@ </div> {{if not .FileIsText}} <div class="panel-footer text-center"> - <a href="{{.FileLink}}" class="btn btn-default">View Raw</a> + {{if .IsImageFile}} + <img src="{{.FileLink}}"> + {{else}} + <a href="{{.FileLink}}" class="btn btn-default">View Raw</a> + {{end}} </div> {{else}} {{if .ReadmeExist}} @@ -43,4 +47,4 @@ </div> {{end}} {{end}} -</div>
\ No newline at end of file +</div> diff --git a/templates/repo/toolbar.tmpl b/templates/repo/toolbar.tmpl index e3390c77ca..ac516c37dd 100644 --- a/templates/repo/toolbar.tmpl +++ b/templates/repo/toolbar.tmpl @@ -5,7 +5,7 @@ <ul class="nav navbar-nav"> <li class="{{if .IsRepoToolbarSource}}active{{end}}"><a href="/{{.RepositoryLink}}">Source</a></li> {{if not .IsBareRepo}} - <li class="{{if .IsRepoToolbarCommits}}active{{end}}"><a href="/{{.RepositoryLink}}/commits/{{.Branchname}}">Commits</a></li> + {{if .IsViewBranch}}<li class="{{if .IsRepoToolbarCommits}}active{{end}}"><a href="/{{.RepositoryLink}}/commits/{{.Branchname}}">Commits</a></li>{{end}} <!-- <li class="{{if .IsRepoToolbarBranches}}active{{end}}"><a href="/{{.RepositoryLink}}/branches">Branches</a></li> --> <!-- <li class="{{if .IsRepoToolbarPulls}}active{{end}}"><a href="/{{.RepositoryLink}}/pulls">Pull Requests</a></li> --> <li class="{{if .IsRepoToolbarIssues}}active{{end}}"><a href="/{{.RepositoryLink}}/issues">Issues <!--<span class="badge">42</span>--></a></li> @@ -138,6 +138,10 @@ func runWeb(*cli.Context) { r.Any("/:userid/delete", admin.DeleteUser) }, adminReq) + if martini.Env == martini.Dev { + m.Get("/template/**", dev.TemplatePreview) + } + m.Group("/:username/:reponame", func(r martini.Router) { r.Post("/settings", repo.SettingPost) r.Get("/settings", repo.Setting) @@ -168,10 +172,6 @@ func runWeb(*cli.Context) { r.Any("/:reponame/**", repo.Http) }, ignSignIn) - if martini.Env == martini.Dev { - m.Get("/template/**", dev.TemplatePreview) - } - // Not found handler. m.NotFound(routers.NotFound) |