2014-02-20 06:45:43 +04:00
// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package repo
import (
2014-04-05 11:17:57 +04:00
"encoding/base64"
"errors"
2014-04-03 23:50:55 +04:00
"fmt"
2014-04-13 05:35:36 +04:00
"github.com/gogits/git"
2014-03-22 21:50:50 +04:00
"path"
2014-03-24 19:56:32 +04:00
"path/filepath"
2014-03-22 21:50:50 +04:00
"strings"
2014-03-30 20:11:28 +04:00
"github.com/go-martini/martini"
2014-03-22 21:50:50 +04:00
2014-02-20 06:45:43 +04:00
"github.com/gogits/gogs/models"
2014-03-08 01:05:18 +04:00
"github.com/gogits/gogs/modules/auth"
2014-03-22 21:50:50 +04:00
"github.com/gogits/gogs/modules/base"
2014-03-19 12:48:45 +04:00
"github.com/gogits/gogs/modules/log"
2014-03-15 17:17:16 +04:00
"github.com/gogits/gogs/modules/middleware"
2014-02-20 06:45:43 +04:00
)
2014-04-11 02:09:57 +04:00
func Create ( ctx * middleware . Context ) {
2014-03-15 17:58:32 +04:00
ctx . Data [ "Title" ] = "Create repository"
2014-04-11 02:09:57 +04:00
ctx . Data [ "PageIsNewRepo" ] = true
2014-03-19 21:14:56 +04:00
ctx . Data [ "LanguageIgns" ] = models . LanguageIgns
ctx . Data [ "Licenses" ] = models . Licenses
2014-04-11 02:09:57 +04:00
ctx . HTML ( 200 , "repo/create" )
}
2014-03-08 01:05:18 +04:00
2014-04-11 02:09:57 +04:00
func CreatePost ( ctx * middleware . Context , form auth . CreateRepoForm ) {
ctx . Data [ "Title" ] = "Create repository"
ctx . Data [ "PageIsNewRepo" ] = true
ctx . Data [ "LanguageIgns" ] = models . LanguageIgns
ctx . Data [ "Licenses" ] = models . Licenses
2014-02-20 06:45:43 +04:00
2014-03-23 00:00:46 +04:00
if ctx . HasError ( ) {
ctx . HTML ( 200 , "repo/create" )
return
}
2014-04-13 04:35:35 +04:00
repo , err := models . CreateRepository ( ctx . User , form . RepoName , form . Description ,
form . Language , form . License , form . Private , false , form . InitReadme )
2014-03-17 19:56:50 +04:00
if err == nil {
2014-03-19 12:48:45 +04:00
log . Trace ( "%s Repository created: %s/%s" , ctx . Req . RequestURI , ctx . User . LowerName , form . RepoName )
2014-03-23 01:59:22 +04:00
ctx . Redirect ( "/" + ctx . User . Name + "/" + form . RepoName )
2014-03-09 06:25:38 +04:00
return
2014-03-17 10:36:28 +04:00
} else if err == models . ErrRepoAlreadyExist {
2014-03-15 18:52:14 +04:00
ctx . RenderWithErr ( "Repository name has already been used" , "repo/create" , & form )
2014-03-10 04:06:29 +04:00
return
2014-03-20 19:41:24 +04:00
} else if err == models . ErrRepoNameIllegal {
ctx . RenderWithErr ( models . ErrRepoNameIllegal . Error ( ) , "repo/create" , & form )
return
2014-03-10 04:06:29 +04:00
}
2014-04-13 04:35:35 +04:00
if repo != nil {
if errDelete := models . DeleteRepository ( ctx . User . Id , repo . Id , ctx . User . Name ) ; errDelete != nil {
log . Error ( "repo.MigratePost(CreatePost): %v" , errDelete )
}
}
2014-04-11 02:09:57 +04:00
ctx . Handle ( 500 , "repo.Create" , err )
2014-02-20 06:45:43 +04:00
}
2014-04-13 04:35:35 +04:00
func Migrate ( ctx * middleware . Context ) {
ctx . Data [ "Title" ] = "Migrate repository"
2014-04-11 02:09:57 +04:00
ctx . Data [ "PageIsNewRepo" ] = true
2014-04-13 04:35:35 +04:00
ctx . HTML ( 200 , "repo/migrate" )
2014-04-11 02:09:57 +04:00
}
2014-04-09 17:28:00 +04:00
2014-04-13 04:35:35 +04:00
func MigratePost ( ctx * middleware . Context , form auth . MigrateRepoForm ) {
ctx . Data [ "Title" ] = "Migrate repository"
2014-04-11 02:09:57 +04:00
ctx . Data [ "PageIsNewRepo" ] = true
2014-04-09 17:28:00 +04:00
if ctx . HasError ( ) {
2014-04-13 04:35:35 +04:00
ctx . HTML ( 200 , "repo/migrate" )
2014-04-09 17:28:00 +04:00
return
}
2014-04-13 04:35:35 +04:00
url := strings . Replace ( form . Url , "://" , fmt . Sprintf ( "://%s:%s@" , form . AuthUserName , form . AuthPasswd ) , 1 )
repo , err := models . MigrateRepository ( ctx . User , form . RepoName , form . Description , form . Private ,
form . Mirror , url )
2014-04-09 17:28:00 +04:00
if err == nil {
2014-04-13 04:35:35 +04:00
log . Trace ( "%s Repository migrated: %s/%s" , ctx . Req . RequestURI , ctx . User . LowerName , form . RepoName )
2014-04-09 17:28:00 +04:00
ctx . Redirect ( "/" + ctx . User . Name + "/" + form . RepoName )
return
} else if err == models . ErrRepoAlreadyExist {
2014-04-13 04:35:35 +04:00
ctx . RenderWithErr ( "Repository name has already been used" , "repo/migrate" , & form )
2014-04-09 17:28:00 +04:00
return
} else if err == models . ErrRepoNameIllegal {
2014-04-13 04:35:35 +04:00
ctx . RenderWithErr ( models . ErrRepoNameIllegal . Error ( ) , "repo/migrate" , & form )
return
}
if repo != nil {
if errDelete := models . DeleteRepository ( ctx . User . Id , repo . Id , ctx . User . Name ) ; errDelete != nil {
log . Error ( "repo.MigratePost(DeleteRepository): %v" , errDelete )
}
}
if strings . Contains ( err . Error ( ) , "Authentication failed" ) {
ctx . RenderWithErr ( err . Error ( ) , "repo/migrate" , & form )
2014-04-09 17:28:00 +04:00
return
}
2014-04-13 04:35:35 +04:00
ctx . Handle ( 500 , "repo.Migrate" , err )
2014-04-09 17:28:00 +04:00
}
2014-03-22 21:50:50 +04:00
func Single ( ctx * middleware . Context , params martini . Params ) {
2014-03-30 06:13:02 +04:00
branchName := ctx . Repo . BranchName
userName := ctx . Repo . Owner . Name
repoName := ctx . Repo . Repository . Name
2014-03-22 21:50:50 +04:00
2014-03-30 06:13:02 +04:00
repoLink := ctx . Repo . RepoLink
branchLink := ctx . Repo . RepoLink + "/src/" + branchName
rawLink := ctx . Repo . RepoLink + "/raw/" + branchName
2014-03-27 20:07:22 +04:00
2014-03-22 21:50:50 +04:00
// Get tree path
treename := params [ "_1" ]
if len ( treename ) > 0 && treename [ len ( treename ) - 1 ] == '/' {
2014-03-30 06:13:02 +04:00
ctx . Redirect ( repoLink + "/src/" + branchName + "/" + treename [ : len ( treename ) - 1 ] )
2014-03-22 21:50:50 +04:00
return
}
ctx . Data [ "IsRepoToolbarSource" ] = true
2014-03-30 06:13:02 +04:00
isViewBranch := ctx . Repo . IsBranch
2014-03-28 05:15:53 +04:00
ctx . Data [ "IsViewBranch" ] = isViewBranch
2014-03-27 20:07:22 +04:00
2014-04-13 05:35:36 +04:00
treePath := treename
if len ( treePath ) != 0 {
treePath = treePath + "/"
}
entry , err := ctx . Repo . Commit . GetTreeEntryByPath ( treename )
2014-03-30 06:13:02 +04:00
2014-04-13 05:35:36 +04:00
if err != nil && err != git . ErrNotExist {
ctx . Handle ( 404 , "repo.Single(GetTreeEntryByPath)" , err )
2014-03-22 21:50:50 +04:00
return
}
2014-04-13 05:35:36 +04:00
if len ( treename ) != 0 && entry == nil {
2014-03-23 09:12:55 +04:00
ctx . Handle ( 404 , "repo.Single" , nil )
2014-03-22 21:50:50 +04:00
return
}
2014-04-13 05:35:36 +04:00
if entry != nil && entry . IsFile ( ) {
blob := entry . Blob ( )
if data , err := blob . Data ( ) ; err != nil {
ctx . Handle ( 404 , "repo.Single(blob.Data)" , err )
2014-03-22 21:50:50 +04:00
} else {
2014-04-13 05:35:36 +04:00
ctx . Data [ "FileSize" ] = blob . Size ( )
2014-03-22 21:50:50 +04:00
ctx . Data [ "IsFile" ] = true
2014-04-13 05:35:36 +04:00
ctx . Data [ "FileName" ] = blob . Name
ext := path . Ext ( blob . Name )
2014-03-22 21:50:50 +04:00
if len ( ext ) > 0 {
ext = ext [ 1 : ]
}
ctx . Data [ "FileExt" ] = ext
2014-03-24 19:56:32 +04:00
ctx . Data [ "FileLink" ] = rawLink + "/" + treename
_ , isTextFile := base . IsTextFile ( data )
2014-03-27 20:30:20 +04:00
_ , isImageFile := base . IsImageFile ( data )
2014-03-24 19:56:32 +04:00
ctx . Data [ "FileIsText" ] = isTextFile
2014-03-22 21:50:50 +04:00
2014-03-27 20:30:20 +04:00
if isImageFile {
ctx . Data [ "IsImageFile" ] = true
2014-03-22 21:50:50 +04:00
} else {
2014-04-13 05:35:36 +04:00
readmeExist := base . IsMarkdownFile ( blob . Name ) || base . IsReadmeFile ( blob . Name )
2014-03-27 20:30:20 +04:00
ctx . Data [ "ReadmeExist" ] = readmeExist
if readmeExist {
ctx . Data [ "FileContent" ] = string ( base . RenderMarkdown ( data , "" ) )
} else {
if isTextFile {
ctx . Data [ "FileContent" ] = string ( data )
}
2014-03-24 19:56:32 +04:00
}
2014-03-22 21:50:50 +04:00
}
}
} else {
// Directory and file list.
2014-04-13 05:35:36 +04:00
tree , err := ctx . Repo . Commit . SubTree ( treename )
2014-03-22 21:50:50 +04:00
if err != nil {
2014-04-13 05:35:36 +04:00
ctx . Handle ( 404 , "repo.Single(SubTree)" , err )
2014-03-22 21:50:50 +04:00
return
}
2014-04-13 05:35:36 +04:00
entries := tree . ListEntries ( )
entries . Sort ( )
files := make ( [ ] [ ] interface { } , 0 , len ( entries ) )
for _ , te := range entries {
c , err := ctx . Repo . Commit . GetCommitOfRelPath ( filepath . Join ( treePath , te . Name ) )
if err != nil {
ctx . Handle ( 404 , "repo.Single(SubTree)" , err )
return
}
files = append ( files , [ ] interface { } { te , c } )
}
2014-03-22 21:50:50 +04:00
ctx . Data [ "Files" ] = files
2014-04-13 05:35:36 +04:00
var readmeFile * git . Blob
2014-03-22 21:50:50 +04:00
2014-04-13 05:35:36 +04:00
for _ , f := range entries {
2014-03-22 21:50:50 +04:00
if ! f . IsFile ( ) || ! base . IsReadmeFile ( f . Name ) {
continue
} else {
2014-04-13 05:35:36 +04:00
readmeFile = f . Blob ( )
2014-03-22 21:50:50 +04:00
break
}
}
if readmeFile != nil {
2014-03-24 20:42:57 +04:00
ctx . Data [ "ReadmeInSingle" ] = true
2014-03-22 21:50:50 +04:00
ctx . Data [ "ReadmeExist" ] = true
2014-04-13 05:35:36 +04:00
if data , err := readmeFile . Data ( ) ; err != nil {
2014-03-23 09:12:55 +04:00
ctx . Handle ( 404 , "repo.Single(readmeFile.LookupBlob)" , err )
2014-03-22 21:50:50 +04:00
return
} else {
2014-03-24 19:56:32 +04:00
ctx . Data [ "FileSize" ] = readmeFile . Size
ctx . Data [ "FileLink" ] = rawLink + "/" + treename
_ , isTextFile := base . IsTextFile ( data )
ctx . Data [ "FileIsText" ] = isTextFile
2014-03-22 21:50:50 +04:00
ctx . Data [ "FileName" ] = readmeFile . Name
2014-03-24 19:56:32 +04:00
if isTextFile {
ctx . Data [ "FileContent" ] = string ( base . RenderMarkdown ( data , branchLink ) )
}
2014-03-22 21:50:50 +04:00
}
}
}
2014-03-27 20:07:22 +04:00
ctx . Data [ "Username" ] = userName
ctx . Data [ "Reponame" ] = repoName
2014-03-22 21:50:50 +04:00
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 ]
}
}
2014-03-30 06:13:02 +04:00
ctx . Data [ "LastCommit" ] = ctx . Repo . Commit
2014-03-22 21:50:50 +04:00
ctx . Data [ "Paths" ] = Paths
ctx . Data [ "Treenames" ] = treenames
2014-04-13 05:35:36 +04:00
ctx . Data [ "TreePath" ] = treePath
2014-03-22 21:50:50 +04:00
ctx . Data [ "BranchLink" ] = branchLink
ctx . HTML ( 200 , "repo/single" )
}
2014-03-24 19:56:32 +04:00
func SingleDownload ( ctx * middleware . Context , params martini . Params ) {
// Get tree path
treename := params [ "_1" ]
2014-04-13 05:35:36 +04:00
blob , err := ctx . Repo . Commit . GetBlobByPath ( treename )
2014-03-24 19:56:32 +04:00
if err != nil {
2014-04-13 05:35:36 +04:00
ctx . Handle ( 404 , "repo.SingleDownload(GetBlobByPath)" , err )
2014-03-24 19:56:32 +04:00
return
}
2014-04-13 05:35:36 +04:00
data , err := blob . Data ( )
2014-03-24 19:56:32 +04:00
if err != nil {
2014-04-13 05:35:36 +04:00
ctx . Handle ( 404 , "repo.SingleDownload(Data)" , err )
2014-03-24 19:56:32 +04:00
return
}
contentType , isTextFile := base . IsTextFile ( data )
2014-03-27 20:30:20 +04:00
_ , isImageFile := base . IsImageFile ( data )
2014-03-24 19:56:32 +04:00
ctx . Res . Header ( ) . Set ( "Content-Type" , contentType )
2014-03-27 20:38:44 +04:00
if ! isTextFile && ! isImageFile {
2014-03-24 19:56:32 +04:00
ctx . Res . Header ( ) . Set ( "Content-Disposition" , "attachment; filename=" + filepath . Base ( treename ) )
ctx . Res . Header ( ) . Set ( "Content-Transfer-Encoding" , "binary" )
}
ctx . Res . Write ( data )
}
2014-04-05 11:17:57 +04:00
func basicEncode ( username , password string ) string {
auth := username + ":" + password
return base64 . StdEncoding . EncodeToString ( [ ] byte ( auth ) )
}
func basicDecode ( encoded string ) ( user string , name string , err error ) {
var s [ ] byte
s , err = base64 . StdEncoding . DecodeString ( encoded )
if err != nil {
return
}
a := strings . Split ( string ( s ) , ":" )
if len ( a ) == 2 {
user , name = a [ 0 ] , a [ 1 ]
} else {
err = errors . New ( "decode failed" )
}
return
}
func authRequired ( ctx * middleware . Context ) {
2014-04-05 18:24:10 +04:00
ctx . ResponseWriter . Header ( ) . Set ( "WWW-Authenticate" , "Basic realm=\".\"" )
2014-04-05 11:17:57 +04:00
ctx . Data [ "ErrorMsg" ] = "no basic auth and digit auth"
ctx . HTML ( 401 , fmt . Sprintf ( "status/401" ) )
}
2014-03-22 21:50:50 +04:00
func Setting ( ctx * middleware . Context , params martini . Params ) {
if ! ctx . Repo . IsOwner {
2014-03-23 09:12:55 +04:00
ctx . Handle ( 404 , "repo.Setting" , nil )
2014-03-22 21:50:50 +04:00
return
}
ctx . Data [ "IsRepoToolbarSetting" ] = true
var title string
if t , ok := ctx . Data [ "Title" ] . ( string ) ; ok {
title = t
}
ctx . Data [ "Title" ] = title + " - settings"
ctx . HTML ( 200 , "repo/setting" )
}
2014-03-24 14:25:15 +04:00
func SettingPost ( ctx * middleware . Context ) {
2014-03-23 14:27:01 +04:00
if ! ctx . Repo . IsOwner {
ctx . Error ( 404 )
return
}
switch ctx . Query ( "action" ) {
case "update" :
2014-04-03 23:50:55 +04:00
newRepoName := ctx . Query ( "name" )
// Check if repository name has been changed.
if ctx . Repo . Repository . Name != newRepoName {
isExist , err := models . IsRepositoryExist ( ctx . Repo . Owner , newRepoName )
if err != nil {
2014-04-11 06:03:31 +04:00
ctx . Handle ( 500 , "repo.SettingPost(update: check existence)" , err )
2014-04-03 23:50:55 +04:00
return
} else if isExist {
ctx . RenderWithErr ( "Repository name has been taken in your repositories." , "repo/setting" , nil )
return
} else if err = models . ChangeRepositoryName ( ctx . Repo . Owner . Name , ctx . Repo . Repository . Name , newRepoName ) ; err != nil {
2014-04-11 06:03:31 +04:00
ctx . Handle ( 500 , "repo.SettingPost(change repository name)" , err )
2014-04-03 23:50:55 +04:00
return
}
log . Trace ( "%s Repository name changed: %s/%s -> %s" , ctx . Req . RequestURI , ctx . User . Name , ctx . Repo . Repository . Name , newRepoName )
ctx . Repo . Repository . Name = newRepoName
}
2014-04-11 06:03:31 +04:00
br := ctx . Query ( "branch" )
2014-04-13 05:35:36 +04:00
if git . IsBranchExist ( models . RepoPath ( ctx . User . Name , ctx . Repo . Repository . Name ) , br ) {
2014-04-11 06:03:31 +04:00
ctx . Repo . Repository . DefaultBranch = br
}
2014-03-23 14:27:01 +04:00
ctx . Repo . Repository . Description = ctx . Query ( "desc" )
ctx . Repo . Repository . Website = ctx . Query ( "site" )
2014-04-10 05:42:25 +04:00
ctx . Repo . Repository . IsGoget = ctx . Query ( "goget" ) == "on"
2014-03-23 14:27:01 +04:00
if err := models . UpdateRepository ( ctx . Repo . Repository ) ; err != nil {
ctx . Handle ( 404 , "repo.SettingPost(update)" , err )
return
}
2014-04-03 23:50:55 +04:00
log . Trace ( "%s Repository updated: %s/%s" , ctx . Req . RequestURI , ctx . Repo . Owner . Name , ctx . Repo . Repository . Name )
2014-04-11 06:03:31 +04:00
ctx . Flash . Success ( "Repository options has been successfully updated." )
ctx . Redirect ( fmt . Sprintf ( "/%s/%s/settings" , ctx . Repo . Owner . Name , ctx . Repo . Repository . Name ) )
2014-04-05 02:31:09 +04:00
case "transfer" :
if len ( ctx . Repo . Repository . Name ) == 0 || ctx . Repo . Repository . Name != ctx . Query ( "repository" ) {
ctx . RenderWithErr ( "Please make sure you entered repository name is correct." , "repo/setting" , nil )
return
}
newOwner := ctx . Query ( "owner" )
// Check if new owner exists.
isExist , err := models . IsUserExist ( newOwner )
if err != nil {
2014-04-11 06:03:31 +04:00
ctx . Handle ( 500 , "repo.SettingPost(transfer: check existence)" , err )
2014-04-05 02:31:09 +04:00
return
} else if ! isExist {
ctx . RenderWithErr ( "Please make sure you entered owner name is correct." , "repo/setting" , nil )
return
} else if err = models . TransferOwnership ( ctx . User , newOwner , ctx . Repo . Repository ) ; err != nil {
2014-04-11 06:03:31 +04:00
ctx . Handle ( 500 , "repo.SettingPost(transfer repository)" , err )
2014-04-05 02:31:09 +04:00
return
}
log . Trace ( "%s Repository transfered: %s/%s -> %s" , ctx . Req . RequestURI , ctx . User . Name , ctx . Repo . Repository . Name , newOwner )
ctx . Redirect ( "/" )
2014-03-23 14:27:01 +04:00
case "delete" :
if len ( ctx . Repo . Repository . Name ) == 0 || ctx . Repo . Repository . Name != ctx . Query ( "repository" ) {
2014-04-03 23:50:55 +04:00
ctx . RenderWithErr ( "Please make sure you entered repository name is correct." , "repo/setting" , nil )
2014-03-23 14:27:01 +04:00
return
}
if err := models . DeleteRepository ( ctx . User . Id , ctx . Repo . Repository . Id , ctx . User . LowerName ) ; err != nil {
2014-04-11 06:03:31 +04:00
ctx . Handle ( 500 , "repo.Delete" , err )
2014-03-23 14:27:01 +04:00
return
}
log . Trace ( "%s Repository deleted: %s/%s" , ctx . Req . RequestURI , ctx . User . LowerName , ctx . Repo . Repository . LowerName )
2014-04-11 06:03:31 +04:00
2014-03-23 14:27:01 +04:00
ctx . Redirect ( "/" )
}
}
2014-03-22 21:50:50 +04:00
func Action ( ctx * middleware . Context , params martini . Params ) {
var err error
switch params [ "action" ] {
case "watch" :
err = models . WatchRepo ( ctx . User . Id , ctx . Repo . Repository . Id , true )
case "unwatch" :
err = models . WatchRepo ( ctx . User . Id , ctx . Repo . Repository . Id , false )
case "desc" :
if ! ctx . Repo . IsOwner {
ctx . Error ( 404 )
return
}
ctx . Repo . Repository . Description = ctx . Query ( "desc" )
ctx . Repo . Repository . Website = ctx . Query ( "site" )
err = models . UpdateRepository ( ctx . Repo . Repository )
}
if err != nil {
log . Error ( "repo.Action(%s): %v" , params [ "action" ] , err )
ctx . JSON ( 200 , map [ string ] interface { } {
"ok" : false ,
"err" : err . Error ( ) ,
} )
return
}
ctx . JSON ( 200 , map [ string ] interface { } {
"ok" : true ,
} )
}