@@ -16,6 +16,7 @@ import (
1616	"code.gitea.io/gitea/modules/charset" 
1717	"code.gitea.io/gitea/modules/git" 
1818	"code.gitea.io/gitea/modules/httplib" 
19+ 	"code.gitea.io/gitea/modules/log" 
1920	"code.gitea.io/gitea/modules/markup" 
2021	"code.gitea.io/gitea/modules/setting" 
2122	"code.gitea.io/gitea/modules/templates" 
@@ -39,26 +40,36 @@ const (
3940	editorCommitChoiceNewBranch  string  =  "commit-to-new-branch" 
4041)
4142
42- func  prepareEditorCommitFormOptions (ctx  * context.Context , editorAction  string ) {
43+ func  prepareEditorCommitFormOptions (ctx  * context.Context , editorAction  string ) * context. CommitFormOptions   {
4344	cleanedTreePath  :=  files_service .CleanGitTreePath (ctx .Repo .TreePath )
4445	if  cleanedTreePath  !=  ctx .Repo .TreePath  {
4546		redirectTo  :=  fmt .Sprintf ("%s/%s/%s/%s" , ctx .Repo .RepoLink , editorAction , util .PathEscapeSegments (ctx .Repo .BranchName ), util .PathEscapeSegments (cleanedTreePath ))
4647		if  ctx .Req .URL .RawQuery  !=  ""  {
4748			redirectTo  +=  "?"  +  ctx .Req .URL .RawQuery 
4849		}
4950		ctx .Redirect (redirectTo )
50- 		return 
51+ 		return   nil 
5152	}
5253
53- 	commitFormBehaviors , err  :=  ctx . Repo .PrepareCommitFormBehaviors ( ctx , ctx .Doer )
54+ 	commitFormOptions , err  :=  context . PrepareCommitFormOptions ( ctx ,  ctx . Doer ,  ctx . Repo .Repository ,  ctx . Repo . Permission , ctx .Repo . RefFullName )
5455	if  err  !=  nil  {
55- 		ctx .ServerError ("PrepareCommitFormBehaviors" , err )
56- 		return 
56+ 		ctx .ServerError ("PrepareCommitFormOptions" , err )
57+ 		return  nil 
58+ 	}
59+ 
60+ 	if  commitFormOptions .NeedFork  {
61+ 		ForkToEdit (ctx )
62+ 		return  nil 
63+ 	}
64+ 
65+ 	if  commitFormOptions .WillSubmitToFork  &&  ! commitFormOptions .TargetRepo .CanEnableEditor () {
66+ 		ctx .Data ["NotFoundPrompt" ] =  ctx .Locale .Tr ("repo.editor.fork_not_editable" )
67+ 		ctx .NotFound (nil )
5768	}
5869
5970	ctx .Data ["BranchLink" ] =  ctx .Repo .RepoLink  +  "/src/"  +  ctx .Repo .RefTypeNameSubURL ()
6071	ctx .Data ["TreePath" ] =  ctx .Repo .TreePath 
61- 	ctx .Data ["CommitFormBehaviors " ] =  commitFormBehaviors 
72+ 	ctx .Data ["CommitFormOptions " ] =  commitFormOptions 
6273
6374	// for online editor 
6475	ctx .Data ["PreviewableExtensions" ] =  strings .Join (markup .PreviewableExtensions (), "," )
@@ -69,33 +80,34 @@ func prepareEditorCommitFormOptions(ctx *context.Context, editorAction string) {
6980	// form fields 
7081	ctx .Data ["commit_summary" ] =  "" 
7182	ctx .Data ["commit_message" ] =  "" 
72- 	ctx .Data ["commit_choice" ] =  util .Iif (commitFormBehaviors .CanCommitToBranch , editorCommitChoiceDirect , editorCommitChoiceNewBranch )
73- 	ctx .Data ["new_branch_name" ] =  getUniquePatchBranchName (ctx , ctx .Doer .LowerName , ctx . Repo . Repository )
83+ 	ctx .Data ["commit_choice" ] =  util .Iif (commitFormOptions .CanCommitToBranch , editorCommitChoiceDirect , editorCommitChoiceNewBranch )
84+ 	ctx .Data ["new_branch_name" ] =  getUniquePatchBranchName (ctx , ctx .Doer .LowerName , commitFormOptions . TargetRepo )
7485	ctx .Data ["last_commit" ] =  ctx .Repo .CommitID 
86+ 	return  commitFormOptions 
7587}
7688
7789func  prepareTreePathFieldsAndPaths (ctx  * context.Context , treePath  string ) {
7890	// show the tree path fields in the "breadcrumb" and help users to edit the target tree path 
7991	ctx .Data ["TreeNames" ], ctx .Data ["TreePaths" ] =  getParentTreeFields (treePath )
8092}
8193
82- type  parsedEditorCommitForm [T  any ] struct  {
83- 	form                  T 
84- 	commonForm            * forms.CommitCommonForm 
85- 	CommitFormBehaviors  * context.CommitFormBehaviors 
86- 	TargetBranchName      string 
87- 	GitCommitter          * files_service.IdentityOptions 
94+ type  preparedEditorCommitForm [T  any ] struct  {
95+ 	form               T 
96+ 	commonForm         * forms.CommitCommonForm 
97+ 	CommitFormOptions  * context.CommitFormOptions 
98+ 	TargetBranchName   string 
99+ 	GitCommitter       * files_service.IdentityOptions 
88100}
89101
90- func  (f  * parsedEditorCommitForm [T ]) GetCommitMessage (defaultCommitMessage  string ) string  {
102+ func  (f  * preparedEditorCommitForm [T ]) GetCommitMessage (defaultCommitMessage  string ) string  {
91103	commitMessage  :=  util .IfZero (strings .TrimSpace (f .commonForm .CommitSummary ), defaultCommitMessage )
92104	if  body  :=  strings .TrimSpace (f .commonForm .CommitMessage ); body  !=  ""  {
93105		commitMessage  +=  "\n \n "  +  body 
94106	}
95107	return  commitMessage 
96108}
97109
98- func  parseEditorCommitSubmittedForm [T  forms.CommitCommonFormInterface ](ctx  * context.Context ) * parsedEditorCommitForm [T ] {
110+ func  prepareEditorCommitSubmittedForm [T  forms.CommitCommonFormInterface ](ctx  * context.Context ) * preparedEditorCommitForm [T ] {
99111	form  :=  web .GetForm (ctx ).(T )
100112	if  ctx .HasError () {
101113		ctx .JSONError (ctx .GetErrMsg ())
@@ -105,15 +117,20 @@ func parseEditorCommitSubmittedForm[T forms.CommitCommonFormInterface](ctx *cont
105117	commonForm  :=  form .GetCommitCommonForm ()
106118	commonForm .TreePath  =  files_service .CleanGitTreePath (commonForm .TreePath )
107119
108- 	commitFormBehaviors , err  :=  ctx . Repo .PrepareCommitFormBehaviors ( ctx , ctx .Doer )
120+ 	commitFormOptions , err  :=  context . PrepareCommitFormOptions ( ctx ,  ctx . Doer ,  ctx . Repo .Repository ,  ctx . Repo . Permission , ctx .Repo . RefFullName )
109121	if  err  !=  nil  {
110- 		ctx .ServerError ("PrepareCommitFormBehaviors" , err )
122+ 		ctx .ServerError ("PrepareCommitFormOptions" , err )
123+ 		return  nil 
124+ 	}
125+ 	if  commitFormOptions .NeedFork  {
126+ 		// It shouldn't happen, because we should have done the checks in the "GET" request. But just in case. 
127+ 		ctx .JSONError (ctx .Locale .TrString ("error.not_found" ))
111128		return  nil 
112129	}
113130
114131	// check commit behavior 
115132	targetBranchName  :=  util .Iif (commonForm .CommitChoice  ==  editorCommitChoiceNewBranch , commonForm .NewBranchName , ctx .Repo .BranchName )
116- 	if  targetBranchName  ==  ctx .Repo .BranchName  &&  ! commitFormBehaviors .CanCommitToBranch  {
133+ 	if  targetBranchName  ==  ctx .Repo .BranchName  &&  ! commitFormOptions .CanCommitToBranch  {
117134		ctx .JSONError (ctx .Tr ("repo.editor.cannot_commit_to_protected_branch" , targetBranchName ))
118135		return  nil 
119136	}
@@ -125,28 +142,38 @@ func parseEditorCommitSubmittedForm[T forms.CommitCommonFormInterface](ctx *cont
125142		return  nil 
126143	}
127144
128- 	return  & parsedEditorCommitForm [T ]{
129- 		form :                form ,
130- 		commonForm :          commonForm ,
131- 		CommitFormBehaviors : commitFormBehaviors ,
132- 		TargetBranchName :    targetBranchName ,
133- 		GitCommitter :        gitCommitter ,
145+ 	fromBaseBranch  :=  ctx .FormString ("from_base_branch" )
146+ 	if  fromBaseBranch  !=  ""  {
147+ 		err  =  editorPushBranchToForkedRepository (ctx , ctx .Doer , ctx .Repo .Repository .BaseRepo , fromBaseBranch , ctx .Repo .Repository , ctx .Repo .RefFullName .BranchName ())
148+ 		if  err  !=  nil  {
149+ 			log .Error ("Unable to editorPushBranchToForkedRepository: %v" , err )
150+ 			ctx .JSONError (ctx .Tr ("repo.editor.fork_failed_to_push_branch" , targetBranchName ))
151+ 			return  nil 
152+ 		}
153+ 	}
154+ 
155+ 	return  & preparedEditorCommitForm [T ]{
156+ 		form :              form ,
157+ 		commonForm :        commonForm ,
158+ 		CommitFormOptions : commitFormOptions ,
159+ 		TargetBranchName :  targetBranchName ,
160+ 		GitCommitter :      gitCommitter ,
134161	}
135162}
136163
137164// redirectForCommitChoice redirects after committing the edit to a branch 
138- func  redirectForCommitChoice [T  any ](ctx  * context.Context , parsed  * parsedEditorCommitForm [T ], treePath  string ) {
165+ func  redirectForCommitChoice [T  any ](ctx  * context.Context , parsed  * preparedEditorCommitForm [T ], treePath  string ) {
139166	if  parsed .commonForm .CommitChoice  ==  editorCommitChoiceNewBranch  {
140167		// Redirect to a pull request when possible 
141168		redirectToPullRequest  :=  false 
142169		repo , baseBranch , headBranch  :=  ctx .Repo .Repository , ctx .Repo .BranchName , parsed .TargetBranchName 
143- 		if  repo .UnitEnabled (ctx , unit .TypePullRequests ) {
144- 			redirectToPullRequest  =  true 
145- 		} else  if  parsed .CommitFormBehaviors .CanCreateBasePullRequest  {
170+ 		if  ctx .Repo .Repository .IsFork  &&  parsed .CommitFormOptions .CanCreateBasePullRequest  {
146171			redirectToPullRequest  =  true 
147172			baseBranch  =  repo .BaseRepo .DefaultBranch 
148173			headBranch  =  repo .Owner .Name  +  "/"  +  repo .Name  +  ":"  +  headBranch 
149174			repo  =  repo .BaseRepo 
175+ 		} else  if  repo .UnitEnabled (ctx , unit .TypePullRequests ) {
176+ 			redirectToPullRequest  =  true 
150177		}
151178		if  redirectToPullRequest  {
152179			ctx .JSONRedirect (repo .Link () +  "/compare/"  +  util .PathEscapeSegments (baseBranch ) +  "..."  +  util .PathEscapeSegments (headBranch ))
@@ -268,7 +295,7 @@ func EditFile(ctx *context.Context) {
268295func  EditFilePost (ctx  * context.Context ) {
269296	editorAction  :=  ctx .PathParam ("editor_action" )
270297	isNewFile  :=  editorAction  ==  "_new" 
271- 	parsed  :=  parseEditorCommitSubmittedForm [* forms.EditRepoFileForm ](ctx )
298+ 	parsed  :=  prepareEditorCommitSubmittedForm [* forms.EditRepoFileForm ](ctx )
272299	if  ctx .Written () {
273300		return 
274301	}
@@ -327,7 +354,7 @@ func DeleteFile(ctx *context.Context) {
327354
328355// DeleteFilePost response for deleting file 
329356func  DeleteFilePost (ctx  * context.Context ) {
330- 	parsed  :=  parseEditorCommitSubmittedForm [* forms.DeleteRepoFileForm ](ctx )
357+ 	parsed  :=  prepareEditorCommitSubmittedForm [* forms.DeleteRepoFileForm ](ctx )
331358	if  ctx .Written () {
332359		return 
333360	}
@@ -360,18 +387,18 @@ func DeleteFilePost(ctx *context.Context) {
360387
361388func  UploadFile (ctx  * context.Context ) {
362389	ctx .Data ["PageIsUpload" ] =  true 
363- 	upload .AddUploadContext (ctx , "repo" )
364390	prepareTreePathFieldsAndPaths (ctx , ctx .Repo .TreePath )
365- 
366- 	prepareEditorCommitFormOptions (ctx , "_upload" )
391+ 	opts  :=  prepareEditorCommitFormOptions (ctx , "_upload" )
367392	if  ctx .Written () {
368393		return 
369394	}
395+ 	upload .AddUploadContextForRepo (ctx , opts .TargetRepo )
396+ 
370397	ctx .HTML (http .StatusOK , tplUploadFile )
371398}
372399
373400func  UploadFilePost (ctx  * context.Context ) {
374- 	parsed  :=  parseEditorCommitSubmittedForm [* forms.UploadRepoFileForm ](ctx )
401+ 	parsed  :=  prepareEditorCommitSubmittedForm [* forms.UploadRepoFileForm ](ctx )
375402	if  ctx .Written () {
376403		return 
377404	}
0 commit comments