Conflicts: routers/repo/single.gotags/v1.2.0-rc1
| @@ -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)) | |||
| } | |||
| } | |||
| log.Trace("repo.Single: Paths: %s", strings.Join(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) | |||