@@ -81,6 +81,7 @@ var ( | |||
var ( | |||
ErrRepoAlreadyExist = errors.New("Repository already exist") | |||
ErrRepoNotExist = errors.New("Repository does not exist") | |||
ErrRepoFileNotExist = errors.New("Target Repo file does not exist") | |||
) | |||
func init() { | |||
@@ -463,6 +464,51 @@ 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)) | |||
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. | |||
func GetReposFiles(userName, reposName, branchName, commitId, rpath string) ([]*RepoFile, error) { | |||
repo, err := git.OpenRepository(RepoPath(userName, reposName)) |
@@ -7,6 +7,8 @@ package base | |||
import ( | |||
"bytes" | |||
"path" | |||
"path/filepath" | |||
"strings" | |||
"github.com/gogits/gfm" | |||
) | |||
@@ -31,6 +33,26 @@ func isLink(link []byte) bool { | |||
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 { | |||
gfm.Renderer | |||
urlPrefix string |
@@ -5,7 +5,6 @@ | |||
package repo | |||
import ( | |||
"fmt" | |||
"strings" | |||
"github.com/codegangsta/martini" | |||
@@ -47,7 +46,7 @@ func Single(ctx *middleware.Context, params martini.Params) { | |||
return | |||
} | |||
if params["branchname"] == "" { | |||
if len(params["branchname"]) == 0 { | |||
params["branchname"] = "master" | |||
} | |||
@@ -56,7 +55,7 @@ func Single(ctx *middleware.Context, params martini.Params) { | |||
if len(treename) > 0 && treename[len(treename)-1] == '/' { | |||
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 | |||
} | |||
@@ -74,14 +73,78 @@ func Single(ctx *middleware.Context, params martini.Params) { | |||
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) | |||
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) | |||
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["Reponame"] = params["reponame"] | |||
ctx.Data["Branchname"] = params["branchname"] | |||
@@ -111,39 +174,10 @@ func Single(ctx *middleware.Context, params martini.Params) { | |||
} | |||
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["Treenames"] = treenames | |||
ctx.Data["IsRepoToolbarSource"] = true | |||
ctx.Data["Files"] = files | |||
ctx.Data["BranchLink"] = branchLink | |||
ctx.HTML(200, "repo/single", ctx.Data) | |||
} | |||
@@ -8,104 +8,35 @@ | |||
Need to fill in some guide. | |||
{{else}} | |||
<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"> | |||
<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> | |||
<ul class="dropdown-menu"> | |||
{{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}} | |||
</ul> | |||
</div> | |||
{{$paths := .Paths}} | |||
{{ $l := Subtract $n 1}} | |||
<ol class="breadcrumb"> | |||
<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"> | |||
{{if eq $i $l}}{{$v}} | |||
{{else}} | |||
<a href="/{{$username}}/{{$reponame}}/tree/{{$branchname}}/{{index $paths $i}}">{{$v}}</a> | |||
<a href="{{$.BranchLink}}/{{index $.Paths $i}}">{{$v}}</a> | |||
{{end}} | |||
</li> | |||
{{end}} | |||
</ol> | |||
</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}} | |||
</div> |
@@ -0,0 +1,20 @@ | |||
<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> |
@@ -0,0 +1,54 @@ | |||
<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}} |
@@ -107,9 +107,9 @@ func runWeb(*cli.Context) { | |||
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/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) | |||
m.Get("/:username/:reponame/tree/:branchname", | |||
m.Get("/:username/:reponame/src/:branchname", | |||
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) |