2016-08-11 15:48:08 +03:00
// Copyright 2016 The Gogs Authors. All rights reserved.
2022-11-27 21:20:29 +03:00
// SPDX-License-Identifier: MIT
2016-08-11 15:48:08 +03:00
package repo
import (
2016-08-30 15:07:50 +03:00
"fmt"
2021-09-22 08:38:34 +03:00
"io"
2021-04-05 18:30:52 +03:00
"net/http"
2016-08-11 15:48:08 +03:00
"path"
"strings"
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/models"
2022-06-12 18:51:54 +03:00
git_model "code.gitea.io/gitea/models/git"
2022-08-25 05:31:57 +03:00
repo_model "code.gitea.io/gitea/models/repo"
2021-11-09 22:57:58 +03:00
"code.gitea.io/gitea/models/unit"
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/modules/base"
2019-08-15 15:07:28 +03:00
"code.gitea.io/gitea/modules/charset"
2019-03-27 12:33:00 +03:00
"code.gitea.io/gitea/modules/git"
2021-07-24 19:03:58 +03:00
"code.gitea.io/gitea/modules/json"
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/modules/log"
2023-03-24 09:12:23 +03:00
"code.gitea.io/gitea/modules/markup"
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/modules/setting"
2021-06-05 15:32:19 +03:00
"code.gitea.io/gitea/modules/typesniffer"
2019-04-17 03:07:13 +03:00
"code.gitea.io/gitea/modules/util"
2021-01-26 18:36:53 +03:00
"code.gitea.io/gitea/modules/web"
2020-02-22 16:08:48 +03:00
"code.gitea.io/gitea/routers/utils"
2024-02-27 10:12:22 +03:00
"code.gitea.io/gitea/services/context"
"code.gitea.io/gitea/services/context/upload"
2021-04-06 22:44:05 +03:00
"code.gitea.io/gitea/services/forms"
2021-11-24 10:56:24 +03:00
files_service "code.gitea.io/gitea/services/repository/files"
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
)
2023-05-25 00:36:02 +03:00
func canCreateBasePullRequest ( ctx * context . Context ) bool {
baseRepo := ctx . Repo . Repository . BaseRepo
return baseRepo != nil && baseRepo . UnitEnabled ( ctx , unit . TypePullRequests )
}
2017-05-02 03:49:55 +03:00
func renderCommitRights ( ctx * context . Context ) bool {
2022-03-22 10:03:22 +03:00
canCommitToBranch , err := ctx . Repo . CanCommitToBranch ( ctx , ctx . Doer )
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
2023-05-25 00:36:02 +03:00
ctx . Data [ "CanCreatePullRequest" ] = ctx . Repo . Repository . UnitEnabled ( ctx , unit . TypePullRequests ) || canCreateBasePullRequest ( ctx )
2020-01-15 11:32:57 +03:00
return canCommitToBranch . CanCommitToBranch
2017-05-02 03:49:55 +03:00
}
2023-05-25 00:36:02 +03:00
// redirectForCommitChoice redirects after committing the edit to a branch
func redirectForCommitChoice ( ctx * context . Context , commitChoice , newBranchName , treePath string ) {
if commitChoice == frmCommitChoiceNewBranch {
// Redirect to a pull request when possible
redirectToPullRequest := false
repo := ctx . Repo . Repository
baseBranch := ctx . Repo . BranchName
headBranch := newBranchName
if repo . UnitEnabled ( ctx , unit . TypePullRequests ) {
redirectToPullRequest = true
} else if canCreateBasePullRequest ( ctx ) {
redirectToPullRequest = true
baseBranch = repo . BaseRepo . DefaultBranch
headBranch = repo . Owner . Name + "/" + repo . Name + ":" + headBranch
repo = repo . BaseRepo
}
if redirectToPullRequest {
ctx . Redirect ( repo . Link ( ) + "/compare/" + util . PathEscapeSegments ( baseBranch ) + "..." + util . PathEscapeSegments ( headBranch ) )
return
}
}
2024-03-27 05:34:10 +03:00
returnURI := ctx . FormString ( "return_uri" )
ctx . RedirectToCurrentSite (
returnURI ,
ctx . Repo . RepoLink + "/src/branch/" + util . PathEscapeSegments ( newBranchName ) + "/" + util . PathEscapeSegments ( treePath ) ,
)
2023-05-25 00:36:02 +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.
2021-12-20 07:41:31 +03:00
func getParentTreeFields ( treePath string ) ( treeNames , treePaths [ ] string ) {
2017-06-22 18:30:47 +03:00
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 ) {
2024-03-27 05:34:10 +03:00
ctx . Data [ "PageIsViewCode" ] = true
2016-08-11 15:48:08 +03:00
ctx . Data [ "PageIsEdit" ] = true
ctx . Data [ "IsNewFile" ] = isNewFile
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
}
2021-07-29 06:39:46 +03:00
// Check if the filename (and additional path) is specified in the querystring
2023-04-19 16:40:42 +03:00
// (filename is a misnomer, but kept for compatibility with GitHub)
2021-07-29 06:39:46 +03:00
filePath , fileName := path . Split ( ctx . Req . URL . Query ( ) . Get ( "filename" ) )
filePath = strings . Trim ( filePath , "/" )
treeNames , treePaths := getParentTreeFields ( path . Join ( ctx . Repo . TreePath , filePath ) )
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 {
2023-09-29 10:42:39 +03:00
HandleGitError ( ctx , "Repo.Commit.GetTreeEntryByPath" , 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
}
2021-05-30 19:38:44 +03:00
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 )
2021-10-25 00:12:43 +03:00
n , _ := util . ReadAtMost ( dataRc , buf )
2016-08-31 23:59:23 +03:00
buf = buf [ : n ]
2016-08-11 15:48:08 +03:00
2021-01-13 06:45:19 +03:00
// Only some file types are editable online as text.
2021-06-05 15:32:19 +03:00
if ! typesniffer . DetectContentType ( buf ) . IsRepresentableAsText ( ) {
ctx . NotFound ( "typesniffer.IsRepresentableAsText" , nil )
2016-08-11 15:48:08 +03:00
return
}
2021-09-22 08:38:34 +03:00
d , _ := io . ReadAll ( dataRc )
2021-05-30 19:38:44 +03:00
2016-08-11 15:48:08 +03:00
buf = append ( buf , d ... )
2024-01-27 21:02:51 +03:00
if content , err := charset . ToUTF8 ( buf , charset . ConvertOpts { KeepBOM : true } ) ; err != nil {
log . Error ( "ToUTF8: %v" , err )
2016-08-11 15:48:08 +03:00
ctx . Data [ "FileContent" ] = string ( buf )
} else {
ctx . Data [ "FileContent" ] = content
}
} else {
2021-07-29 06:39:46 +03:00
// Append filename from query, or empty string to allow user name the new file.
treeNames = append ( treeNames , fileName )
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
2023-03-24 09:12:23 +03:00
ctx . Data [ "PreviewableExtensions" ] = strings . Join ( markup . PreviewableExtensions ( ) , "," )
2016-08-12 12:29:29 +03:00
ctx . Data [ "LineWrapExtensions" ] = strings . Join ( setting . Repository . Editor . LineWrapExtensions , "," )
2023-08-08 13:44:19 +03:00
ctx . Data [ "EditorconfigJson" ] = GetEditorConfig ( ctx , treePath )
2016-08-11 15:48:08 +03:00
2024-03-27 05:34:10 +03:00
ctx . Data [ "IsEditingFileOnly" ] = ctx . FormString ( "return_uri" ) != ""
ctx . Data [ "ReturnURI" ] = ctx . FormString ( "return_uri" )
2021-04-05 18:30:52 +03:00
ctx . HTML ( http . StatusOK , tplEditFile )
2016-08-11 15:48:08 +03:00
}
2020-05-14 19:06:01 +03:00
// GetEditorConfig returns a editorconfig JSON string for given treePath or "null"
func GetEditorConfig ( ctx * context . Context , treePath string ) string {
2023-04-06 23:01:20 +03:00
ec , _ , err := ctx . Repo . GetEditorconfig ( )
2020-05-14 19:06:01 +03:00
if err == nil {
def , err := ec . GetDefinitionForFilename ( treePath )
if err == nil {
jsonStr , _ := json . Marshal ( def )
return string ( jsonStr )
}
}
return "null"
}
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
}
2021-04-06 22:44:05 +03:00
func editFilePost ( ctx * context . Context , form forms . 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
2020-08-30 00:32:46 +03:00
ctx . Data [ "PageHasPosted" ] = true
2019-04-17 19:06:35 +03:00
ctx . Data [ "IsNewFile" ] = isNewFile
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
2021-11-16 21:18:25 +03:00
ctx . Data [ "BranchLink" ] = ctx . Repo . RepoLink + "/src/branch/" + util . PathEscapeSegments ( 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
2023-03-24 09:12:23 +03:00
ctx . Data [ "PreviewableExtensions" ] = strings . Join ( markup . PreviewableExtensions ( ) , "," )
2016-08-12 12:29:29 +03:00
ctx . Data [ "LineWrapExtensions" ] = strings . Join ( setting . Repository . Editor . LineWrapExtensions , "," )
2023-08-08 13:44:19 +03:00
ctx . Data [ "EditorconfigJson" ] = GetEditorConfig ( ctx , form . TreePath )
2016-08-11 15:48:08 +03:00
if ctx . HasError ( ) {
2021-04-05 18:30:52 +03:00
ctx . HTML ( http . StatusOK , 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 {
2024-02-15 00:48:45 +03:00
message = ctx . Locale . TrString ( "repo.editor.add" , form . TreePath )
2016-08-11 15:48:08 +03:00
} else {
2024-02-15 00:48:45 +03:00
message = ctx . Locale . TrString ( "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
}
2023-05-29 12:41:35 +03:00
operation := "update"
if isNewFile {
operation = "create"
}
if _ , err := files_service . ChangeRepoFiles ( ctx , ctx . Repo . Repository , ctx . Doer , & files_service . ChangeRepoFilesOptions {
2019-04-17 19:06:35 +03:00
LastCommitID : form . LastCommit ,
OldBranch : ctx . Repo . BranchName ,
2016-08-15 09:02:14 +03:00
NewBranch : branchName ,
Message : message ,
2023-05-29 12:41:35 +03:00
Files : [ ] * files_service . ChangeRepoFile {
{
2023-07-18 21:14:47 +03:00
Operation : operation ,
FromTreePath : ctx . Repo . TreePath ,
TreePath : form . TreePath ,
2023-11-22 12:14:16 +03:00
ContentReader : strings . NewReader ( strings . ReplaceAll ( form . Content , "\r" , "" ) ) ,
2023-05-29 12:41:35 +03:00
} ,
} ,
Signoff : form . Signoff ,
2016-08-15 09:02:14 +03:00
} ) ; err != nil {
2023-05-29 12:41:35 +03:00
// This is where we handle all the errors thrown by files_service.ChangeRepoFiles
2019-04-17 19:06:35 +03:00
if git . IsErrNotExist ( err ) {
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.file_editing_no_longer_exists" , ctx . Repo . TreePath ) , tplEditFile , & form )
2022-06-12 18:51:54 +03:00
} else if git_model . IsErrLFSFileLocked ( err ) {
2020-03-28 07:13:18 +03:00
ctx . Data [ "Err_TreePath" ] = true
2022-06-12 18:51:54 +03:00
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.upload_file_is_locked" , err . ( git_model . ErrLFSFileLocked ) . Path , err . ( git_model . ErrLFSFileLocked ) . UserName ) , tplEditFile , & form )
2019-04-17 19:06:35 +03:00
} 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 :
2024-09-24 22:06:52 +03:00
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.filename_is_invalid" , fileErr . Path ) , tplEditFile , & form )
2019-04-17 19:06:35 +03:00
}
} else {
2021-04-05 18:30:52 +03:00
ctx . Error ( http . StatusInternalServerError , err . Error ( ) )
2019-04-17 19:06:35 +03:00
}
} 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 {
2021-04-05 18:30:52 +03:00
ctx . Error ( http . StatusInternalServerError , err . Error ( ) )
2019-04-17 19:06:35 +03:00
}
2023-06-29 13:03:20 +03:00
} else if git_model . IsErrBranchAlreadyExists ( err ) {
2019-04-17 19:06:35 +03:00
// For when a user specifies a new branch that already exists
ctx . Data [ "Err_NewBranchName" ] = true
2023-06-29 13:03:20 +03:00
if branchErr , ok := err . ( git_model . ErrBranchAlreadyExists ) ; ok {
2019-04-17 19:06:35 +03:00
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.branch_already_exists" , branchErr . BranchName ) , tplEditFile , & form )
} else {
2021-04-05 18:30:52 +03:00
ctx . Error ( http . StatusInternalServerError , err . Error ( ) )
2019-04-17 19:06:35 +03:00
}
2020-03-28 07:13:18 +03:00
} else if models . IsErrCommitIDDoesNotMatch ( err ) {
2024-03-22 14:17:30 +03:00
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.commit_id_not_matching" ) , tplEditFile , & form )
2020-03-28 07:13:18 +03:00
} else if git . IsErrPushOutOfDate ( err ) {
2024-03-22 14:17:30 +03:00
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.push_out_of_date" ) , tplEditFile , & form )
2020-03-28 07:13:18 +03:00
} else if git . IsErrPushRejected ( err ) {
errPushRej := err . ( * git . ErrPushRejected )
2020-02-22 16:08:48 +03:00
if len ( errPushRej . Message ) == 0 {
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.push_rejected_no_message" ) , tplEditFile , & form )
} else {
2024-03-02 18:05:07 +03:00
flashError , err := ctx . RenderToHTML ( tplAlertDetails , map [ string ] any {
2020-10-21 02:50:10 +03:00
"Message" : ctx . Tr ( "repo.editor.push_rejected" ) ,
"Summary" : ctx . Tr ( "repo.editor.push_rejected_summary" ) ,
"Details" : utils . SanitizeFlashErrorString ( errPushRej . Message ) ,
} )
if err != nil {
ctx . ServerError ( "editFilePost.HTMLString" , err )
return
}
ctx . RenderWithErr ( flashError , tplEditFile , & form )
2020-02-22 16:08:48 +03:00
}
2019-04-17 19:06:35 +03:00
} else {
2024-03-02 18:05:07 +03:00
flashError , err := ctx . RenderToHTML ( tplAlertDetails , map [ string ] any {
2020-10-21 02:50:10 +03:00
"Message" : ctx . Tr ( "repo.editor.fail_to_update_file" , form . TreePath ) ,
"Summary" : ctx . Tr ( "repo.editor.fail_to_update_file_summary" ) ,
"Details" : utils . SanitizeFlashErrorString ( err . Error ( ) ) ,
} )
if err != nil {
ctx . ServerError ( "editFilePost.HTMLString" , err )
return
}
ctx . RenderWithErr ( flashError , tplEditFile , & form )
2019-04-17 19:06:35 +03:00
}
2019-07-17 21:40:28 +03:00
}
2023-04-19 16:40:42 +03:00
if ctx . Repo . Repository . IsEmpty {
2023-08-17 07:43:39 +03:00
if isEmpty , err := ctx . Repo . GitRepo . IsEmpty ( ) ; err == nil && ! isEmpty {
_ = repo_model . UpdateRepositoryCols ( ctx , & repo_model . Repository { ID : ctx . Repo . Repository . ID , IsEmpty : false } , "is_empty" )
}
2023-04-19 16:40:42 +03:00
}
2023-05-25 00:36:02 +03:00
redirectForCommitChoice ( ctx , form . CommitChoice , branchName , form . TreePath )
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
2021-01-26 18:36:53 +03:00
func EditFilePost ( ctx * context . Context ) {
2021-04-06 22:44:05 +03:00
form := web . GetForm ( ctx ) . ( * forms . EditRepoFileForm )
2021-01-26 18:36:53 +03:00
editFilePost ( ctx , * form , false )
2016-08-15 09:02:14 +03:00
}
2016-11-24 10:04:31 +03:00
// NewFilePost response for creating file
2021-01-26 18:36:53 +03:00
func NewFilePost ( ctx * context . Context ) {
2021-04-06 22:44:05 +03:00
form := web . GetForm ( ctx ) . ( * forms . EditRepoFileForm )
2021-01-26 18:36:53 +03:00
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
2021-01-26 18:36:53 +03:00
func DiffPreviewPost ( ctx * context . Context ) {
2021-04-06 22:44:05 +03:00
form := web . GetForm ( ctx ) . ( * forms . EditPreviewDiffForm )
2019-02-12 16:07:31 +03:00
treePath := cleanUploadFileName ( ctx . Repo . TreePath )
if len ( treePath ) == 0 {
2021-04-05 18:30:52 +03:00
ctx . Error ( http . StatusInternalServerError , "file name to diff is invalid" )
2019-02-12 16:07:31 +03:00
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 {
2021-04-05 18:30:52 +03:00
ctx . Error ( http . StatusInternalServerError , "GetTreeEntryByPath: " + err . Error ( ) )
2016-08-15 09:02:14 +03:00
return
2016-08-28 14:31:42 +03:00
} else if entry . IsDir ( ) {
2021-04-05 18:30:52 +03:00
ctx . Error ( http . StatusUnprocessableEntity )
2016-08-11 15:48:08 +03:00
return
}
2022-01-20 02:26:57 +03:00
diff , err := files_service . GetDiffPreview ( ctx , ctx . Repo . Repository , ctx . Repo . BranchName , treePath , form . Content )
2016-08-11 15:48:08 +03:00
if err != nil {
2021-04-05 18:30:52 +03:00
ctx . Error ( http . StatusInternalServerError , "GetDiffPreview: " + err . Error ( ) )
2016-08-11 15:48:08 +03:00
return
}
2024-04-29 23:53:15 +03:00
if diff . NumFiles != 0 {
ctx . Data [ "File" ] = diff . Files [ 0 ]
2016-08-11 15:48:08 +03:00
}
2021-04-05 18:30:52 +03:00
ctx . HTML ( http . StatusOK , 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
2021-04-05 18:30:52 +03:00
ctx . HTML ( http . StatusOK , 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
2021-01-26 18:36:53 +03:00
func DeleteFilePost ( ctx * context . Context ) {
2021-04-06 22:44:05 +03:00
form := web . GetForm ( ctx ) . ( * forms . 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 ( ) {
2021-04-05 18:30:52 +03:00
ctx . HTML ( http . StatusOK , 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 {
2024-02-15 00:48:45 +03:00
message = ctx . Locale . TrString ( "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
}
2023-05-29 12:41:35 +03:00
if _ , err := files_service . ChangeRepoFiles ( ctx , ctx . Repo . Repository , ctx . Doer , & files_service . ChangeRepoFilesOptions {
2019-04-17 19:06:35 +03:00
LastCommitID : form . LastCommit ,
OldBranch : ctx . Repo . BranchName ,
2016-08-28 11:41:44 +03:00
NewBranch : branchName ,
2023-05-29 12:41:35 +03:00
Files : [ ] * files_service . ChangeRepoFile {
{
Operation : "delete" ,
TreePath : ctx . Repo . TreePath ,
} ,
} ,
Message : message ,
Signoff : form . Signoff ,
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 {
2021-04-05 18:30:52 +03:00
ctx . Error ( http . StatusInternalServerError , err . Error ( ) )
2019-04-17 19:06:35 +03:00
}
2023-06-29 13:03:20 +03:00
} else if git_model . IsErrBranchAlreadyExists ( err ) {
2019-04-17 19:06:35 +03:00
// For when a user specifies a new branch that already exists
2023-06-29 13:03:20 +03:00
if branchErr , ok := err . ( git_model . 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 {
2021-04-05 18:30:52 +03:00
ctx . Error ( http . StatusInternalServerError , err . Error ( ) )
2019-04-17 19:06:35 +03:00
}
2020-03-28 07:13:18 +03:00
} else if models . IsErrCommitIDDoesNotMatch ( err ) || git . IsErrPushOutOfDate ( err ) {
2021-11-16 21:18:25 +03:00
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.file_changed_while_deleting" , ctx . Repo . RepoLink + "/compare/" + util . PathEscapeSegments ( form . LastCommit ) + "..." + util . PathEscapeSegments ( ctx . Repo . CommitID ) ) , tplDeleteFile , & form )
2020-03-28 07:13:18 +03:00
} else if git . IsErrPushRejected ( err ) {
errPushRej := err . ( * git . ErrPushRejected )
2020-02-22 16:08:48 +03:00
if len ( errPushRej . Message ) == 0 {
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.push_rejected_no_message" ) , tplDeleteFile , & form )
} else {
2024-03-02 18:05:07 +03:00
flashError , err := ctx . RenderToHTML ( tplAlertDetails , map [ string ] any {
2020-10-21 02:50:10 +03:00
"Message" : ctx . Tr ( "repo.editor.push_rejected" ) ,
"Summary" : ctx . Tr ( "repo.editor.push_rejected_summary" ) ,
"Details" : utils . SanitizeFlashErrorString ( errPushRej . Message ) ,
} )
if err != nil {
ctx . ServerError ( "DeleteFilePost.HTMLString" , err )
return
}
ctx . RenderWithErr ( flashError , tplDeleteFile , & form )
2020-02-22 16:08:48 +03:00
}
2019-04-17 19:06:35 +03:00
} else {
ctx . ServerError ( "DeleteRepoFile" , err )
}
2024-05-28 12:31:59 +03:00
return
2019-07-17 21:40:28 +03:00
}
ctx . Flash . Success ( ctx . Tr ( "repo.editor.file_delete_success" , ctx . Repo . TreePath ) )
2023-05-25 00:36:02 +03:00
treePath := path . 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
2019-07-17 21:40:28 +03:00
}
2016-08-15 09:38:35 +03:00
}
2023-05-25 00:36:02 +03:00
redirectForCommitChoice ( ctx , form . CommitChoice , branchName , treePath )
2016-08-15 09:38:35 +03:00
}
2016-08-30 15:07:50 +03:00
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
2020-10-05 08:49:33 +03:00
upload . AddUploadContext ( ctx , "repo" )
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
2021-04-05 18:30:52 +03:00
ctx . HTML ( http . StatusOK , tplUploadFile )
2016-08-30 15:07:50 +03:00
}
2016-11-24 10:04:31 +03:00
// UploadFilePost response for uploading file
2021-01-26 18:36:53 +03:00
func UploadFilePost ( ctx * context . Context ) {
2021-04-06 22:44:05 +03:00
form := web . GetForm ( ctx ) . ( * forms . UploadRepoFileForm )
2016-08-30 15:07:50 +03:00
ctx . Data [ "PageIsUpload" ] = true
2020-10-05 08:49:33 +03:00
upload . AddUploadContext ( ctx , "repo" )
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
2021-11-16 21:18:25 +03:00
ctx . Data [ "BranchLink" ] = ctx . Repo . RepoLink + "/src/branch/" + util . PathEscapeSegments ( 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 ( ) {
2021-04-05 18:30:52 +03:00
ctx . HTML ( http . StatusOK , tplUploadFile )
2016-08-30 15:07:50 +03:00
return
}
if oldBranchName != branchName {
2022-01-20 02:26:57 +03:00
if _ , err := ctx . Repo . GitRepo . GetBranch ( 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
}
2023-04-19 16:40:42 +03:00
if ! ctx . Repo . Repository . IsEmpty {
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 ) {
break // Means there is no item with that name, so we're good
}
ctx . ServerError ( "Repo.Commit.GetTreeEntryByPath" , err )
return
2016-08-30 15:07:50 +03:00
}
2023-04-19 16:40:42 +03:00
// User can only upload files to a directory, the directory name shouldn't be an existing file.
if ! entry . IsDir ( ) {
ctx . Data [ "Err_TreePath" ] = true
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.directory_is_a_file" , part ) , tplUploadFile , & form )
return
}
2016-08-30 15:07:50 +03:00
}
}
message := strings . TrimSpace ( form . CommitSummary )
if len ( message ) == 0 {
2023-06-20 22:58:03 +03:00
dir := form . TreePath
if dir == "" {
dir = "/"
}
2024-02-15 00:48:45 +03:00
message = ctx . Locale . TrString ( "repo.editor.upload_files_to_dir" , dir )
2016-08-30 15:07:50 +03:00
}
form . CommitMessage = strings . TrimSpace ( form . CommitMessage )
if len ( form . CommitMessage ) > 0 {
message += "\n\n" + form . CommitMessage
}
2022-03-22 10:03:22 +03:00
if err := files_service . UploadRepoFiles ( ctx , ctx . Repo . Repository , ctx . Doer , & files_service . 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 ,
2021-01-29 11:57:45 +03:00
Signoff : form . Signoff ,
2016-08-30 15:07:50 +03:00
} ) ; err != nil {
2022-06-12 18:51:54 +03:00
if git_model . IsErrLFSFileLocked ( err ) {
2020-03-28 07:13:18 +03:00
ctx . Data [ "Err_TreePath" ] = true
2022-06-12 18:51:54 +03:00
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.upload_file_is_locked" , err . ( git_model . ErrLFSFileLocked ) . Path , err . ( git_model . ErrLFSFileLocked ) . UserName ) , tplUploadFile , & form )
2020-03-28 07:13:18 +03:00
} else if models . IsErrFilenameInvalid ( err ) {
ctx . Data [ "Err_TreePath" ] = true
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.filename_is_invalid" , form . TreePath ) , tplUploadFile , & form )
} else if models . IsErrFilePathInvalid ( err ) {
ctx . Data [ "Err_TreePath" ] = true
fileErr := err . ( models . ErrFilePathInvalid )
switch fileErr . Type {
case git . EntryModeSymlink :
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.file_is_a_symlink" , fileErr . Path ) , tplUploadFile , & form )
case git . EntryModeTree :
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.filename_is_a_directory" , fileErr . Path ) , tplUploadFile , & form )
case git . EntryModeBlob :
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.directory_is_a_file" , fileErr . Path ) , tplUploadFile , & form )
default :
2021-04-05 18:30:52 +03:00
ctx . Error ( http . StatusInternalServerError , err . Error ( ) )
2020-03-28 07:13:18 +03:00
}
} else if models . IsErrRepoFileAlreadyExists ( err ) {
ctx . Data [ "Err_TreePath" ] = true
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.file_already_exists" , form . TreePath ) , tplUploadFile , & form )
} else if git . IsErrBranchNotExist ( err ) {
branchErr := err . ( git . ErrBranchNotExist )
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.branch_does_not_exist" , branchErr . Name ) , tplUploadFile , & form )
2023-06-29 13:03:20 +03:00
} else if git_model . IsErrBranchAlreadyExists ( err ) {
2020-03-28 07:13:18 +03:00
// For when a user specifies a new branch that already exists
ctx . Data [ "Err_NewBranchName" ] = true
2023-06-29 13:03:20 +03:00
branchErr := err . ( git_model . ErrBranchAlreadyExists )
2020-03-28 07:13:18 +03:00
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.branch_already_exists" , branchErr . BranchName ) , tplUploadFile , & form )
} else if git . IsErrPushOutOfDate ( err ) {
2021-11-16 21:18:25 +03:00
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.file_changed_while_editing" , ctx . Repo . RepoLink + "/compare/" + util . PathEscapeSegments ( ctx . Repo . CommitID ) + "..." + util . PathEscapeSegments ( form . NewBranchName ) ) , tplUploadFile , & form )
2020-03-28 07:13:18 +03:00
} else if git . IsErrPushRejected ( err ) {
errPushRej := err . ( * git . ErrPushRejected )
if len ( errPushRej . Message ) == 0 {
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.push_rejected_no_message" ) , tplUploadFile , & form )
} else {
2024-03-02 18:05:07 +03:00
flashError , err := ctx . RenderToHTML ( tplAlertDetails , map [ string ] any {
2020-10-21 02:50:10 +03:00
"Message" : ctx . Tr ( "repo.editor.push_rejected" ) ,
"Summary" : ctx . Tr ( "repo.editor.push_rejected_summary" ) ,
"Details" : utils . SanitizeFlashErrorString ( errPushRej . Message ) ,
} )
if err != nil {
ctx . ServerError ( "UploadFilePost.HTMLString" , err )
return
}
ctx . RenderWithErr ( flashError , tplUploadFile , & form )
2020-03-28 07:13:18 +03:00
}
2019-11-02 10:37:05 +03:00
} else {
2020-03-28 07:13:18 +03:00
// os.ErrNotExist - upload file missing in the intervening time?!
log . Error ( "Error during upload to repo: %-v to filepath: %s on %s from %s: %v" , ctx . Repo . Repository , form . TreePath , oldBranchName , form . NewBranchName , err )
2019-11-02 10:37:05 +03:00
ctx . RenderWithErr ( ctx . Tr ( "repo.editor.unable_to_upload_files" , form . TreePath , err ) , tplUploadFile , & form )
}
2016-08-30 15:07:50 +03:00
return
}
2023-04-19 16:40:42 +03:00
if ctx . Repo . Repository . IsEmpty {
2023-08-17 07:43:39 +03:00
if isEmpty , err := ctx . Repo . GitRepo . IsEmpty ( ) ; err == nil && ! isEmpty {
_ = repo_model . UpdateRepositoryCols ( ctx , & repo_model . Repository { ID : ctx . Repo . Repository . ID , IsEmpty : false } , "is_empty" )
}
2023-04-19 16:40:42 +03:00
}
2023-05-25 00:36:02 +03:00
redirectForCommitChoice ( ctx , form . CommitChoice , branchName , 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
2023-03-21 23:02:49 +03:00
name = util . PathJoinRel ( name )
2019-02-12 16:07:31 +03:00
// 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 {
2021-04-05 18:30:52 +03:00
ctx . Error ( http . StatusInternalServerError , fmt . Sprintf ( "FormFile: %v" , err ) )
2016-08-30 15:07:50 +03:00
return
}
defer file . Close ( )
buf := make ( [ ] byte , 1024 )
2021-10-25 00:12:43 +03:00
n , _ := util . ReadAtMost ( file , buf )
2016-08-30 15:07:50 +03:00
if n > 0 {
buf = buf [ : n ]
}
2020-10-05 08:49:33 +03:00
err = upload . Verify ( buf , header . Filename , setting . Repository . Upload . AllowedTypes )
if err != nil {
2021-04-05 18:30:52 +03:00
ctx . Error ( http . StatusBadRequest , err . Error ( ) )
2020-10-05 08:49:33 +03:00
return
2016-08-30 15:07:50 +03:00
}
2018-12-21 05:32:11 +03:00
name := cleanUploadFileName ( header . Filename )
if len ( name ) == 0 {
2021-04-05 18:30:52 +03:00
ctx . Error ( http . StatusInternalServerError , "Upload file name is invalid" )
2018-12-21 05:32:11 +03:00
return
}
2023-09-15 09:13:19 +03:00
upload , err := repo_model . NewUpload ( ctx , name , buf , file )
2016-08-30 15:07:50 +03:00
if err != nil {
2021-04-05 18:30:52 +03:00
ctx . Error ( http . StatusInternalServerError , fmt . Sprintf ( "NewUpload: %v" , err ) )
2016-08-30 15:07:50 +03:00
return
}
log . Trace ( "New file uploaded: %s" , upload . UUID )
2021-04-05 18:30:52 +03:00
ctx . JSON ( http . StatusOK , map [ string ] string {
2016-08-30 15:07:50 +03:00
"uuid" : upload . UUID ,
} )
}
2016-11-24 10:04:31 +03:00
// RemoveUploadFileFromServer remove file from server file dir
2021-01-26 18:36:53 +03:00
func RemoveUploadFileFromServer ( ctx * context . Context ) {
2021-04-06 22:44:05 +03:00
form := web . GetForm ( ctx ) . ( * forms . RemoveUploadFileForm )
2016-08-30 15:07:50 +03:00
if len ( form . File ) == 0 {
2022-03-23 07:54:07 +03:00
ctx . Status ( http . StatusNoContent )
2016-08-30 15:07:50 +03:00
return
}
2023-09-15 09:13:19 +03:00
if err := repo_model . DeleteUploadByUUID ( ctx , form . File ) ; err != nil {
2021-04-05 18:30:52 +03:00
ctx . Error ( http . StatusInternalServerError , fmt . Sprintf ( "DeleteUploadByUUID: %v" , err ) )
2016-08-30 15:07:50 +03:00
return
}
log . Trace ( "Upload file removed: %s" , form . File )
2022-03-23 07:54:07 +03:00
ctx . Status ( http . StatusNoContent )
2016-08-30 15:07:50 +03:00
}
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 {
2022-03-22 10:03:22 +03:00
prefix := ctx . Doer . LowerName + "-patch-"
2019-07-17 21:40:28 +03:00
for i := 1 ; i <= 1000 ; i ++ {
branchName := fmt . Sprintf ( "%s%d" , prefix , i )
2022-01-20 02:26:57 +03:00
if _ , err := ctx . Repo . GitRepo . GetBranch ( 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
2021-01-19 02:20:10 +03:00
return GetClosestParentWithFiles ( path . Dir ( treePath ) , commit )
2019-07-17 21:40:28 +03:00
} else if entries , err := tree . ListEntries ( ) ; err != nil || len ( entries ) == 0 {
// no files in this dir, going up a dir
2021-01-19 02:20:10 +03:00
return GetClosestParentWithFiles ( path . Dir ( treePath ) , commit )
2019-07-17 21:40:28 +03:00
}
return treePath
}