diff --git a/.gitignore b/.gitignore index 425f227c1..eab518bb5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ + + gogs *.exe *.exe~ @@ -9,4 +11,27 @@ data/ .vendor/ .idea/ *.iml -public/img/avatar/ \ No newline at end of file +public/img/avatar/ + +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe \ No newline at end of file diff --git a/LICENSE b/LICENSE index 7cecc2485..3af16b072 100644 --- a/LICENSE +++ b/LICENSE @@ -1,19 +1,27 @@ -Copyright (c) 2011 Dmitriy Zaporozhets +Copyright (c) 2014 +All rights reserved. -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: +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. -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. +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the {organization} nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/README.md b/README.md index 104b7eab5..a39a92a32 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,7 @@ There are two ways to install Gogs: - Mail Service, modules design is inspired by [WeTalk](https://github.com/beego/wetalk). - System Monitor Status is inspired by [GoBlog](https://github.com/fuxiaohei/goblog). - Usage and modification from [beego](http://beego.me) modules. +- Thanks [gobuild.io](http://gobuild.io) for providing binary compile and download service. ## Contributors diff --git a/README_ZH.md b/README_ZH.md index 838e30a0e..440f952f8 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -49,6 +49,7 @@ Gogs 完全使用 Go 语言来实现对 Git 数据的操作,实现 **零** 依 - 基于 [GoBlog](https://github.com/fuxiaohei/goblog) 修改的系统监视状态。 - [beego](http://beego.me) 模块的使用与修改。 - [martini](http://martini.codegangsta.io/) 的路由与中间件机制。 +- 感谢 [gobuild.io](http://gobuild.io) 提供二进制编译与下载服务。 ## 贡献成员 diff --git a/models/repo.go b/models/repo.go index 6cbfaf105..d5f9be72a 100644 --- a/models/repo.go +++ b/models/repo.go @@ -109,7 +109,7 @@ func IsRepositoryExist(user *User, repoName string) (bool, error) { var ( // Define as all lower case!! - illegalPatterns = []string{"[.][Gg][Ii][Tt]", "user", "help", "stars", "issues", "pulls", "commits", "admin", "repo", "template", "admin"} + illegalPatterns = []string{"[.][Gg][Ii][Tt]", "raw", "user", "help", "stars", "issues", "pulls", "commits", "admin", "repo", "template", "admin"} ) // IsLegalName returns false if name contains illegal characters. diff --git a/modules/base/conf.go b/modules/base/conf.go index b4e0de97b..90b354764 100644 --- a/modules/base/conf.go +++ b/modules/base/conf.go @@ -100,7 +100,7 @@ func newService() { Service.EnableCacheAvatar = Cfg.MustBool("service", "ENABLE_CACHE_AVATAR", false) } -func NewLogService() { +func newLogService() { // Get and check log mode. LogMode = Cfg.MustValue("log", "MODE", "console") modeSec := "log." + LogMode @@ -243,7 +243,7 @@ func newNotifyMailService() { } func NewConfigContext() { - var err error + //var err error workDir, err := exeDir() if err != nil { fmt.Printf("Fail to get work directory: %s\n", err) @@ -296,7 +296,7 @@ func NewConfigContext() { func NewServices() { newService() - NewLogService() + newLogService() newCacheService() newSessionService() newMailService() diff --git a/modules/base/markdown.go b/modules/base/markdown.go index 05ce0c833..c722f04b2 100644 --- a/modules/base/markdown.go +++ b/modules/base/markdown.go @@ -6,6 +6,7 @@ package base import ( "bytes" + "net/http" "path" "path/filepath" "strings" @@ -42,6 +43,14 @@ func IsMarkdownFile(name string) bool { return false } +func IsTextFile(data []byte) (string, bool) { + contentType := http.DetectContentType(data) + if strings.Index(contentType, "text/") != -1 { + return contentType, true + } + return contentType, false +} + func IsReadmeFile(name string) bool { name = strings.ToLower(name) if len(name) < 6 { diff --git a/public/css/gogs.css b/public/css/gogs.css index 434af0359..2254ef5a2 100755 --- a/public/css/gogs.css +++ b/public/css/gogs.css @@ -224,6 +224,15 @@ html, body { line-height: 30px; } +#gogs-install-card{ + width: 800px; +} + +#gogs-install-card .form-group { + margin-left: 0; + margin-right: 0; +} + .gogs-card .btn { cursor: pointer; margin-right: 1.2em; @@ -676,6 +685,12 @@ html, body { margin: 0 .5em 0 0; } +.file-content .file-head .file-size { + font-size: 13px; + color: #888; + margin-left: 1em; +} + .file-content .file-body { padding: 30px 30px 50px; border: none; diff --git a/public/js/app.js b/public/js/app.js index 9a58a6f2f..f98fd03f8 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -291,7 +291,7 @@ function initRepository() { $clone.find('button[data-link]').on("click",function (e) { var $this = $(this); if (!$this.hasClass('btn-primary')) { - $clone.find('.btn-primary').removeClass('btn-primary').addClass("btn-default"); + $clone.find('.input-group-btn .btn-primary').removeClass('btn-primary').addClass("btn-default"); $(this).addClass('btn-primary').removeClass('btn-default'); $url.val($this.data("link")); $clone.find('span.clone-url').text($this.data('link')); diff --git a/routers/dashboard.go b/routers/dashboard.go index 76ecc3f67..2c81cf23c 100644 --- a/routers/dashboard.go +++ b/routers/dashboard.go @@ -5,6 +5,7 @@ package routers import ( + "github.com/gogits/gogs/modules/base" "github.com/gogits/gogs/modules/middleware" "github.com/gogits/gogs/routers/user" ) @@ -14,6 +15,14 @@ func Home(ctx *middleware.Context) { user.Dashboard(ctx) return } + + // Check auto-login. + userName := ctx.GetCookie(base.CookieUserName) + if len(userName) != 0 { + ctx.Redirect("/user/login") + return + } + ctx.Data["PageIsHome"] = true ctx.HTML(200, "home") } diff --git a/routers/install.go b/routers/install.go new file mode 100644 index 000000000..d7d5159ef --- /dev/null +++ b/routers/install.go @@ -0,0 +1,13 @@ +// 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 routers + +import "github.com/gogits/gogs/modules/middleware" + +func Install(ctx *middleware.Context){ + ctx.Data["PageIsInstall"] = true + ctx.Data["Title"] = "Install" + ctx.HTML(200,"install") +} diff --git a/routers/repo/issue.go b/routers/repo/issue.go index e03f115e2..d54582a2a 100644 --- a/routers/repo/issue.go +++ b/routers/repo/issue.go @@ -45,6 +45,7 @@ func CreateIssue(ctx *middleware.Context, params martini.Params, form auth.Creat } ctx.Data["Title"] = "Create issue" + ctx.Data["IsRepoToolbarIssues"] = true if ctx.Req.Method == "GET" { ctx.HTML(200, "issue/create") diff --git a/routers/repo/repo.go b/routers/repo/repo.go index a055b416c..cd28d52ca 100644 --- a/routers/repo/repo.go +++ b/routers/repo/repo.go @@ -6,11 +6,11 @@ package repo import ( "path" + "path/filepath" "strings" "github.com/codegangsta/martini" - "github.com/gogits/git" "github.com/gogits/webdav" "github.com/gogits/gogs/models" @@ -96,6 +96,7 @@ func Single(ctx *middleware.Context, params martini.Params) { } branchLink := "/" + ctx.Repo.Owner.LowerName + "/" + ctx.Repo.Repository.Name + "/src/" + params["branchname"] + rawLink := "/" + ctx.Repo.Owner.LowerName + "/" + ctx.Repo.Repository.Name + "/raw/" + params["branchname"] if len(treename) != 0 && repoFile == nil { ctx.Handle(404, "repo.Single", nil) @@ -103,12 +104,10 @@ func Single(ctx *middleware.Context, params martini.Params) { } if repoFile != nil && repoFile.IsFile() { - if repoFile.Size > 1024*1024 || repoFile.Filemode != git.FileModeBlob { - ctx.Data["FileIsLarge"] = true - } else if blob, err := repoFile.LookupBlob(); err != nil { - //log.Error("repo.Single(repoFile.LookupBlob): %v", err) + if blob, err := repoFile.LookupBlob(); err != nil { ctx.Handle(404, "repo.Single(repoFile.LookupBlob)", err) } else { + ctx.Data["FileSize"] = repoFile.Size ctx.Data["IsFile"] = true ctx.Data["FileName"] = repoFile.Name ext := path.Ext(repoFile.Name) @@ -116,13 +115,20 @@ func Single(ctx *middleware.Context, params martini.Params) { ext = ext[1:] } ctx.Data["FileExt"] = ext + ctx.Data["FileLink"] = rawLink + "/" + treename + + data := blob.Contents() + _, isTextFile := base.IsTextFile(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(blob.Contents(), "")) + ctx.Data["FileContent"] = string(base.RenderMarkdown(data, "")) } else { - ctx.Data["FileContent"] = string(blob.Contents()) + if isTextFile { + ctx.Data["FileContent"] = string(data) + } } } @@ -150,18 +156,21 @@ func Single(ctx *middleware.Context, params martini.Params) { } if readmeFile != nil { + ctx.Data["ReadmeInSingle"] = true ctx.Data["ReadmeExist"] = true - // if file large than 1M not show it - if readmeFile.Size > 1024*1024 || readmeFile.Filemode != git.FileModeBlob { - ctx.Data["FileIsLarge"] = true - } else if blob, err := readmeFile.LookupBlob(); err != nil { + if blob, err := readmeFile.LookupBlob(); err != nil { ctx.Handle(404, "repo.Single(readmeFile.LookupBlob)", err) return } else { - // current repo branch link - + ctx.Data["FileSize"] = readmeFile.Size + ctx.Data["FileLink"] = rawLink + "/" + treename + data := blob.Contents() + _, isTextFile := base.IsTextFile(data) + ctx.Data["FileIsText"] = isTextFile ctx.Data["FileName"] = readmeFile.Name - ctx.Data["FileContent"] = string(base.RenderMarkdown(blob.Contents(), branchLink)) + if isTextFile { + ctx.Data["FileContent"] = string(base.RenderMarkdown(data, branchLink)) + } } } } @@ -201,6 +210,44 @@ func Single(ctx *middleware.Context, params martini.Params) { ctx.HTML(200, "repo/single") } +func SingleDownload(ctx *middleware.Context, params martini.Params) { + if !ctx.Repo.IsValid { + ctx.Handle(404, "repo.SingleDownload", nil) + return + } + + if len(params["branchname"]) == 0 { + params["branchname"] = "master" + } + + // Get tree path + treename := params["_1"] + + repoFile, err := models.GetTargetFile(params["username"], params["reponame"], + params["branchname"], params["commitid"], treename) + + if err != nil { + ctx.Handle(404, "repo.SingleDownload(GetTargetFile)", err) + return + } + + blob, err := repoFile.LookupBlob() + if err != nil { + ctx.Handle(404, "repo.SingleDownload(LookupBlob)", err) + return + } + + data := blob.Contents() + contentType, isTextFile := base.IsTextFile(data) + ctx.Res.Header().Set("Content-Type", contentType) + if !isTextFile { + ctx.Res.Header().Set("Content-Type", contentType) + ctx.Res.Header().Set("Content-Disposition", "attachment; filename="+filepath.Base(treename)) + ctx.Res.Header().Set("Content-Transfer-Encoding", "binary") + } + ctx.Res.Write(data) +} + func Http(ctx *middleware.Context, params martini.Params) { /*if !ctx.Repo.IsValid { return diff --git a/serve.go b/serve.go index 06815b65d..acdcb877f 100644 --- a/serve.go +++ b/serve.go @@ -13,6 +13,7 @@ import ( "os/exec" "strconv" "strings" + "time" "github.com/codegangsta/cli" "github.com/gogits/gogs/modules/log" @@ -45,7 +46,10 @@ gogs serv provide access auth for repositories`, } func init() { - log.NewLogger(10000, "file", fmt.Sprintf(`{"filename":"%s"}`, "log/serv.log")) + level := "0" + os.MkdirAll("log", os.ModePerm) + log.NewLogger(10000, "file", fmt.Sprintf(`{"level":%s,"filename":"%s"}`, level, "log/serv.log")) + log.Info("start logging...") } func parseCmd(cmd string) (string, string) { @@ -72,7 +76,6 @@ func runServ(k *cli.Context) { base.NewConfigContext() models.LoadModelsConfig() models.NewEngine() - base.NewLogService() keys := strings.Split(os.Args[2], "-") if len(keys) != 2 { @@ -109,25 +112,32 @@ func runServ(k *cli.Context) { repoName = repoName[:len(repoName)-4] } + isWrite := In(verb, COMMANDS_WRITE) + isRead := In(verb, COMMANDS_READONLY) + repo, err := models.GetRepositoryByName(user.Id, repoName) var isExist bool = true if err != nil { if err == models.ErrRepoNotExist { isExist = false + if isRead { + println("Repository", user.Name+"/"+repoName, "is not exist") + return + } } else { - println("Unavilable repository", err) + println("Get repository error:", err) + log.Error(err.Error()) return } } - isWrite := In(verb, COMMANDS_WRITE) - isRead := In(verb, COMMANDS_READONLY) - + // access check switch { case isWrite: has, err := models.HasAccess(user.Name, repoName, models.AU_WRITABLE) if err != nil { println("Inernel error:", err) + log.Error(err.Error()) return } if !has { @@ -138,12 +148,14 @@ func runServ(k *cli.Context) { has, err := models.HasAccess(user.Name, repoName, models.AU_READABLE) if err != nil { println("Inernel error") + log.Error(err.Error()) return } if !has { has, err = models.HasAccess(user.Name, repoName, models.AU_WRITABLE) if err != nil { println("Inernel error") + log.Error(err.Error()) return } } @@ -156,28 +168,30 @@ func runServ(k *cli.Context) { return } + var rep *git.Repository + repoPath := models.RepoPath(user.Name, repoName) if !isExist { - if isRead { - println("Repository", user.Name+"/"+repoName, "is not exist") - return - } else if isWrite { - _, err := models.CreateRepository(user, repoName, "", "", "", false, true) + if isWrite { + _, err = models.CreateRepository(user, repoName, "", "", "", false, true) if err != nil { println("Create repository failed") + log.Error(err.Error()) return } } } - rep, err := git.OpenRepository(models.RepoPath(user.Name, repoName)) - if err != nil { - println(err.Error()) - return - } + rep, err = git.OpenRepository(repoPath) + if err != nil { + println("OpenRepository failed:", err.Error()) + log.Error(err.Error()) + return + } refs, err := rep.AllReferencesMap() if err != nil { - println(err.Error()) + println("Get All References failed:", err.Error()) + log.Error(err.Error()) return } @@ -194,17 +208,17 @@ func runServ(k *cli.Context) { if err = gitcmd.Run(); err != nil { println("execute command error:", err.Error()) + log.Error(err.Error()) + return } - if !strings.HasPrefix(cmd, "git-receive-pack") { + if isRead { return } - // update - //w, _ := os.Create("serve.log") - //defer w.Close() - //log.SetOutput(w) + time.Sleep(time.Second) + // find push reference name var t = "ok refs/heads/" var i int var refname string @@ -220,24 +234,31 @@ func runServ(k *cli.Context) { refname = l[idx+len(t):] } } + if refname == "" { + println("No find any reference name:", b.String()) + return + } + var ref *git.Reference var ok bool - var l *list.List //log.Info("----", refname, "-----") if ref, ok = refs[refname]; !ok { + // for new branch refs, err = rep.AllReferencesMap() if err != nil { - println(err.Error()) + println("Get All References failed:", err.Error()) + log.Error(err.Error()) return } if ref, ok = refs[refname]; !ok { - log.Trace("unknow reference name -", refname, "-", b.String()) + log.Error("unknow reference name -", refname, "-", b.String()) return } l, err = ref.AllCommits() if err != nil { - println(err.Error()) + println("Get All Commits failed:", err.Error()) + log.Error(err.Error()) return } } else { @@ -246,20 +267,23 @@ func runServ(k *cli.Context) { //log.Info("00000", ref.Oid.String()) last, err = ref.LastCommit() if err != nil { - println(err.Error()) + println("Get last commit failed:", err.Error()) + log.Error(err.Error()) return } ref2, err := rep.LookupReference(ref.Name) if err != nil { - println(err.Error()) + println("look up reference failed:", err.Error()) + log.Error(err.Error()) return } //log.Info("11111", ref2.Oid.String()) before, err := ref2.LastCommit() if err != nil { - println(err.Error()) + println("Get last commit failed:", err.Error()) + log.Error(err.Error()) return } //log.Info("----", before.Id(), "-----", last.Id()) @@ -280,13 +304,8 @@ func runServ(k *cli.Context) { repo.Id, repoName, refname, &base.PushCommits{l.Len(), commits}); err != nil { log.Error("runUpdate.models.CommitRepoAction: %v", err, commits) } else { - //log.Info("refname", refname) - //log.Info("Listen: %v", cmd) - //fmt.Println("...", cmd) - - //runUpdate(k) c := exec.Command("git", "update-server-info") - c.Dir = models.RepoPath(user.Name, repoName) + c.Dir = repoPath err := c.Run() if err != nil { log.Error("update-server-info: %v", err) diff --git a/templates/base/navbar.tmpl b/templates/base/navbar.tmpl index 2c18b038a..90e90f296 100644 --- a/templates/base/navbar.tmpl +++ b/templates/base/navbar.tmpl @@ -12,7 +12,7 @@ {{if .IsAdmin}}{{end}} {{else}}Sign In - Sign Up{{end}} + Sign Up{{end}} diff --git a/templates/install.tmpl b/templates/install.tmpl new file mode 100644 index 000000000..a13f21908 --- /dev/null +++ b/templates/install.tmpl @@ -0,0 +1,70 @@ +{{template "base/head" .}} +
+
+ {{.CsrfTokenHtml}} +

Install Steps

+
{{.ErrorMsg}}
+

GoGits need MySQL or PostgreSQL server

+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +
+
+
+ +
+ +

Recommend use INNODB engine with utf8_general_ci charset.

+
+
+ +
+
+ +
+
+ +
+ +

General settings for GoGits

+ +
+ +
+ +

The git copy of each repository is saved in this directory.

+
+
+
+ +
+ +

The user has access to visit and run GoGits.

+
+
+
+ +
+
+{{template "base/footer" .}} \ No newline at end of file diff --git a/templates/issue/create.tmpl b/templates/issue/create.tmpl new file mode 100644 index 000000000..e71843e6f --- /dev/null +++ b/templates/issue/create.tmpl @@ -0,0 +1,10 @@ +{{template "base/head" .}} +{{template "base/navbar" .}} +{{template "repo/nav" .}} +{{template "repo/toolbar" .}} +
+
+ new-issues +
+
+{{template "base/footer" .}} \ No newline at end of file diff --git a/templates/repo/issues.tmpl b/templates/repo/issues.tmpl index daacc089c..bf23ff83f 100644 --- a/templates/repo/issues.tmpl +++ b/templates/repo/issues.tmpl @@ -4,6 +4,7 @@ {{template "repo/toolbar" .}}
+ issues
{{template "base/footer" .}} \ No newline at end of file diff --git a/templates/repo/nav.tmpl b/templates/repo/nav.tmpl index 080cb72e5..2770328cb 100644 --- a/templates/repo/nav.tmpl +++ b/templates/repo/nav.tmpl @@ -30,6 +30,10 @@

Need help cloning? Visit Help!

+
+
+ Download ZIP +
diff --git a/templates/repo/single.tmpl b/templates/repo/single.tmpl index a1c3cfbb2..56e6746c7 100644 --- a/templates/repo/single.tmpl +++ b/templates/repo/single.tmpl @@ -9,7 +9,7 @@ {{else}}
{{ $n := len .Treenames}} - {{if not .IsFile}}{{end}} + {{if not .IsFile}}{{end}}