2016-08-11 15:48:08 +03:00
// Copyright 2016 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 (
2016-08-30 15:07:50 +03:00
"fmt"
2016-08-11 15:48:08 +03:00
"io/ioutil"
"path"
2019-07-17 21:40:28 +03:00
"path/filepath"
2016-08-11 15:48:08 +03:00
"strings"
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/auth"
"code.gitea.io/gitea/modules/base"
2019-08-15 15:07:28 +03:00
"code.gitea.io/gitea/modules/charset"
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/modules/context"
2019-03-27 12:33:00 +03:00
"code.gitea.io/gitea/modules/git"
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/modules/log"
2019-04-17 19:06:35 +03:00
"code.gitea.io/gitea/modules/repofiles"
2020-01-14 06:38:04 +03:00
repo_module "code.gitea.io/gitea/modules/repository"
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/modules/setting"
2019-07-07 05:25:05 +03:00
"code.gitea.io/gitea/modules/upload"
2019-04-17 03:07:13 +03:00
"code.gitea.io/gitea/modules/util"
2020-02-22 16:08:48 +03:00
"code.gitea.io/gitea/routers/utils"
2016-08-11 15:48:08 +03:00
)
const (
2016-11-24 10:04:31 +03:00
tplEditFile base . TplName = "repo/editor/edit"
tplEditDiffPreview base . TplName = "repo/editor/diff_preview"
tplDeleteFile base . TplName = "repo/editor/delete"
tplUploadFile base . TplName = "repo/editor/upload"
2017-05-02 03:49:55 +03:00
frmCommitChoiceDirect string = "direct"
frmCommitChoiceNewBranch string = "commit-to-new-branch"
2016-08-11 15:48:08 +03:00
)
2017-05-02 03:49:55 +03:00
func renderCommitRights ( ctx * context . Context ) bool {
2020-01-15 11:32:57 +03:00
canCommitToBranch , err := ctx . Repo . CanCommitToBranch ( ctx . User )
2017-05-02 03:49:55 +03:00
if err != nil {
2019-04-02 10:48:31 +03:00
log . Error ( "CanCommitToBranch: %v" , err )
2017-05-02 03:49:55 +03:00
}
2020-01-15 11:32:57 +03:00
ctx . Data [ "CanCommitToBranch" ] = canCommitToBranch
return canCommitToBranch . CanCommitToBranch
2017-05-02 03:49:55 +03:00
}
2017-06-22 18:30:47 +03:00
// getParentTreeFields returns list of parent tree names and corresponding tree paths
// based on given tree path.
func getParentTreeFields ( treePath string ) ( treeNames [ ] string , treePaths [ ] string ) {
if len ( treePath ) == 0 {
return treeNames , treePaths
}
treeNames = strings . Split ( treePath , "/" )
treePaths = make ( [ ] string , len ( treeNames ) )
for i := range treeNames {
treePaths [ i ] = strings . Join ( treeNames [ : i + 1 ] , "/" )
}
return treeNames , treePaths
}
2016-08-11 15:48:08 +03:00
func editFile ( ctx * context . Context , isNewFile bool ) {
ctx . Data [ "PageIsEdit" ] = true
ctx . Data [ "IsNewFile" ] = isNewFile
ctx . Data [ "RequireHighlightJS" ] = true
2016-08-15 09:02:14 +03:00
ctx . Data [ "RequireSimpleMDE" ] = true
2017-05-02 03:49:55 +03:00
canCommit := renderCommitRights ( ctx )
2016-08-11 15:48:08 +03:00
2019-02-12 16:07:31 +03:00
treePath := cleanUploadFileName ( ctx . Repo . TreePath )
if treePath != ctx . Repo . TreePath {
if isNewFile {
2019-04-17 03:07:13 +03:00
ctx . Redirect ( path . Join ( ctx . Repo . RepoLink , "_new" , util . PathEscapeSegments ( ctx . Repo . BranchName ) , util . PathEscapeSegments ( treePath ) ) )
2019-02-12 16:07:31 +03:00
} else {
2019-04-17 03:07:13 +03:00
ctx . Redirect ( path . Join ( ctx . Repo . RepoLink , "_edit" , util . PathEscapeSegments ( ctx . Repo . BranchName ) , util . PathEscapeSegments ( treePath ) ) )
2019-02-12 16:07:31 +03:00
}
return
}
2017-06-22 18:30:47 +03:00
treeNames , treePaths := getParentTreeFields ( ctx . Repo . TreePath )
2016-08-11 15:48:08 +03:00
if ! isNewFile {
2016-08-25 07:35:03 +03:00
entry , err := ctx . Repo . Commit . GetTreeEntryByPath ( ctx . Repo . TreePath )
2016-08-15 09:02:14 +03:00
if err != nil {
2016-08-30 15:07:50 +03:00
ctx . NotFoundOrServerError ( "GetTreeEntryByPath" , git . IsErrNotExist , err )
2016-08-11 15:48:08 +03:00
return
}
2016-08-15 09:02:14 +03:00
// No way to edit a directory online.
if entry . IsDir ( ) {
2018-01-11 00:34:17 +03:00
ctx . NotFound ( "entry.IsDir" , nil )
2016-08-11 15:48:08 +03:00
return
}
blob := entry . Blob ( )
2017-11-29 04:50:39 +03:00
if blob . Size ( ) >= setting . UI . MaxDisplayFileSize {
2018-01-11 00:34:17 +03:00
ctx . NotFound ( "blob.Size" , err )
2017-11-29 04:50:39 +03:00
return
}
2019-04-19 15:17:27 +03:00
dataRc , err := blob . DataAsync ( )
2016-08-11 15:48:08 +03:00
if err != nil {
2018-01-11 00:34:17 +03:00
ctx . NotFound ( "blob.Data" , err )
2016-08-11 15:48:08 +03:00
return
}
2019-04-19 15:17:27 +03:00
defer dataRc . Close ( )
2016-08-11 15:48:08 +03:00
ctx . Data [ "FileSize" ] = blob . Size ( )
ctx . Data [ "FileName" ] = blob . Name ( )
buf := make ( [ ] byte , 1024 )
n , _ := dataRc . Read ( buf )
2016-08-31 23:59:23 +03:00
buf = buf [ : n ]
2016-08-11 15:48:08 +03:00
2016-08-15 09:02:14 +03:00
// Only text file are editable online.
2016-08-30 12:08:38 +03:00
if ! base . IsTextFile ( buf ) {
2018-01-11 00:34:17 +03:00
ctx . NotFound ( "base.IsTextFile" , nil )
2016-08-11 15:48:08 +03:00
return
}
d , _ := ioutil . ReadAll ( dataRc )
buf = append ( buf , d ... )
2019-08-15 15:07:28 +03:00
if content , err := charset . ToUTF8WithErr ( buf ) ; err != nil {
2019-06-12 22:41:28 +03:00
log . Error ( "ToUTF8WithErr: %v" , err )
2016-08-11 15:48:08 +03:00
ctx . Data [ "FileContent" ] = string ( buf )
} else {
ctx . Data [ "FileContent" ] = content
}
} else {
2016-08-15 09:02:14 +03:00
treeNames = append ( treeNames , "" ) // Append empty string to allow user name the new file.
2016-08-11 15:48:08 +03:00
}
ctx . Data [ "TreeNames" ] = treeNames
2017-06-22 18:30:47 +03:00
ctx . Data [ "TreePaths" ] = treePaths
2017-10-30 05:04:25 +03:00
ctx . Data [ "BranchLink" ] = ctx . Repo . RepoLink + "/src/" + ctx . Repo . BranchNameSubURL ( )
2016-08-15 09:02:14 +03:00
ctx . Data [ "commit_summary" ] = ""
ctx . Data [ "commit_message" ] = ""
2017-05-02 03:49:55 +03:00
if canCommit {
ctx . Data [ "commit_choice" ] = frmCommitChoiceDirect
} else {
ctx . Data [ "commit_choice" ] = frmCommitChoiceNewBranch
}
2019-07-17 21:40:28 +03:00
ctx . Data [ "new_branch_name" ] = GetUniquePatchBranchName ( ctx )
2019-04-17 19:06:35 +03:00
ctx . Data [ "last_commit" ] = ctx . Repo . CommitID
2016-08-12 12:29:29 +03:00
ctx . Data [ "MarkdownFileExts" ] = strings . Join ( setting . Markdown . FileExtensions , "," )
ctx . Data [ "LineWrapExtensions" ] = strings . Join ( setting . Repository . Editor . LineWrapExtensions , "," )
ctx . Data [ "PreviewableFileModes" ] = strings . Join ( setting . Repository . Editor . PreviewableFileModes , "," )
2016-11-27 13:14:25 +03:00
ctx . Data [ "EditorconfigURLPrefix" ] = fmt . Sprintf ( "%s/api/v1/repos/%s/editorconfig/" , setting . AppSubURL , ctx . Repo . Repository . FullName ( ) )
2016-08-11 15:48:08 +03:00
2016-11-24 10:04:31 +03:00
ctx . HTML ( 200 , tplEditFile )
2016-08-11 15:48:08 +03:00
}
2016-11-24 10:04:31 +03:00
// EditFile render edit file page
2016-08-15 09:02:14 +03:00
func EditFile ( ctx * context . Context ) {
editFile ( ctx , false )
2016-08-11 15:48:08 +03:00
}
2016-11-24 10:04:31 +03:00
// NewFile render create file page
2016-08-15 09:02:14 +03:00
func NewFile ( ctx * context . Context ) {
editFile ( ctx , true )
2016-08-11 15:48:08 +03:00
}
func editFilePost ( ctx * context . Context , form auth . EditRepoFileForm , isNewFile bool ) {
2017-05-02 03:49:55 +03:00
canCommit := renderCommitRights ( ctx )
2019-04-17 19:06:35 +03:00
treeNames , treePaths := getParentTreeFields ( form . TreePath )
branchName := ctx . Repo . BranchName
2017-05-02 03:49:55 +03:00
if form . CommitChoice == frmCommitChoiceNewBranch {
2016-08-11 15:48:08 +03:00
branchName = form . NewBranchName
}
2019-04-17 19:06:35 +03:00
ctx . Data [ "PageIsEdit" ] = true
ctx . Data [ "IsNewFile" ] = isNewFile
ctx . Data [ "RequireHighlightJS" ] = true
ctx . Data [ "RequireSimpleMDE" ] = true
2016-08-25 07:35:03 +03:00
ctx . Data [ "TreePath" ] = form . TreePath
2016-08-11 15:48:08 +03:00
ctx . Data [ "TreeNames" ] = treeNames
2017-06-22 18:30:47 +03:00
ctx . Data [ "TreePaths" ] = treePaths
2019-04-17 19:06:35 +03:00
ctx . Data [ "BranchLink" ] = ctx . Repo . RepoLink + "/src/branch/" + ctx . Repo . BranchName
2016-08-28 11:41:44 +03:00
ctx . Data [ "FileContent" ] = form . Content
2016-08-15 09:02:14 +03:00
ctx . Data [ "commit_summary" ] = form . CommitSummary
ctx . Data [ "commit_message" ] = form . CommitMessage
2016-08-28 11:41:44 +03:00
ctx . Data [ "commit_choice" ] = form . CommitChoice
2019-04-17 19:06:35 +03:00
ctx . Data [ "new_branch_name" ] = form . NewBranchName
ctx . Data [ "last_commit" ] = ctx . Repo . CommitID
2016-08-12 12:29:29 +03:00
ctx . Data [ "MarkdownFileExts" ] = strings . Join ( setting . Markdown . FileExtensions , "," )
ctx . Data [ "LineWrapExtensions" ] = strings . Join ( setting . Repository . Editor . LineWrapExtensions , "," )
ctx . Data [ "PreviewableFileModes" ] = strings . Join ( setting . Repository . Editor . PreviewableFileModes , "," )
2016-08-11 15:48:08 +03:00
if ctx . HasError ( ) {
2016-11-24 10:04:31 +03:00
ctx . HTML ( 200 , tplEditFile )
2016-08-11 15:48:08 +03:00
return
}
2019-04-17 19:06:35 +03:00
// Cannot commit to a an existing branch if user doesn't have rights
if branchName == ctx . Repo . BranchName && ! canCommit {
2017-05-02 03:49:55 +03:00
ctx . Data [ "Err_NewBranchName" ] = true
ctx . Data [ "commit_choice" ] = frmCommitChoiceNewBranch
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.cannot_commit_to_protected_branch" , branchName ) , tplEditFile , & form )
return
2016-08-11 15:48:08 +03:00
}
2019-04-17 19:06:35 +03:00
// CommitSummary is optional in the web form, if empty, give it a default message based on add or update
// `message` will be both the summary and message combined
2016-08-28 11:41:44 +03:00
message := strings . TrimSpace ( form . CommitSummary )
if len ( message ) == 0 {
2016-08-11 15:48:08 +03:00
if isNewFile {
2016-08-25 07:35:03 +03:00
message = ctx . Tr ( "repo.editor.add" , form . TreePath )
2016-08-11 15:48:08 +03:00
} else {
2016-08-25 07:35:03 +03:00
message = ctx . Tr ( "repo.editor.update" , form . TreePath )
2016-08-11 15:48:08 +03:00
}
}
2016-08-15 09:02:14 +03:00
form . CommitMessage = strings . TrimSpace ( form . CommitMessage )
if len ( form . CommitMessage ) > 0 {
message += "\n\n" + form . CommitMessage
2016-08-11 15:48:08 +03:00
}
2019-04-17 19:06:35 +03:00
if _ , err := repofiles . CreateOrUpdateRepoFile ( ctx . Repo . Repository , ctx . User , & repofiles . UpdateRepoFileOptions {
LastCommitID : form . LastCommit ,
OldBranch : ctx . Repo . BranchName ,
2016-08-15 09:02:14 +03:00
NewBranch : branchName ,
2019-04-17 19:06:35 +03:00
FromTreePath : ctx . Repo . TreePath ,
TreePath : form . TreePath ,
2016-08-15 09:02:14 +03:00
Message : message ,
2016-08-28 15:32:10 +03:00
Content : strings . Replace ( form . Content , "\r" , "" , - 1 ) ,
2016-08-15 09:02:14 +03:00
IsNewFile : isNewFile ,
} ) ; err != nil {
2019-04-17 19:06:35 +03:00
// This is where we handle all the errors thrown by repofiles.CreateOrUpdateRepoFile
if git . IsErrNotExist ( err ) {
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.file_editing_no_longer_exists" , ctx . Repo . TreePath ) , tplEditFile , & form )
} else if models . IsErrFilenameInvalid ( err ) {
ctx . Data [ "Err_TreePath" ] = true
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.filename_is_invalid" , form . TreePath ) , tplEditFile , & form )
} else if models . IsErrFilePathInvalid ( err ) {
ctx . Data [ "Err_TreePath" ] = true
if fileErr , ok := err . ( models . ErrFilePathInvalid ) ; ok {
switch fileErr . Type {
case git . EntryModeSymlink :
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.file_is_a_symlink" , fileErr . Path ) , tplEditFile , & form )
case git . EntryModeTree :
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.filename_is_a_directory" , fileErr . Path ) , tplEditFile , & form )
case git . EntryModeBlob :
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.directory_is_a_file" , fileErr . Path ) , tplEditFile , & form )
default :
ctx . Error ( 500 , err . Error ( ) )
}
} else {
ctx . Error ( 500 , err . Error ( ) )
}
} else if models . IsErrRepoFileAlreadyExists ( err ) {
ctx . Data [ "Err_TreePath" ] = true
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.file_already_exists" , form . TreePath ) , tplEditFile , & form )
2019-04-19 15:17:27 +03:00
} else if git . IsErrBranchNotExist ( err ) {
2019-04-17 19:06:35 +03:00
// For when a user adds/updates a file to a branch that no longer exists
2019-04-19 15:17:27 +03:00
if branchErr , ok := err . ( git . ErrBranchNotExist ) ; ok {
2019-04-17 19:06:35 +03:00
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.branch_does_not_exist" , branchErr . Name ) , tplEditFile , & form )
} else {
ctx . Error ( 500 , err . Error ( ) )
}
} else if models . IsErrBranchAlreadyExists ( err ) {
// For when a user specifies a new branch that already exists
ctx . Data [ "Err_NewBranchName" ] = true
if branchErr , ok := err . ( models . ErrBranchAlreadyExists ) ; ok {
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.branch_already_exists" , branchErr . BranchName ) , tplEditFile , & form )
} else {
ctx . Error ( 500 , err . Error ( ) )
}
2020-02-22 16:08:48 +03:00
} else if models . IsErrCommitIDDoesNotMatch ( err ) || models . IsErrMergePushOutOfDate ( err ) {
2019-04-17 19:06:35 +03:00
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.file_changed_while_editing" , ctx . Repo . RepoLink + "/compare/" + form . LastCommit + "..." + ctx . Repo . CommitID ) , tplEditFile , & form )
2020-02-22 16:08:48 +03:00
} else if models . IsErrPushRejected ( err ) {
errPushRej := err . ( models . ErrPushRejected )
if len ( errPushRej . Message ) == 0 {
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.push_rejected_no_message" ) , tplEditFile , & form )
} else {
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.push_rejected" , utils . SanitizeFlashErrorString ( errPushRej . Message ) ) , tplEditFile , & form )
}
2019-04-17 19:06:35 +03:00
} else {
2020-02-22 16:08:48 +03:00
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.fail_to_update_file" , form . TreePath , utils . SanitizeFlashErrorString ( err . Error ( ) ) ) , tplEditFile , & form )
2019-04-17 19:06:35 +03:00
}
2019-07-17 21:40:28 +03:00
}
2019-10-05 05:09:19 +03:00
if form . CommitChoice == frmCommitChoiceNewBranch && ctx . Repo . Repository . UnitEnabled ( models . UnitTypePullRequests ) {
2019-07-17 21:40:28 +03:00
ctx . Redirect ( ctx . Repo . RepoLink + "/compare/" + ctx . Repo . BranchName + "..." + form . NewBranchName )
2019-04-17 19:06:35 +03:00
} else {
ctx . Redirect ( ctx . Repo . RepoLink + "/src/branch/" + util . PathEscapeSegments ( branchName ) + "/" + util . PathEscapeSegments ( form . TreePath ) )
2016-08-11 15:48:08 +03:00
}
2016-08-15 09:02:14 +03:00
}
2016-08-11 15:48:08 +03:00
2016-11-24 10:04:31 +03:00
// EditFilePost response for editing file
2016-08-15 09:02:14 +03:00
func EditFilePost ( ctx * context . Context , form auth . EditRepoFileForm ) {
editFilePost ( ctx , form , false )
}
2016-11-24 10:04:31 +03:00
// NewFilePost response for creating file
2016-08-15 09:02:14 +03:00
func NewFilePost ( ctx * context . Context , form auth . EditRepoFileForm ) {
editFilePost ( ctx , form , true )
2016-08-11 15:48:08 +03:00
}
2016-11-24 10:04:31 +03:00
// DiffPreviewPost render preview diff page
2016-08-11 15:48:08 +03:00
func DiffPreviewPost ( ctx * context . Context , form auth . EditPreviewDiffForm ) {
2019-02-12 16:07:31 +03:00
treePath := cleanUploadFileName ( ctx . Repo . TreePath )
if len ( treePath ) == 0 {
ctx . Error ( 500 , "file name to diff is invalid" )
return
}
2016-08-11 15:48:08 +03:00
2016-08-28 11:41:44 +03:00
entry , err := ctx . Repo . Commit . GetTreeEntryByPath ( treePath )
2016-08-15 09:02:14 +03:00
if err != nil {
2016-08-28 14:31:42 +03:00
ctx . Error ( 500 , "GetTreeEntryByPath: " + err . Error ( ) )
2016-08-15 09:02:14 +03:00
return
2016-08-28 14:31:42 +03:00
} else if entry . IsDir ( ) {
2016-08-15 09:02:14 +03:00
ctx . Error ( 422 )
2016-08-11 15:48:08 +03:00
return
}
2019-04-17 19:06:35 +03:00
diff , err := repofiles . GetDiffPreview ( ctx . Repo . Repository , ctx . Repo . BranchName , treePath , form . Content )
2016-08-11 15:48:08 +03:00
if err != nil {
2016-08-15 09:02:14 +03:00
ctx . Error ( 500 , "GetDiffPreview: " + err . Error ( ) )
2016-08-11 15:48:08 +03:00
return
}
if diff . NumFiles ( ) == 0 {
2016-08-15 09:02:14 +03:00
ctx . PlainText ( 200 , [ ] byte ( ctx . Tr ( "repo.editor.no_changes_to_show" ) ) )
2016-08-11 15:48:08 +03:00
return
}
ctx . Data [ "File" ] = diff . Files [ 0 ]
2016-11-24 10:04:31 +03:00
ctx . HTML ( 200 , tplEditDiffPreview )
2016-08-28 11:41:44 +03:00
}
2016-11-24 10:04:31 +03:00
// DeleteFile render delete file page
2016-08-28 11:41:44 +03:00
func DeleteFile ( ctx * context . Context ) {
ctx . Data [ "PageIsDelete" ] = true
2017-10-30 05:04:25 +03:00
ctx . Data [ "BranchLink" ] = ctx . Repo . RepoLink + "/src/" + ctx . Repo . BranchNameSubURL ( )
2019-02-12 16:07:31 +03:00
treePath := cleanUploadFileName ( ctx . Repo . TreePath )
if treePath != ctx . Repo . TreePath {
2019-04-17 03:07:13 +03:00
ctx . Redirect ( path . Join ( ctx . Repo . RepoLink , "_delete" , util . PathEscapeSegments ( ctx . Repo . BranchName ) , util . PathEscapeSegments ( treePath ) ) )
2019-02-12 16:07:31 +03:00
return
}
ctx . Data [ "TreePath" ] = treePath
2017-05-02 03:49:55 +03:00
canCommit := renderCommitRights ( ctx )
2016-08-28 11:41:44 +03:00
ctx . Data [ "commit_summary" ] = ""
ctx . Data [ "commit_message" ] = ""
2019-04-17 19:06:35 +03:00
ctx . Data [ "last_commit" ] = ctx . Repo . CommitID
2017-05-02 03:49:55 +03:00
if canCommit {
ctx . Data [ "commit_choice" ] = frmCommitChoiceDirect
} else {
ctx . Data [ "commit_choice" ] = frmCommitChoiceNewBranch
}
2019-07-17 21:40:28 +03:00
ctx . Data [ "new_branch_name" ] = GetUniquePatchBranchName ( ctx )
2017-05-02 03:49:55 +03:00
2016-11-24 10:04:31 +03:00
ctx . HTML ( 200 , tplDeleteFile )
2016-08-11 15:48:08 +03:00
}
2016-08-15 09:38:35 +03:00
2016-11-24 10:04:31 +03:00
// DeleteFilePost response for deleting file
2016-08-15 09:38:35 +03:00
func DeleteFilePost ( ctx * context . Context , form auth . DeleteRepoFileForm ) {
2017-05-02 03:49:55 +03:00
canCommit := renderCommitRights ( ctx )
2019-04-17 19:06:35 +03:00
branchName := ctx . Repo . BranchName
2017-05-02 03:49:55 +03:00
if form . CommitChoice == frmCommitChoiceNewBranch {
2016-08-28 11:41:44 +03:00
branchName = form . NewBranchName
}
2019-04-17 19:06:35 +03:00
ctx . Data [ "PageIsDelete" ] = true
ctx . Data [ "BranchLink" ] = ctx . Repo . RepoLink + "/src/" + ctx . Repo . BranchNameSubURL ( )
ctx . Data [ "TreePath" ] = ctx . Repo . TreePath
2016-08-28 11:41:44 +03:00
ctx . Data [ "commit_summary" ] = form . CommitSummary
ctx . Data [ "commit_message" ] = form . CommitMessage
ctx . Data [ "commit_choice" ] = form . CommitChoice
2019-04-17 19:06:35 +03:00
ctx . Data [ "new_branch_name" ] = form . NewBranchName
ctx . Data [ "last_commit" ] = ctx . Repo . CommitID
2016-08-15 09:38:35 +03:00
if ctx . HasError ( ) {
2016-11-24 10:04:31 +03:00
ctx . HTML ( 200 , tplDeleteFile )
2016-08-15 09:38:35 +03:00
return
}
2019-07-17 21:40:28 +03:00
if branchName == ctx . Repo . BranchName && ! canCommit {
2017-05-02 03:49:55 +03:00
ctx . Data [ "Err_NewBranchName" ] = true
ctx . Data [ "commit_choice" ] = frmCommitChoiceNewBranch
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.cannot_commit_to_protected_branch" , branchName ) , tplDeleteFile , & form )
return
2016-08-28 11:41:44 +03:00
}
message := strings . TrimSpace ( form . CommitSummary )
if len ( message ) == 0 {
2016-08-30 15:07:50 +03:00
message = ctx . Tr ( "repo.editor.delete" , ctx . Repo . TreePath )
2016-08-28 11:41:44 +03:00
}
form . CommitMessage = strings . TrimSpace ( form . CommitMessage )
if len ( form . CommitMessage ) > 0 {
message += "\n\n" + form . CommitMessage
}
2019-04-17 19:06:35 +03:00
if _ , err := repofiles . DeleteRepoFile ( ctx . Repo . Repository , ctx . User , & repofiles . DeleteRepoFileOptions {
LastCommitID : form . LastCommit ,
OldBranch : ctx . Repo . BranchName ,
2016-08-28 11:41:44 +03:00
NewBranch : branchName ,
2016-08-30 15:07:50 +03:00
TreePath : ctx . Repo . TreePath ,
2016-08-28 11:41:44 +03:00
Message : message ,
2016-08-17 09:06:38 +03:00
} ) ; err != nil {
2019-04-17 19:06:35 +03:00
// This is where we handle all the errors thrown by repofiles.DeleteRepoFile
if git . IsErrNotExist ( err ) || models . IsErrRepoFileDoesNotExist ( err ) {
2019-07-17 21:40:28 +03:00
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.file_deleting_no_longer_exists" , ctx . Repo . TreePath ) , tplDeleteFile , & form )
2019-04-17 19:06:35 +03:00
} else if models . IsErrFilenameInvalid ( err ) {
ctx . Data [ "Err_TreePath" ] = true
2019-07-17 21:40:28 +03:00
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.filename_is_invalid" , ctx . Repo . TreePath ) , tplDeleteFile , & form )
2019-04-17 19:06:35 +03:00
} else if models . IsErrFilePathInvalid ( err ) {
ctx . Data [ "Err_TreePath" ] = true
if fileErr , ok := err . ( models . ErrFilePathInvalid ) ; ok {
switch fileErr . Type {
case git . EntryModeSymlink :
2019-07-17 21:40:28 +03:00
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.file_is_a_symlink" , fileErr . Path ) , tplDeleteFile , & form )
2019-04-17 19:06:35 +03:00
case git . EntryModeTree :
2019-07-17 21:40:28 +03:00
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.filename_is_a_directory" , fileErr . Path ) , tplDeleteFile , & form )
2019-04-17 19:06:35 +03:00
case git . EntryModeBlob :
2019-07-17 21:40:28 +03:00
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.directory_is_a_file" , fileErr . Path ) , tplDeleteFile , & form )
2019-04-17 19:06:35 +03:00
default :
ctx . ServerError ( "DeleteRepoFile" , err )
}
} else {
ctx . ServerError ( "DeleteRepoFile" , err )
}
2019-04-19 15:17:27 +03:00
} else if git . IsErrBranchNotExist ( err ) {
2019-04-17 19:06:35 +03:00
// For when a user deletes a file to a branch that no longer exists
2019-04-19 15:17:27 +03:00
if branchErr , ok := err . ( git . ErrBranchNotExist ) ; ok {
2019-07-17 21:40:28 +03:00
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.branch_does_not_exist" , branchErr . Name ) , tplDeleteFile , & form )
2019-04-17 19:06:35 +03:00
} else {
ctx . Error ( 500 , err . Error ( ) )
}
} else if models . IsErrBranchAlreadyExists ( err ) {
// For when a user specifies a new branch that already exists
if branchErr , ok := err . ( models . ErrBranchAlreadyExists ) ; ok {
2019-07-17 21:40:28 +03:00
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.branch_already_exists" , branchErr . BranchName ) , tplDeleteFile , & form )
2019-04-17 19:06:35 +03:00
} else {
ctx . Error ( 500 , err . Error ( ) )
}
2020-02-22 16:08:48 +03:00
} else if models . IsErrCommitIDDoesNotMatch ( err ) || models . IsErrMergePushOutOfDate ( err ) {
2019-07-17 21:40:28 +03:00
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.file_changed_while_deleting" , ctx . Repo . RepoLink + "/compare/" + form . LastCommit + "..." + ctx . Repo . CommitID ) , tplDeleteFile , & form )
2020-02-22 16:08:48 +03:00
} else if models . IsErrPushRejected ( err ) {
errPushRej := err . ( models . ErrPushRejected )
if len ( errPushRej . Message ) == 0 {
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.push_rejected_no_message" ) , tplDeleteFile , & form )
} else {
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.push_rejected" , utils . SanitizeFlashErrorString ( errPushRej . Message ) ) , tplDeleteFile , & form )
}
2019-04-17 19:06:35 +03:00
} else {
ctx . ServerError ( "DeleteRepoFile" , err )
}
2019-07-17 21:40:28 +03:00
}
ctx . Flash . Success ( ctx . Tr ( "repo.editor.file_delete_success" , ctx . Repo . TreePath ) )
2019-10-05 05:09:19 +03:00
if form . CommitChoice == frmCommitChoiceNewBranch && ctx . Repo . Repository . UnitEnabled ( models . UnitTypePullRequests ) {
2019-07-17 21:40:28 +03:00
ctx . Redirect ( ctx . Repo . RepoLink + "/compare/" + ctx . Repo . BranchName + "..." + form . NewBranchName )
2019-04-17 19:06:35 +03:00
} else {
2019-07-17 21:40:28 +03:00
treePath := filepath . Dir ( ctx . Repo . TreePath )
if treePath == "." {
treePath = "" // the file deleted was in the root, so we return the user to the root directory
}
if len ( treePath ) > 0 {
// Need to get the latest commit since it changed
commit , err := ctx . Repo . GitRepo . GetBranchCommit ( ctx . Repo . BranchName )
if err == nil && commit != nil {
// We have the comment, now find what directory we can return the user to
// (must have entries)
treePath = GetClosestParentWithFiles ( treePath , commit )
} else {
treePath = "" // otherwise return them to the root of the repo
}
}
ctx . Redirect ( ctx . Repo . RepoLink + "/src/branch/" + util . PathEscapeSegments ( branchName ) + "/" + util . PathEscapeSegments ( treePath ) )
2016-08-15 09:38:35 +03:00
}
}
2016-08-30 15:07:50 +03:00
func renderUploadSettings ( ctx * context . Context ) {
ctx . Data [ "RequireDropzone" ] = true
2019-12-18 06:59:59 +03:00
ctx . Data [ "RequireTribute" ] = true
ctx . Data [ "RequireSimpleMDE" ] = true
2016-08-30 15:07:50 +03:00
ctx . Data [ "UploadAllowedTypes" ] = strings . Join ( setting . Repository . Upload . AllowedTypes , "," )
ctx . Data [ "UploadMaxSize" ] = setting . Repository . Upload . FileMaxSize
ctx . Data [ "UploadMaxFiles" ] = setting . Repository . Upload . MaxFiles
}
2016-11-24 10:04:31 +03:00
// UploadFile render upload file page
2016-08-30 15:07:50 +03:00
func UploadFile ( ctx * context . Context ) {
ctx . Data [ "PageIsUpload" ] = true
renderUploadSettings ( ctx )
2017-05-02 03:49:55 +03:00
canCommit := renderCommitRights ( ctx )
2019-02-12 16:07:31 +03:00
treePath := cleanUploadFileName ( ctx . Repo . TreePath )
if treePath != ctx . Repo . TreePath {
2019-04-17 03:07:13 +03:00
ctx . Redirect ( path . Join ( ctx . Repo . RepoLink , "_upload" , util . PathEscapeSegments ( ctx . Repo . BranchName ) , util . PathEscapeSegments ( treePath ) ) )
2019-02-12 16:07:31 +03:00
return
}
ctx . Repo . TreePath = treePath
2016-08-30 15:07:50 +03:00
2017-06-22 18:30:47 +03:00
treeNames , treePaths := getParentTreeFields ( ctx . Repo . TreePath )
if len ( treeNames ) == 0 {
// We must at least have one element for user to input.
treeNames = [ ] string { "" }
2016-08-30 15:07:50 +03:00
}
ctx . Data [ "TreeNames" ] = treeNames
2017-06-22 18:30:47 +03:00
ctx . Data [ "TreePaths" ] = treePaths
2017-10-30 05:04:25 +03:00
ctx . Data [ "BranchLink" ] = ctx . Repo . RepoLink + "/src/" + ctx . Repo . BranchNameSubURL ( )
2016-08-30 15:07:50 +03:00
ctx . Data [ "commit_summary" ] = ""
ctx . Data [ "commit_message" ] = ""
2017-05-02 03:49:55 +03:00
if canCommit {
ctx . Data [ "commit_choice" ] = frmCommitChoiceDirect
} else {
ctx . Data [ "commit_choice" ] = frmCommitChoiceNewBranch
}
2019-07-17 21:40:28 +03:00
ctx . Data [ "new_branch_name" ] = GetUniquePatchBranchName ( ctx )
2016-08-30 15:07:50 +03:00
2016-11-24 10:04:31 +03:00
ctx . HTML ( 200 , tplUploadFile )
2016-08-30 15:07:50 +03:00
}
2016-11-24 10:04:31 +03:00
// UploadFilePost response for uploading file
2016-08-30 15:07:50 +03:00
func UploadFilePost ( ctx * context . Context , form auth . UploadRepoFileForm ) {
ctx . Data [ "PageIsUpload" ] = true
renderUploadSettings ( ctx )
2017-05-02 03:49:55 +03:00
canCommit := renderCommitRights ( ctx )
2016-08-30 15:07:50 +03:00
oldBranchName := ctx . Repo . BranchName
branchName := oldBranchName
2017-05-02 03:49:55 +03:00
if form . CommitChoice == frmCommitChoiceNewBranch {
2016-08-30 15:07:50 +03:00
branchName = form . NewBranchName
}
2019-01-04 19:08:26 +03:00
form . TreePath = cleanUploadFileName ( form . TreePath )
2017-06-22 18:30:47 +03:00
treeNames , treePaths := getParentTreeFields ( form . TreePath )
if len ( treeNames ) == 0 {
// We must at least have one element for user to input.
treeNames = [ ] string { "" }
2016-08-30 15:07:50 +03:00
}
ctx . Data [ "TreePath" ] = form . TreePath
ctx . Data [ "TreeNames" ] = treeNames
2017-06-22 18:30:47 +03:00
ctx . Data [ "TreePaths" ] = treePaths
2017-10-30 05:04:25 +03:00
ctx . Data [ "BranchLink" ] = ctx . Repo . RepoLink + "/src/branch/" + branchName
2016-08-30 15:07:50 +03:00
ctx . Data [ "commit_summary" ] = form . CommitSummary
ctx . Data [ "commit_message" ] = form . CommitMessage
ctx . Data [ "commit_choice" ] = form . CommitChoice
ctx . Data [ "new_branch_name" ] = branchName
if ctx . HasError ( ) {
2016-11-24 10:04:31 +03:00
ctx . HTML ( 200 , tplUploadFile )
2016-08-30 15:07:50 +03:00
return
}
if oldBranchName != branchName {
2020-01-14 06:38:04 +03:00
if _ , err := repo_module . GetBranch ( ctx . Repo . Repository , branchName ) ; err == nil {
2016-08-30 15:07:50 +03:00
ctx . Data [ "Err_NewBranchName" ] = true
2016-11-24 10:04:31 +03:00
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.branch_already_exists" , branchName ) , tplUploadFile , & form )
2016-08-30 15:07:50 +03:00
return
}
2017-05-02 03:49:55 +03:00
} else if ! canCommit {
ctx . Data [ "Err_NewBranchName" ] = true
ctx . Data [ "commit_choice" ] = frmCommitChoiceNewBranch
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.cannot_commit_to_protected_branch" , branchName ) , tplUploadFile , & form )
return
2016-08-30 15:07:50 +03:00
}
var newTreePath string
for _ , part := range treeNames {
newTreePath = path . Join ( newTreePath , part )
entry , err := ctx . Repo . Commit . GetTreeEntryByPath ( newTreePath )
if err != nil {
if git . IsErrNotExist ( err ) {
// Means there is no item with that name, so we're good
break
}
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "Repo.Commit.GetTreeEntryByPath" , err )
2016-08-30 15:07:50 +03:00
return
}
// User can only upload files to a directory.
if ! entry . IsDir ( ) {
ctx . Data [ "Err_TreePath" ] = true
2016-11-24 10:04:31 +03:00
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.directory_is_a_file" , part ) , tplUploadFile , & form )
2016-08-30 15:07:50 +03:00
return
}
}
message := strings . TrimSpace ( form . CommitSummary )
if len ( message ) == 0 {
message = ctx . Tr ( "repo.editor.upload_files_to_dir" , form . TreePath )
}
form . CommitMessage = strings . TrimSpace ( form . CommitMessage )
if len ( form . CommitMessage ) > 0 {
message += "\n\n" + form . CommitMessage
}
2019-04-17 19:06:35 +03:00
if err := repofiles . UploadRepoFiles ( ctx . Repo . Repository , ctx . User , & repofiles . UploadRepoFileOptions {
2016-08-30 15:07:50 +03:00
LastCommitID : ctx . Repo . CommitID ,
OldBranch : oldBranchName ,
NewBranch : branchName ,
TreePath : form . TreePath ,
Message : message ,
Files : form . Files ,
} ) ; err != nil {
ctx . Data [ "Err_TreePath" ] = true
2019-11-02 10:37:05 +03:00
if models . IsErrLFSFileLocked ( err ) {
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.upload_file_is_locked" , err . ( models . ErrLFSFileLocked ) . Path , err . ( models . ErrLFSFileLocked ) . UserName ) , tplUploadFile , & form )
} else {
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.unable_to_upload_files" , form . TreePath , err ) , tplUploadFile , & form )
}
2016-08-30 15:07:50 +03:00
return
}
2019-10-05 05:09:19 +03:00
if form . CommitChoice == frmCommitChoiceNewBranch && ctx . Repo . Repository . UnitEnabled ( models . UnitTypePullRequests ) {
2019-07-17 21:40:28 +03:00
ctx . Redirect ( ctx . Repo . RepoLink + "/compare/" + ctx . Repo . BranchName + "..." + form . NewBranchName )
} else {
ctx . Redirect ( ctx . Repo . RepoLink + "/src/branch/" + util . PathEscapeSegments ( branchName ) + "/" + util . PathEscapeSegments ( form . TreePath ) )
}
2016-08-30 15:07:50 +03:00
}
2018-12-21 05:32:11 +03:00
func cleanUploadFileName ( name string ) string {
2019-02-12 16:07:31 +03:00
// Rebase the filename
name = strings . Trim ( path . Clean ( "/" + name ) , " /" )
// Git disallows any filenames to have a .git directory in them.
for _ , part := range strings . Split ( name , "/" ) {
if strings . ToLower ( part ) == ".git" {
return ""
}
2018-12-21 05:32:11 +03:00
}
return name
}
2016-11-24 10:04:31 +03:00
// UploadFileToServer upload file to server file dir not git
2016-08-30 15:07:50 +03:00
func UploadFileToServer ( ctx * context . Context ) {
file , header , err := ctx . Req . FormFile ( "file" )
if err != nil {
ctx . Error ( 500 , fmt . Sprintf ( "FormFile: %v" , err ) )
return
}
defer file . Close ( )
buf := make ( [ ] byte , 1024 )
n , _ := file . Read ( buf )
if n > 0 {
buf = buf [ : n ]
}
if len ( setting . Repository . Upload . AllowedTypes ) > 0 {
2019-07-07 05:25:05 +03:00
err = upload . VerifyAllowedContentType ( buf , setting . Repository . Upload . AllowedTypes )
if err != nil {
ctx . Error ( 400 , err . Error ( ) )
2016-08-30 15:07:50 +03:00
return
}
}
2018-12-21 05:32:11 +03:00
name := cleanUploadFileName ( header . Filename )
if len ( name ) == 0 {
ctx . Error ( 500 , "Upload file name is invalid" )
return
}
upload , err := models . NewUpload ( name , buf , file )
2016-08-30 15:07:50 +03:00
if err != nil {
ctx . Error ( 500 , fmt . Sprintf ( "NewUpload: %v" , err ) )
return
}
log . Trace ( "New file uploaded: %s" , upload . UUID )
ctx . JSON ( 200 , map [ string ] string {
"uuid" : upload . UUID ,
} )
}
2016-11-24 10:04:31 +03:00
// RemoveUploadFileFromServer remove file from server file dir
2016-08-30 15:07:50 +03:00
func RemoveUploadFileFromServer ( ctx * context . Context , form auth . RemoveUploadFileForm ) {
if len ( form . File ) == 0 {
ctx . Status ( 204 )
return
}
if err := models . DeleteUploadByUUID ( form . File ) ; err != nil {
ctx . Error ( 500 , fmt . Sprintf ( "DeleteUploadByUUID: %v" , err ) )
return
}
log . Trace ( "Upload file removed: %s" , form . File )
ctx . Status ( 204 )
}
2019-07-17 21:40:28 +03:00
// GetUniquePatchBranchName Gets a unique branch name for a new patch branch
// It will be in the form of <username>-patch-<num> where <num> is the first branch of this format
// that doesn't already exist. If we exceed 1000 tries or an error is thrown, we just return "" so the user has to
// type in the branch name themselves (will be an empty field)
func GetUniquePatchBranchName ( ctx * context . Context ) string {
prefix := ctx . User . LowerName + "-patch-"
for i := 1 ; i <= 1000 ; i ++ {
branchName := fmt . Sprintf ( "%s%d" , prefix , i )
2020-01-14 06:38:04 +03:00
if _ , err := repo_module . GetBranch ( ctx . Repo . Repository , branchName ) ; err != nil {
2019-07-17 21:40:28 +03:00
if git . IsErrBranchNotExist ( err ) {
return branchName
}
log . Error ( "GetUniquePatchBranchName: %v" , err )
return ""
}
}
return ""
}
// GetClosestParentWithFiles Recursively gets the path of parent in a tree that has files (used when file in a tree is
// deleted). Returns "" for the root if no parents other than the root have files. If the given treePath isn't a
// SubTree or it has no entries, we go up one dir and see if we can return the user to that listing.
func GetClosestParentWithFiles ( treePath string , commit * git . Commit ) string {
if len ( treePath ) == 0 || treePath == "." {
return ""
}
// see if the tree has entries
if tree , err := commit . SubTree ( treePath ) ; err != nil {
// failed to get tree, going up a dir
return GetClosestParentWithFiles ( filepath . Dir ( treePath ) , commit )
} else if entries , err := tree . ListEntries ( ) ; err != nil || len ( entries ) == 0 {
// no files in this dir, going up a dir
return GetClosestParentWithFiles ( filepath . Dir ( treePath ) , commit )
}
return treePath
}