Browse Source

read single file

pull/33/head
slene 11 years ago
parent
commit
24678d73f5
  1. 46
      models/repo.go
  2. 22
      modules/base/markdown.go
  3. 100
      routers/repo/single.go
  4. 89
      templates/repo/single.tmpl
  5. 20
      templates/repo/single_file.tmpl
  6. 54
      templates/repo/single_list.tmpl
  7. 4
      web.go

46
models/repo.go

@ -81,6 +81,7 @@ var (
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() {
@ -463,6 +464,51 @@ func GetBranches(userName, reposName string) ([]string, error) {
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))

22
modules/base/markdown.go

@ -7,6 +7,8 @@ package base
import ( import (
"bytes" "bytes"
"path" "path"
"path/filepath"
"strings"
"github.com/gogits/gfm" "github.com/gogits/gfm"
) )
@ -31,6 +33,26 @@ func isLink(link []byte) bool {
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

100
routers/repo/single.go

@ -5,7 +5,6 @@
package repo package repo
import ( import (
"fmt"
"strings" "strings"
"github.com/codegangsta/martini" "github.com/codegangsta/martini"
@ -47,7 +46,7 @@ func Single(ctx *middleware.Context, params martini.Params) {
return return
} }
if params["branchname"] == "" { if len(params["branchname"]) == 0 {
params["branchname"] = "master" params["branchname"] = "master"
} }
@ -56,7 +55,7 @@ func Single(ctx *middleware.Context, params martini.Params) {
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
} }
@ -74,49 +73,54 @@ func Single(ctx *middleware.Context, params martini.Params) {
ctx.Data["Branches"] = brs ctx.Data["Branches"] = brs
// Directory and file list. repoFile, err := models.GetTargetFile(params["username"], params["reponame"],
files, err := models.GetReposFiles(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
} }
ctx.Data["Username"] = params["username"]
ctx.Data["Reponame"] = params["reponame"]
ctx.Data["Branchname"] = params["branchname"]
var treenames []string branchLink := "/" + ctx.Repo.Owner.LowerName + "/" + ctx.Repo.Repository.Name + "/src/" + params["branchname"]
Paths := make([]string, 0)
if len(treename) > 0 { if repoFile != nil && repoFile.IsFile() {
treenames = strings.Split(treename, "/") if repoFile.Size > 1024*1024 || repoFile.Filemode != git.FileModeBlob {
for i, _ := range treenames { ctx.Data["FileIsLarge"] = true
Paths = append(Paths, strings.Join(treenames[0:i+1], "/")) } 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
ctx.Data["HasParentPath"] = true readmeExist := base.IsMarkdownFile(repoFile.Name) || base.IsReadmeFile(repoFile.Name)
if len(Paths)-2 >= 0 { ctx.Data["ReadmeExist"] = readmeExist
ctx.Data["ParentPath"] = "/" + Paths[len(Paths)-2] if readmeExist {
ctx.Data["FileContent"] = string(base.RenderMarkdown(blob.Contents(), ""))
} else {
ctx.Data["FileContent"] = string(blob.Contents())
} }
} }
// Get latest commit according username and repo name } else {
commit, err := models.GetCommit(params["username"], params["reponame"], // Directory and file list.
params["branchname"], params["commitid"]) files, err := models.GetReposFiles(params["username"], params["reponame"],
params["branchname"], params["commitid"], treename)
if err != nil { if err != nil {
log.Error("repo.Single(GetCommit): %v", err) log.Error("repo.Single(GetReposFiles): %v", err)
ctx.Error(404) ctx.Error(404)
return return
} }
ctx.Data["LastCommit"] = commit
ctx.Data["Files"] = files
var readmeFile *models.RepoFile var readmeFile *models.RepoFile
for _, f := range files { for _, f := range files {
if !f.IsFile() || len(f.Name) < 6 { if !f.IsFile() || !base.IsReadmeFile(f.Name) {
continue continue
} else if strings.ToLower(f.Name[:6]) == "readme" { } else {
readmeFile = f readmeFile = f
break break
} }
@ -128,22 +132,52 @@ func Single(ctx *middleware.Context, params martini.Params) {
if readmeFile.Size > 1024*1024 || readmeFile.Filemode != git.FileModeBlob { if readmeFile.Size > 1024*1024 || readmeFile.Filemode != git.FileModeBlob {
ctx.Data["FileIsLarge"] = true ctx.Data["FileIsLarge"] = true
} else if blob, err := readmeFile.LookupBlob(); err != nil { } else if blob, err := readmeFile.LookupBlob(); err != nil {
ctx.Data["ReadmeExist"] = false log.Error("repo.Single(readmeFile.LookupBlob): %v", err)
ctx.Error(404)
return
} else { } else {
// current repo branch link // current repo branch link
urlPrefix := "http://" + base.Domain + "/" + ctx.Repo.Owner.LowerName + "/" + urlPrefix := "http://" + base.Domain + branchLink
ctx.Repo.Repository.Name + "/tree/" + params["branchname"]
ctx.Data["ReadmeContent"] = string(base.RenderMarkdown(blob.Contents(), urlPrefix)) ctx.Data["FileName"] = readmeFile.Name
ctx.Data["FileContent"] = string(base.RenderMarkdown(blob.Contents(), urlPrefix))
}
} }
} }
fmt.Println(Paths) ctx.Data["Username"] = params["username"]
ctx.Data["Reponame"] = params["reponame"]
ctx.Data["Branchname"] = params["branchname"]
var treenames []string
Paths := make([]string, 0)
if len(treename) > 0 {
treenames = strings.Split(treename, "/")
for i, _ := range treenames {
Paths = append(Paths, strings.Join(treenames[0:i+1], "/"))
}
ctx.Data["HasParentPath"] = true
if len(Paths)-2 >= 0 {
ctx.Data["ParentPath"] = "/" + Paths[len(Paths)-2]
}
}
// Get latest commit according username and repo name
commit, err := models.GetCommit(params["username"], params["reponame"],
params["branchname"], params["commitid"])
if err != nil {
log.Error("repo.Single(GetCommit): %v", err)
ctx.Error(404)
return
}
ctx.Data["LastCommit"] = commit
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)
} }

89
templates/repo/single.tmpl

@ -8,104 +8,35 @@
Need to fill in some guide. Need to fill in some guide.
{{else}} {{else}}
<div class="source-toolbar"> <div class="source-toolbar">
{{ $username := .Username}} {{ $n := len .Treenames}}
{{ $reponame := .Reponame}} {{if not .IsFile}}<button class="btn btn-default pull-right"><i class="fa fa-plus-square"></i>Add File</button>{{end}}
{{ $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>
<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}}&nbsp;&nbsp; <a href="#" class="btn btn-success dropdown-toggle" data-toggle="dropdown"><i class="fa fa-chain"></i>{{.Branchname}}&nbsp;&nbsp;
<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> <a href="{{.BranchLink}}">{{.Repository.Name}}</a></li>
{{range $i, $v := $treenames}} {{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>&nbsp; <a href="{{$.BranchLink}}/{{index $.Paths $i}}">{{$v}}</a>&nbsp;
{{end}} {{end}}
</li> </li>
{{end}} {{end}}
</ol> </ol>
</div> </div>
{{if .IsFile}}
<div class="panel panel-default info-box"> {{template "repo/single_file" .}}
<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}} {{else}}
<div class="panel-body file-body markdown"> {{template "repo/single_list" .}}
{{.ReadmeContent|str2html}}
</div>
{{end}}
</div>
{{end}} {{end}}
{{end}} {{end}}
</div> </div>

20
templates/repo/single_file.tmpl

@ -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>

54
templates/repo/single_list.tmpl

@ -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}}

4
web.go

@ -107,9 +107,9 @@ func runWeb(*cli.Context) {
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)

Loading…
Cancel
Save