@ -7,7 +7,6 @@ package repo
import (
import (
"fmt"
"fmt"
"io/ioutil"
"io/ioutil"
"net/url"
"path/filepath"
"path/filepath"
"strings"
"strings"
"time"
"time"
@ -47,140 +46,30 @@ func MustEnableWiki(ctx *context.Context) {
// PageMeta wiki page meat information
// PageMeta wiki page meat information
type PageMeta struct {
type PageMeta struct {
Name string
Name string
URL string
Sub URL string
Updated time . Time
Updated time . Time
}
}
func urlEncoded ( str string ) string {
// findEntryForFile finds the tree entry for a target filepath.
u , err := url . Parse ( str )
func findEntryForFile ( commit * git . Commit , target string ) ( * git . TreeEntry , error ) {
if err != nil {
return str
}
return u . String ( )
}
func urlDecoded ( str string ) string {
res , err := url . QueryUnescape ( str )
if err != nil {
return str
}
return res
}
// commitTreeBlobEntry processes found file and checks if it matches search target
func commitTreeBlobEntry ( entry * git . TreeEntry , path string , targets [ ] string , textOnly bool ) * git . TreeEntry {
name := entry . Name ( )
ext := filepath . Ext ( name )
if ! textOnly || markdown . IsMarkdownFile ( name ) || ext == ".textile" {
for _ , target := range targets {
if matchName ( path , target ) || matchName ( urlEncoded ( path ) , target ) || matchName ( urlDecoded ( path ) , target ) {
return entry
}
pathNoExt := strings . TrimSuffix ( path , ext )
if matchName ( pathNoExt , target ) || matchName ( urlEncoded ( pathNoExt ) , target ) || matchName ( urlDecoded ( pathNoExt ) , target ) {
return entry
}
}
}
return nil
}
// commitTreeDirEntry is a recursive file tree traversal function
func commitTreeDirEntry ( repo * git . Repository , commit * git . Commit , entries [ ] * git . TreeEntry , prevPath string , targets [ ] string , textOnly bool ) ( * git . TreeEntry , error ) {
for i := range entries {
entry := entries [ i ]
var path string
if len ( prevPath ) == 0 {
path = entry . Name ( )
} else {
path = prevPath + "/" + entry . Name ( )
}
if entry . Type == git . ObjectBlob {
// File
if res := commitTreeBlobEntry ( entry , path , targets , textOnly ) ; res != nil {
return res , nil
}
} else if entry . IsDir ( ) {
// Directory
// Get our tree entry, handling all possible errors
var err error
var tree * git . Tree
if tree , err = repo . GetTree ( entry . ID . String ( ) ) ; tree == nil || err != nil {
if err == nil {
err = fmt . Errorf ( "repo.GetTree(%s) => nil" , entry . ID . String ( ) )
}
return nil , err
}
// Found us, get children entries
var ls git . Entries
if ls , err = tree . ListEntries ( ) ; err != nil {
return nil , err
}
// Call itself recursively to find needed entry
var te * git . TreeEntry
if te , err = commitTreeDirEntry ( repo , commit , ls , path , targets , textOnly ) ; err != nil {
return nil , err
}
if te != nil {
return te , nil
}
}
}
return nil , nil
}
// commitTreeEntry is a first step of commitTreeDirEntry, which should be never called directly
func commitTreeEntry ( repo * git . Repository , commit * git . Commit , targets [ ] string , textOnly bool ) ( * git . TreeEntry , error ) {
entries , err := commit . ListEntries ( )
entries , err := commit . ListEntries ( )
if err != nil {
if err != nil {
return nil , err
return nil , err
}
}
return commitTreeDirEntry ( repo , commit , entries , "" , targets , textOnly )
for _ , entry := range entries {
}
if entry . Type == git . ObjectBlob && entry . Name ( ) == target {
// findFile finds the best match for given filename in repo file tree
func findFile ( repo * git . Repository , commit * git . Commit , target string , textOnly bool ) ( * git . TreeEntry , error ) {
targets := [ ] string { target , urlEncoded ( target ) , urlDecoded ( target ) }
var entry * git . TreeEntry
var err error
if entry , err = commitTreeEntry ( repo , commit , targets , textOnly ) ; err != nil {
return nil , err
}
return entry , nil
return entry , nil
}
// matchName matches generic name representation of the file with required one
func matchName ( target , name string ) bool {
if len ( target ) != len ( name ) {
return false
}
name = strings . ToLower ( name )
target = strings . ToLower ( target )
if name == target {
return true
}
target = strings . Replace ( target , " " , "?" , - 1 )
target = strings . Replace ( target , "-" , "?" , - 1 )
for i := range name {
ch := name [ i ]
reqCh := target [ i ]
if ch != reqCh {
if string ( reqCh ) != "?" {
return false
}
}
}
}
}
return nil , nil
return true
}
}
func findWikiRepoCommit ( ctx * context . Context ) ( * git . Repository , * git . Commit , error ) {
func findWikiRepoCommit ( ctx * context . Context ) ( * git . Repository , * git . Commit , error ) {
wikiRepo , err := git . OpenRepository ( ctx . Repo . Repository . WikiPath ( ) )
wikiRepo , err := git . OpenRepository ( ctx . Repo . Repository . WikiPath ( ) )
if err != nil {
if err != nil {
// ctx.Handle(500, "OpenRepository", err)
ctx . Handle ( 500 , "OpenRepository" , err )
return nil , nil , err
return nil , nil , err
}
}
if ! wikiRepo . IsBranchExist ( "master" ) {
return wikiRepo , nil , nil
}
commit , err := wikiRepo . GetBranchCommit ( "master" )
commit , err := wikiRepo . GetBranchCommit ( "master" )
if err != nil {
if err != nil {
@ -190,14 +79,40 @@ func findWikiRepoCommit(ctx *context.Context) (*git.Repository, *git.Commit, err
return wikiRepo , commit , nil
return wikiRepo , commit , nil
}
}
// wikiContentsByEntry returns the contents of the wiki page referenced by the
// given tree entry. Writes to ctx if an error occurs.
func wikiContentsByEntry ( ctx * context . Context , entry * git . TreeEntry ) [ ] byte {
reader , err := entry . Blob ( ) . Data ( )
if err != nil {
ctx . Handle ( 500 , "Blob.Data" , err )
return nil
}
content , err := ioutil . ReadAll ( reader )
if err != nil {
ctx . Handle ( 500 , "ReadAll" , err )
return nil
}
return content
}
// wikiContentsByName returns the contents of a wiki page, along with a boolean
// indicating whether the page exists. Writes to ctx if an error occurs.
func wikiContentsByName ( ctx * context . Context , commit * git . Commit , wikiName string ) ( [ ] byte , bool ) {
entry , err := findEntryForFile ( commit , models . WikiNameToFilename ( wikiName ) )
if err != nil {
ctx . Handle ( 500 , "findEntryForFile" , err )
return nil , false
} else if entry == nil {
return nil , false
}
return wikiContentsByEntry ( ctx , entry ) , true
}
func renderWikiPage ( ctx * context . Context , isViewPage bool ) ( * git . Repository , * git . TreeEntry ) {
func renderWikiPage ( ctx * context . Context , isViewPage bool ) ( * git . Repository , * git . TreeEntry ) {
wikiRepo , commit , err := findWikiRepoCommit ( ctx )
wikiRepo , commit , err := findWikiRepoCommit ( ctx )
if err != nil {
if err != nil {
return nil , nil
return nil , nil
}
}
if commit == nil {
return wikiRepo , nil
}
// Get page list.
// Get page list.
if isViewPage {
if isViewPage {
@ -206,85 +121,62 @@ func renderWikiPage(ctx *context.Context, isViewPage bool) (*git.Repository, *gi
ctx . Handle ( 500 , "ListEntries" , err )
ctx . Handle ( 500 , "ListEntries" , err )
return nil , nil
return nil , nil
}
}
pages := [ ] PageMeta { }
pages := make ( [ ] PageMeta , 0 , len ( entries ) )
for i := range entries {
for _ , entry := range entries {
if entries [ i ] . Type == git . ObjectBlob {
if entry . Type != git . ObjectBlob {
name := entries [ i ] . Name ( )
continue
ext := filepath . Ext ( name )
}
if markdown . IsMarkdownFile ( name ) || ext == ".textile" {
wikiName , err := models . WikiFilenameToName ( entry . Name ( ) )
name = strings . TrimSuffix ( name , ext )
if err != nil {
if name == "" || name == "_Sidebar" || name == "_Footer" || name == "_Header" {
ctx . Handle ( 500 , "WikiFilenameToName" , err )
return nil , nil
} else if wikiName == "_Sidebar" || wikiName == "_Footer" {
continue
continue
}
}
pages = append ( pages , PageMeta {
pages = append ( pages , PageMeta {
Name : models . ToWikiPageName ( name ) ,
Name : wikiName ,
URL : n ame,
Sub URL: models . WikiN ameToSubURL ( wikiName ) ,
} )
} )
}
}
}
}
ctx . Data [ "Pages" ] = pages
ctx . Data [ "Pages" ] = pages
}
}
pageURL := ctx . Params ( ":page" )
pageName := models . NormalizeWikiName ( ctx . Params ( ":page" ) )
if len ( pageURL ) == 0 {
if len ( pageName ) == 0 {
pageURL = "Home"
pageName = "Home"
}
}
ctx . Data [ "PageURL" ] = pageURL
ctx . Data [ "PageURL" ] = models . WikiNameToSubURL ( pageName )
pageName := models . ToWikiPageName ( pageURL )
ctx . Data [ "old_title" ] = pageName
ctx . Data [ "old_title" ] = pageName
ctx . Data [ "Title" ] = pageName
ctx . Data [ "Title" ] = pageName
ctx . Data [ "title" ] = pageName
ctx . Data [ "title" ] = pageName
ctx . Data [ "RequireHighlightJS" ] = true
ctx . Data [ "RequireHighlightJS" ] = true
pageFilename := models . WikiNameToFilename ( pageName )
var entry * git . TreeEntry
var entry * git . TreeEntry
if entry , err = findFile ( wikiRepo , commit , pageName , tru e ) ; err != nil {
if entry , err = findEntryFor File ( commit , pageFilenam e ) ; err != nil {
ctx . Handle ( 500 , "findFile" , err )
ctx . Handle ( 500 , "findEntryFor File" , err )
return nil , nil
return nil , nil
}
} else if entry == nil {
if entry == nil {
ctx . Redirect ( ctx . Repo . RepoLink + "/wiki/_pages" )
ctx . Redirect ( ctx . Repo . RepoLink + "/wiki/_pages" )
return nil , nil
return nil , nil
}
}
blob := entry . Blob ( )
data := wikiContentsByEntry ( ctx , entry )
r , err := blob . Data ( )
if ctx . Written ( ) {
if err != nil {
ctx . Handle ( 500 , "Data" , err )
return nil , nil
return nil , nil
}
}
data , err := ioutil . ReadAll ( r )
if err != nil {
if isViewPage {
ctx . Handle ( 500 , "ReadAll" , err )
sidebarContent , sidebarPresent := wikiContentsByName ( ctx , commit , "_Sidebar" )
if ctx . Written ( ) {
return nil , nil
return nil , nil
}
}
sidebarPresent := false
sidebarContent := [ ] byte { }
footerContent , footerPresent := wikiContentsByName ( ctx , commit , "_Footer" )
sentry , err := findFile ( wikiRepo , commit , "_Sidebar" , true )
if ctx . Written ( ) {
if err == nil && sentry != nil {
return nil , nil
r , err = sentry . Blob ( ) . Data ( )
if err == nil {
dataSB , err := ioutil . ReadAll ( r )
if err == nil {
sidebarPresent = true
sidebarContent = dataSB
}
}
}
footerPresent := false
footerContent := [ ] byte { }
sentry , err = findFile ( wikiRepo , commit , "_Footer" , true )
if err == nil && sentry != nil {
r , err = sentry . Blob ( ) . Data ( )
if err == nil {
dataSB , err := ioutil . ReadAll ( r )
if err == nil {
footerPresent = true
footerContent = dataSB
}
}
}
}
if isViewPage {
metas := ctx . Repo . Repository . ComposeMetas ( )
metas := ctx . Repo . Repository . ComposeMetas ( )
ctx . Data [ "content" ] = markdown . RenderWiki ( data , ctx . Repo . RepoLink , metas )
ctx . Data [ "content" ] = markdown . RenderWiki ( data , ctx . Repo . RepoLink , metas )
ctx . Data [ "sidebarPresent" ] = sidebarPresent
ctx . Data [ "sidebarPresent" ] = sidebarPresent
@ -322,13 +214,13 @@ func Wiki(ctx *context.Context) {
return
return
}
}
ename := entry . Name ( )
wikiPath := entry . Name ( )
if markup . Type ( ename ) != markdown . MarkupName {
if markup . Type ( wikiPath ) != markdown . MarkupName {
ext := strings . ToUpper ( filepath . Ext ( ename ) )
ext := strings . ToUpper ( filepath . Ext ( wikiPath ) )
ctx . Data [ "FormatWarning" ] = fmt . Sprintf ( "%s rendering is not supported at the moment. Rendered as Markdown." , ext )
ctx . Data [ "FormatWarning" ] = fmt . Sprintf ( "%s rendering is not supported at the moment. Rendered as Markdown." , ext )
}
}
// Get last change information.
// Get last change information.
lastCommit , err := wikiRepo . GetCommitByPath ( ename )
lastCommit , err := wikiRepo . GetCommitByPath ( wikiPath )
if err != nil {
if err != nil {
ctx . Handle ( 500 , "GetCommitByPath" , err )
ctx . Handle ( 500 , "GetCommitByPath" , err )
return
return
@ -359,28 +251,26 @@ func WikiPages(ctx *context.Context) {
return
return
}
}
pages := make ( [ ] PageMeta , 0 , len ( entries ) )
pages := make ( [ ] PageMeta , 0 , len ( entries ) )
for i := range entries {
for _ , entry := range entries {
if entries [ i ] . Type == git . ObjectBlob {
if entry . Type != git . ObjectBlob {
c , err := wikiRepo . GetCommitByPath ( entries [ i ] . Name ( ) )
continue
}
c , err := wikiRepo . GetCommitByPath ( entry . Name ( ) )
if err != nil {
if err != nil {
ctx . Handle ( 500 , "GetCommit" , err )
ctx . Handle ( 500 , "GetCommit" , err )
return
return
}
}
name := entries [ i ] . Name ( )
wikiName , err := models . WikiFilenameToName ( entry . Name ( ) )
ext := filepath . Ext ( name )
if err != nil {
if markdown . IsMarkdownFile ( name ) || ext == ".textile" {
ctx . Handle ( 500 , "WikiFilenameToName" , err )
name = strings . TrimSuffix ( name , ext )
return
if name == "" {
continue
}
}
pages = append ( pages , PageMeta {
pages = append ( pages , PageMeta {
Name : models . ToWikiPageName ( name ) ,
Name : wikiName ,
URL : name ,
SubURL : models . WikiNameToSubURL ( wikiName ) ,
Updated : c . Author . When ,
Updated : c . Author . When ,
} )
} )
}
}
}
}
ctx . Data [ "Pages" ] = pages
ctx . Data [ "Pages" ] = pages
ctx . HTML ( 200 , tplWikiPages )
ctx . HTML ( 200 , tplWikiPages )
@ -394,31 +284,23 @@ func WikiRaw(ctx *context.Context) {
return
return
}
}
}
}
uri := ctx . Params ( "*" )
providedPath := ctx . Params ( "*" )
if strings . HasSuffix ( providedPath , ".md" ) {
providedPath = providedPath [ : len ( providedPath ) - 3 ]
}
wikiPath := models . WikiNameToFilename ( providedPath )
var entry * git . TreeEntry
var entry * git . TreeEntry
if commit != nil {
if commit != nil {
entry , err = findFile ( wikiRepo , commit , uri , false )
entry , err = findEntryFor File ( commit , wikiPath )
}
}
if err != nil || entry == nil {
if err != nil {
if entry == nil || commit == nil {
defBranch := ctx . Repo . Repository . DefaultBranch
if commit , err = ctx . Repo . GitRepo . GetBranchCommit ( defBranch ) ; commit == nil || err != nil {
ctx . Handle ( 500 , "GetBranchCommit" , err )
return
}
if entry , err = findFile ( ctx . Repo . GitRepo , commit , uri , false ) ; err != nil {
ctx . Handle ( 500 , "findFile" , err )
ctx . Handle ( 500 , "findFile" , err )
return
return
}
} else if entry == nil {
if entry == nil {
ctx . Handle ( 404 , "findEntryForFile" , nil )
ctx . Handle ( 404 , "findFile" , nil )
return
return
}
}
} else {
ctx . Handle ( 500 , "findFile" , err )
return
}
}
if err = ServeBlob ( ctx , entry . Blob ( ) ) ; err != nil {
if err = ServeBlob ( ctx , entry . Blob ( ) ) ; err != nil {
ctx . Handle ( 500 , "ServeBlob" , err )
ctx . Handle ( 500 , "ServeBlob" , err )
}
}
@ -437,7 +319,7 @@ func NewWiki(ctx *context.Context) {
ctx . HTML ( 200 , tplWikiNew )
ctx . HTML ( 200 , tplWikiNew )
}
}
// NewWikiPost response fr o wiki create request
// NewWikiPost response for wiki create request
func NewWikiPost ( ctx * context . Context , form auth . NewWikiForm ) {
func NewWikiPost ( ctx * context . Context , form auth . NewWikiForm ) {
ctx . Data [ "Title" ] = ctx . Tr ( "repo.wiki.new_page" )
ctx . Data [ "Title" ] = ctx . Tr ( "repo.wiki.new_page" )
ctx . Data [ "PageIsWiki" ] = true
ctx . Data [ "PageIsWiki" ] = true
@ -448,10 +330,12 @@ func NewWikiPost(ctx *context.Context, form auth.NewWikiForm) {
return
return
}
}
wikiPath := models . ToWikiPageURL ( form . Title )
wikiName := models . NormalizeWikiName ( form . Title )
if err := ctx . Repo . Repository . AddWikiPage ( ctx . User , wikiName , form . Content , form . Message ) ; err != nil {
if err := ctx . Repo . Repository . AddWikiPage ( ctx . User , wikiPath , form . Content , form . Message ) ; err != nil {
if models . IsErrWikiReservedName ( err ) {
if models . IsErrWikiAlreadyExist ( err ) {
ctx . Data [ "Err_Title" ] = true
ctx . RenderWithErr ( ctx . Tr ( "repo.wiki.reserved_page" , wikiName ) , tplWikiNew , & form )
} else if models . IsErrWikiAlreadyExist ( err ) {
ctx . Data [ "Err_Title" ] = true
ctx . Data [ "Err_Title" ] = true
ctx . RenderWithErr ( ctx . Tr ( "repo.wiki.page_already_exists" ) , tplWikiNew , & form )
ctx . RenderWithErr ( ctx . Tr ( "repo.wiki.page_already_exists" ) , tplWikiNew , & form )
} else {
} else {
@ -460,7 +344,7 @@ func NewWikiPost(ctx *context.Context, form auth.NewWikiForm) {
return
return
}
}
ctx . Redirect ( ctx . Repo . RepoLink + "/wiki/" + wikiPath )
ctx . Redirect ( ctx . Repo . RepoLink + "/wiki/" + models . WikiNameToFilename ( wikiName ) )
}
}
// EditWiki render wiki modify page
// EditWiki render wiki modify page
@ -482,7 +366,7 @@ func EditWiki(ctx *context.Context) {
ctx . HTML ( 200 , tplWikiNew )
ctx . HTML ( 200 , tplWikiNew )
}
}
// EditWikiPost response fr o wiki modify request
// EditWikiPost response for wiki modify request
func EditWikiPost ( ctx * context . Context , form auth . NewWikiForm ) {
func EditWikiPost ( ctx * context . Context , form auth . NewWikiForm ) {
ctx . Data [ "Title" ] = ctx . Tr ( "repo.wiki.new_page" )
ctx . Data [ "Title" ] = ctx . Tr ( "repo.wiki.new_page" )
ctx . Data [ "PageIsWiki" ] = true
ctx . Data [ "PageIsWiki" ] = true
@ -493,25 +377,25 @@ func EditWikiPost(ctx *context.Context, form auth.NewWikiForm) {
return
return
}
}
oldWikiPath := models . ToWikiPageURL ( ctx . Params ( ":page" ) )
oldWikiName := models . NormalizeWikiName ( ctx . Params ( ":page" ) )
newWikiPath := models . ToWikiPageURL ( form . Title )
newWikiName := models . NormalizeWikiName ( form . Title )
if err := ctx . Repo . Repository . EditWikiPage ( ctx . User , oldWikiPath , newWikiPath , form . Content , form . Message ) ; err != nil {
if err := ctx . Repo . Repository . EditWikiPage ( ctx . User , oldWikiName , newWikiName , form . Content , form . Message ) ; err != nil {
ctx . Handle ( 500 , "EditWikiPage" , err )
ctx . Handle ( 500 , "EditWikiPage" , err )
return
return
}
}
ctx . Redirect ( ctx . Repo . RepoLink + "/wiki/" + newWikiPath )
ctx . Redirect ( ctx . Repo . RepoLink + "/wiki/" + models . WikiNameToFilename ( newWikiName ) )
}
}
// DeleteWikiPagePost delete wiki page
// DeleteWikiPagePost delete wiki page
func DeleteWikiPagePost ( ctx * context . Context ) {
func DeleteWikiPagePost ( ctx * context . Context ) {
pageURL := models . ToWikiPageURL ( ctx . Params ( ":page" ) )
wikiName := models . NormalizeWikiName ( ctx . Params ( ":page" ) )
if len ( pageURL ) == 0 {
if len ( wikiName ) == 0 {
pageURL = "Home"
wikiName = "Home"
}
}
if err := ctx . Repo . Repository . DeleteWikiPage ( ctx . User , pageURL ) ; err != nil {
if err := ctx . Repo . Repository . DeleteWikiPage ( ctx . User , wikiName ) ; err != nil {
ctx . Handle ( 500 , "DeleteWikiPage" , err )
ctx . Handle ( 500 , "DeleteWikiPage" , err )
return
return
}
}