diff --git a/models/repo.go b/models/repo.go index edf800bd7..4b6dedaf9 100644 --- a/models/repo.go +++ b/models/repo.go @@ -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)) diff --git a/modules/base/markdown.go b/modules/base/markdown.go index 049b10754..a9f4cbf0d 100644 --- a/modules/base/markdown.go +++ b/modules/base/markdown.go @@ -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 diff --git a/routers/repo/single.go b/routers/repo/single.go index 141a6cdae..c10d30a7d 100644 --- a/routers/repo/single.go +++ b/routers/repo/single.go @@ -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) } diff --git a/templates/repo/single.tmpl b/templates/repo/single.tmpl index e18f79c29..60247898e 100644 --- a/templates/repo/single.tmpl +++ b/templates/repo/single.tmpl @@ -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> diff --git a/templates/repo/single_file.tmpl b/templates/repo/single_file.tmpl new file mode 100644 index 000000000..f74e09de0 --- /dev/null +++ b/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> \ No newline at end of file diff --git a/templates/repo/single_list.tmpl b/templates/repo/single_list.tmpl new file mode 100644 index 000000000..b0c31d1e4 --- /dev/null +++ b/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}} \ No newline at end of file diff --git a/web.go b/web.go index f793518a1..ceb193e6f 100644 --- a/web.go +++ b/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/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)