var ( | var ( | ||||
ErrRepoAlreadyExist = errors.New("Repository already exist") | ErrRepoAlreadyExist = errors.New("Repository already exist") | ||||
ErrRepoNotExist = errors.New("Repository does not exist") | ErrRepoNotExist = errors.New("Repository does not exist") | ||||
ErrRepoFileNotExist = errors.New("Target Repo file does not exist") | |||||
) | ) | ||||
func init() { | func init() { | ||||
return brs, nil | return brs, nil | ||||
} | } | ||||
func GetTargetFile(userName, reposName, branchName, commitId, rpath string) (*RepoFile, error) { | |||||
repo, err := git.OpenRepository(RepoPath(userName, reposName)) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
commit, err := repo.GetCommit(branchName, commitId) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
parts := strings.Split(path.Clean(rpath), "/") | |||||
var entry *git.TreeEntry | |||||
tree := commit.Tree | |||||
for i, part := range parts { | |||||
if i == len(parts)-1 { | |||||
entry = tree.EntryByName(part) | |||||
if entry == nil { | |||||
return nil, ErrRepoFileNotExist | |||||
} | |||||
} else { | |||||
tree, err = repo.SubTree(tree, part) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
} | |||||
} | |||||
size, err := repo.ObjectSize(entry.Id) | |||||
if err != nil { | |||||
return nil, err | |||||
} | |||||
repoFile := &RepoFile{ | |||||
entry, | |||||
rpath, | |||||
size, | |||||
repo, | |||||
commit, | |||||
} | |||||
return repoFile, nil | |||||
} | |||||
// GetReposFiles returns a list of file object in given directory of repository. | // GetReposFiles returns a list of file object in given directory of repository. | ||||
func GetReposFiles(userName, reposName, branchName, commitId, rpath string) ([]*RepoFile, error) { | func GetReposFiles(userName, reposName, branchName, commitId, rpath string) ([]*RepoFile, error) { | ||||
repo, err := git.OpenRepository(RepoPath(userName, reposName)) | repo, err := git.OpenRepository(RepoPath(userName, reposName)) |
import ( | import ( | ||||
"bytes" | "bytes" | ||||
"path" | "path" | ||||
"path/filepath" | |||||
"strings" | |||||
"github.com/gogits/gfm" | "github.com/gogits/gfm" | ||||
) | ) | ||||
return false | return false | ||||
} | } | ||||
func IsMarkdownFile(name string) bool { | |||||
name = strings.ToLower(name) | |||||
switch filepath.Ext(name) { | |||||
case "md", "markdown": | |||||
return true | |||||
} | |||||
return false | |||||
} | |||||
func IsReadmeFile(name string) bool { | |||||
name = strings.ToLower(name) | |||||
if len(name) < 6 { | |||||
return false | |||||
} | |||||
if name[:6] == "readme" { | |||||
return true | |||||
} | |||||
return false | |||||
} | |||||
type CustomRender struct { | type CustomRender struct { | ||||
gfm.Renderer | gfm.Renderer | ||||
urlPrefix string | urlPrefix string |
package repo | package repo | ||||
import ( | import ( | ||||
"fmt" | |||||
"strings" | "strings" | ||||
"github.com/codegangsta/martini" | "github.com/codegangsta/martini" | ||||
return | return | ||||
} | } | ||||
if params["branchname"] == "" { | |||||
if len(params["branchname"]) == 0 { | |||||
params["branchname"] = "master" | params["branchname"] = "master" | ||||
} | } | ||||
if len(treename) > 0 && treename[len(treename)-1] == '/' { | if len(treename) > 0 && treename[len(treename)-1] == '/' { | ||||
ctx.Redirect("/"+ctx.Repo.Owner.LowerName+"/"+ | ctx.Redirect("/"+ctx.Repo.Owner.LowerName+"/"+ | ||||
ctx.Repo.Repository.Name+"/tree/"+params["branchname"]+"/"+treename[:len(treename)-1], 302) | |||||
ctx.Repo.Repository.Name+"/src/"+params["branchname"]+"/"+treename[:len(treename)-1], 302) | |||||
return | return | ||||
} | } | ||||
ctx.Data["Branches"] = brs | ctx.Data["Branches"] = brs | ||||
// Directory and file list. | |||||
files, err := models.GetReposFiles(params["username"], params["reponame"], | |||||
repoFile, err := models.GetTargetFile(params["username"], params["reponame"], | |||||
params["branchname"], params["commitid"], treename) | params["branchname"], params["commitid"], treename) | ||||
if err != nil { | |||||
log.Error("repo.Single(GetReposFiles): %v", err) | |||||
if err != nil && err != models.ErrRepoFileNotExist { | |||||
log.Error("repo.Single(GetTargetFile): %v", err) | |||||
ctx.Error(404) | ctx.Error(404) | ||||
return | return | ||||
} | } | ||||
branchLink := "/" + ctx.Repo.Owner.LowerName + "/" + ctx.Repo.Repository.Name + "/src/" + params["branchname"] | |||||
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) | |||||
ctx.Error(404) | |||||
} else { | |||||
ctx.Data["IsFile"] = true | |||||
ctx.Data["FileName"] = repoFile.Name | |||||
readmeExist := base.IsMarkdownFile(repoFile.Name) || base.IsReadmeFile(repoFile.Name) | |||||
ctx.Data["ReadmeExist"] = readmeExist | |||||
if readmeExist { | |||||
ctx.Data["FileContent"] = string(base.RenderMarkdown(blob.Contents(), "")) | |||||
} else { | |||||
ctx.Data["FileContent"] = string(blob.Contents()) | |||||
} | |||||
} | |||||
} else { | |||||
// Directory and file list. | |||||
files, err := models.GetReposFiles(params["username"], params["reponame"], | |||||
params["branchname"], params["commitid"], treename) | |||||
if err != nil { | |||||
log.Error("repo.Single(GetReposFiles): %v", err) | |||||
ctx.Error(404) | |||||
return | |||||
} | |||||
ctx.Data["Files"] = files | |||||
var readmeFile *models.RepoFile | |||||
for _, f := range files { | |||||
if !f.IsFile() || !base.IsReadmeFile(f.Name) { | |||||
continue | |||||
} else { | |||||
readmeFile = f | |||||
break | |||||
} | |||||
} | |||||
if readmeFile != nil { | |||||
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 { | |||||
log.Error("repo.Single(readmeFile.LookupBlob): %v", err) | |||||
ctx.Error(404) | |||||
return | |||||
} else { | |||||
// current repo branch link | |||||
urlPrefix := "http://" + base.Domain + branchLink | |||||
ctx.Data["FileName"] = readmeFile.Name | |||||
ctx.Data["FileContent"] = string(base.RenderMarkdown(blob.Contents(), urlPrefix)) | |||||
} | |||||
} | |||||
} | |||||
ctx.Data["Username"] = params["username"] | ctx.Data["Username"] = params["username"] | ||||
ctx.Data["Reponame"] = params["reponame"] | ctx.Data["Reponame"] = params["reponame"] | ||||
ctx.Data["Branchname"] = params["branchname"] | ctx.Data["Branchname"] = params["branchname"] | ||||
} | } | ||||
ctx.Data["LastCommit"] = commit | ctx.Data["LastCommit"] = commit | ||||
var readmeFile *models.RepoFile | |||||
for _, f := range files { | |||||
if !f.IsFile() || len(f.Name) < 6 { | |||||
continue | |||||
} else if strings.ToLower(f.Name[:6]) == "readme" { | |||||
readmeFile = f | |||||
break | |||||
} | |||||
} | |||||
if readmeFile != nil { | |||||
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 { | |||||
ctx.Data["ReadmeExist"] = false | |||||
} else { | |||||
// current repo branch link | |||||
urlPrefix := "http://" + base.Domain + "/" + ctx.Repo.Owner.LowerName + "/" + | |||||
ctx.Repo.Repository.Name + "/tree/" + params["branchname"] | |||||
ctx.Data["ReadmeContent"] = string(base.RenderMarkdown(blob.Contents(), urlPrefix)) | |||||
} | |||||
} | |||||
fmt.Println(Paths) | |||||
ctx.Data["Paths"] = Paths | ctx.Data["Paths"] = Paths | ||||
ctx.Data["Treenames"] = treenames | ctx.Data["Treenames"] = treenames | ||||
ctx.Data["IsRepoToolbarSource"] = true | ctx.Data["IsRepoToolbarSource"] = true | ||||
ctx.Data["Files"] = files | |||||
ctx.Data["BranchLink"] = branchLink | |||||
ctx.HTML(200, "repo/single", ctx.Data) | ctx.HTML(200, "repo/single", ctx.Data) | ||||
} | } | ||||
Need to fill in some guide. | Need to fill in some guide. | ||||
{{else}} | {{else}} | ||||
<div class="source-toolbar"> | <div class="source-toolbar"> | ||||
{{ $username := .Username}} | |||||
{{ $reponame := .Reponame}} | |||||
{{ $branchname := .Branchname}} | |||||
{{ $treenames := .Treenames}} | |||||
{{ $repoLink := .RepositoryLink}} | |||||
{{ $n := len $treenames}} | |||||
<button class="btn btn-default pull-right"><i class="fa fa-plus-square"></i>Add File</button> | |||||
{{ $n := len .Treenames}} | |||||
{{if not .IsFile}}<button class="btn btn-default pull-right"><i class="fa fa-plus-square"></i>Add File</button>{{end}} | |||||
<div class="dropdown branch-switch"> | <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>{{.Branchname}} | |||||
<b class="caret"></b></a> | <b class="caret"></b></a> | ||||
<ul class="dropdown-menu"> | <ul class="dropdown-menu"> | ||||
{{range .Branches}} | {{range .Branches}} | ||||
<li><a {{if eq . $branchname}}class="current" {{end}}href="/{{$repoLink}}/tree/{{.}}">{{.}}</a></li> | |||||
<li><a {{if eq . $.Branchname}}class="current" {{end}}href="{{$.BranchLink}}">{{.}}</a></li> | |||||
{{end}} | {{end}} | ||||
</ul> | </ul> | ||||
</div> | </div> | ||||
{{$paths := .Paths}} | |||||
{{ $l := Subtract $n 1}} | {{ $l := Subtract $n 1}} | ||||
<ol class="breadcrumb"> | <ol class="breadcrumb"> | ||||
<li class="root dir"> | <li class="root dir"> | ||||
<a href="/{{$username}}/{{$reponame}}/tree/{{$branchname}}">{{.Repository.Name}}</a></li> | |||||
{{range $i, $v := $treenames}} | |||||
<a href="{{.BranchLink}}">{{.Repository.Name}}</a></li> | |||||
{{range $i, $v := .Treenames}} | |||||
<li class="dir"> | <li class="dir"> | ||||
{{if eq $i $l}}{{$v}} | {{if eq $i $l}}{{$v}} | ||||
{{else}} | {{else}} | ||||
<a href="/{{$username}}/{{$reponame}}/tree/{{$branchname}}/{{index $paths $i}}">{{$v}}</a> | |||||
<a href="{{$.BranchLink}}/{{index $.Paths $i}}">{{$v}}</a> | |||||
{{end}} | {{end}} | ||||
</li> | </li> | ||||
{{end}} | {{end}} | ||||
</ol> | </ol> | ||||
</div> | </div> | ||||
<div class="panel panel-default info-box"> | |||||
<div class="panel-heading info-head"> | |||||
<a href="/{{$username}}/{{$reponame}}/commit/{{.LastCommit.Oid.String}}">{{.LastCommit.Message}}</a> | |||||
</div> | |||||
<div class="panel-body info-content"> | |||||
<a href="/user/{{.LastCommit.Author.Name}}">{{.LastCommit.Author.Name}}</a> <span class="text-muted">{{TimeSince .LastCommit.Author.When}}</span> | |||||
</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> | |||||
{{if .HasParentPath}} | |||||
<tr class="has-parent"> | |||||
<td class="icon"><a href="/{{$username}}/{{$reponame}}/tree/{{$branchname}}{{.ParentPath}}"><i class="fa fa-reply"></i></a></td> | |||||
<td class="name"><a href="/{{$username}}/{{$reponame}}/tree/{{$branchname}}{{.ParentPath}}">..</a></td> | |||||
<td class="text"></td> | |||||
<td class="date"></td> | |||||
</tr> | |||||
{{end}} | |||||
{{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"><a href="/{{$username}}/{{$reponame}}/commit/{{.Commit.Oid}}">{{.Commit.Message}}</a></span> | |||||
</td> | |||||
<td class="date"> | |||||
<span class="wrap">{{TimeSince .Commit.Committer.When}}</span> | |||||
</td> | |||||
</tr> | |||||
{{end}} | |||||
</tbody> | |||||
</table> | |||||
</div> | |||||
{{if .ReadmeExist}} | |||||
<div class="panel panel-default file-content"> | |||||
<div class="panel-heading file-head"> | |||||
<i class="icon fa fa-book"></i> README.md | |||||
</div> | |||||
{{if .FileIsLarge}} | |||||
<div class="panel-footer"> | |||||
Large file size 1000kb | |||||
</div> | |||||
{{else}} | |||||
<div class="panel-body file-body markdown"> | |||||
{{.ReadmeContent|str2html}} | |||||
</div> | |||||
{{end}} | |||||
</div> | |||||
{{if .IsFile}} | |||||
{{template "repo/single_file" .}} | |||||
{{else}} | |||||
{{template "repo/single_list" .}} | |||||
{{end}} | {{end}} | ||||
{{end}} | {{end}} | ||||
</div> | </div> |
<div class="panel panel-default file-content"> | |||||
<div class="panel-heading file-head"> | |||||
<i class="icon fa fa-book"></i> {{.FileName}} | |||||
</div> | |||||
{{if .FileIsLarge}} | |||||
<div class="panel-footer"> | |||||
Large file size 1000kb | |||||
</div> | |||||
{{else}} | |||||
{{if .ReadmeExist}} | |||||
<div class="panel-body file-body markdown"> | |||||
{{.FileContent|str2html}} | |||||
</div> | |||||
{{else}} | |||||
<div class="panel-body file-body markdown"> | |||||
<pre><code>{{.FileContent|str2html}}</code></pre> | |||||
</div> | |||||
{{end}} | |||||
{{end}} | |||||
</div> |
<div class="panel panel-default info-box"> | |||||
<div class="panel-heading info-head"> | |||||
<a href="/{{.Username}}/{{.Reponame}}/commit/{{.LastCommit.Oid.String}}">{{.LastCommit.Message}}</a> | |||||
</div> | |||||
<div class="panel-body info-content"> | |||||
<a href="/user/{{.LastCommit.Author.Name}}">{{.LastCommit.Author.Name}}</a> <span class="text-muted">{{TimeSince .LastCommit.Author.When}}</span> | |||||
</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> | |||||
{{if .HasParentPath}} | |||||
<tr class="has-parent"> | |||||
<td class="icon"><a href="{{.BranchLink}}{{.ParentPath}}"><i class="fa fa-reply"></i></a></td> | |||||
<td class="name"><a href="{{.BranchLink}}{{.ParentPath}}">..</a></td> | |||||
<td class="text"></td> | |||||
<td class="date"></td> | |||||
</tr> | |||||
{{end}} | |||||
{{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="{{$.BranchLink}}/{{.Path}}">{{.Name}}</a> | |||||
{{else}} | |||||
<a href="{{$.BranchLink}}/{{.Path}}">{{.Name}}</a> | |||||
{{end}} | |||||
</span> | |||||
</td> | |||||
<td class="text"> | |||||
<span class="wrap"><a href="/{{$.Username}}/{{$.Reponame}}/commit/{{.Commit.Oid}}">{{.Commit.Message}}</a></span> | |||||
</td> | |||||
<td class="date"> | |||||
<span class="wrap">{{TimeSince .Commit.Committer.When}}</span> | |||||
</td> | |||||
</tr> | |||||
{{end}} | |||||
</tbody> | |||||
</table> | |||||
</div> | |||||
{{if .ReadmeExist}} | |||||
{{template "repo/single_file" .}} | |||||
{{end}} |
m.Get("/:username/:reponame/pulls", ignSignIn, middleware.RepoAssignment(true), repo.Pulls) | m.Get("/:username/:reponame/pulls", ignSignIn, middleware.RepoAssignment(true), repo.Pulls) | ||||
m.Get("/:username/:reponame/branches", ignSignIn, middleware.RepoAssignment(true), repo.Branches) | m.Get("/:username/:reponame/branches", ignSignIn, middleware.RepoAssignment(true), repo.Branches) | ||||
m.Get("/:username/:reponame/action/:action", reqSignIn, middleware.RepoAssignment(true), repo.Action) | m.Get("/:username/:reponame/action/:action", reqSignIn, middleware.RepoAssignment(true), repo.Action) | ||||
m.Get("/:username/:reponame/tree/:branchname/**", | |||||
m.Get("/:username/:reponame/src/:branchname/**", | |||||
ignSignIn, middleware.RepoAssignment(true), repo.Single) | ignSignIn, middleware.RepoAssignment(true), repo.Single) | ||||
m.Get("/:username/:reponame/tree/:branchname", | |||||
m.Get("/:username/:reponame/src/:branchname", | |||||
ignSignIn, middleware.RepoAssignment(true), repo.Single) | ignSignIn, middleware.RepoAssignment(true), repo.Single) | ||||
m.Get("/:username/:reponame/commit/:commitid/**", ignSignIn, middleware.RepoAssignment(true), repo.Single) | m.Get("/:username/:reponame/commit/:commitid/**", ignSignIn, middleware.RepoAssignment(true), repo.Single) | ||||
m.Get("/:username/:reponame/commit/:commitid", ignSignIn, middleware.RepoAssignment(true), repo.Single) | m.Get("/:username/:reponame/commit/:commitid", ignSignIn, middleware.RepoAssignment(true), repo.Single) |