mirror of https://github.com/go-gitea/gitea.git
show "Add File", check branch name, move prompt, handle json error
This commit is contained in:
parent
03b3e53d6d
commit
8313490acb
|
|
@ -1404,6 +1404,7 @@ editor.fork_create_description = You can not edit this repository directly. Inst
|
||||||
editor.fork_edit_description = You can not edit this repository directly. The changes will be written to your fork <b>%s</b>, so you can create a pull request.
|
editor.fork_edit_description = You can not edit this repository directly. The changes will be written to your fork <b>%s</b>, so you can create a pull request.
|
||||||
editor.fork_not_editable = You have forked this repository but your fork is not editable.
|
editor.fork_not_editable = You have forked this repository but your fork is not editable.
|
||||||
editor.fork_failed_to_push_branch = Failed to push branch %s to your repository.
|
editor.fork_failed_to_push_branch = Failed to push branch %s to your repository.
|
||||||
|
editor.fork_branch_exists = Branch "%s" already exists in your fork, please choose a new branch name.
|
||||||
|
|
||||||
commits.desc = Browse source code change history.
|
commits.desc = Browse source code change history.
|
||||||
commits.commits = Commits
|
commits.commits = Commits
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,17 @@ func prepareEditorCommitSubmittedForm[T forms.CommitCommonFormInterface](ctx *co
|
||||||
oldBranchName := ctx.Repo.BranchName
|
oldBranchName := ctx.Repo.BranchName
|
||||||
fromBaseBranch := ctx.FormString("from_base_branch")
|
fromBaseBranch := ctx.FormString("from_base_branch")
|
||||||
if fromBaseBranch != "" {
|
if fromBaseBranch != "" {
|
||||||
err = editorPushBranchToForkedRepository(ctx, ctx.Doer, ctx.Repo.Repository.BaseRepo, fromBaseBranch, ctx.Repo.Repository, targetBranchName)
|
// if target branch exists, we should warn users
|
||||||
|
targetBranchExists, err := git_model.IsBranchExist(ctx, commitFormOptions.TargetRepo.ID, targetBranchName)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("IsBranchExist", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if targetBranchExists {
|
||||||
|
ctx.JSONError(ctx.Tr("repo.editor.fork_branch_exists", targetBranchName))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
err = editorPushBranchToForkedRepository(ctx, ctx.Doer, ctx.Repo.Repository.BaseRepo, fromBaseBranch, commitFormOptions.TargetRepo, targetBranchName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Unable to editorPushBranchToForkedRepository: %v", err)
|
log.Error("Unable to editorPushBranchToForkedRepository: %v", err)
|
||||||
ctx.JSONError(ctx.Tr("repo.editor.fork_failed_to_push_branch", targetBranchName))
|
ctx.JSONError(ctx.Tr("repo.editor.fork_failed_to_push_branch", targetBranchName))
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
{{template "base/alert" .}}
|
{{template "base/alert" .}}
|
||||||
<form class="ui edit form form-fetch-action" method="post" action="{{.CommitFormOptions.TargetFormAction}}">
|
<form class="ui edit form form-fetch-action" method="post" action="{{.CommitFormOptions.TargetFormAction}}">
|
||||||
{{.CsrfTokenHtml}}
|
{{.CsrfTokenHtml}}
|
||||||
|
{{template "repo/editor/common_top" .}}
|
||||||
<input type="hidden" name="revert" value="{{if eq .CherryPickType "revert"}}true{{else}}false{{end}}">
|
<input type="hidden" name="revert" value="{{if eq .CherryPickType "revert"}}true{{else}}false{{end}}">
|
||||||
<div class="repo-editor-header">
|
<div class="repo-editor-header">
|
||||||
<div class="breadcrumb">
|
<div class="breadcrumb">
|
||||||
|
|
|
||||||
|
|
@ -78,12 +78,6 @@
|
||||||
{{end}}
|
{{end}}
|
||||||
</div>
|
</div>
|
||||||
<input type="hidden" name="last_commit" value="{{.last_commit}}">
|
<input type="hidden" name="last_commit" value="{{.last_commit}}">
|
||||||
|
|
||||||
{{if .CommitFormOptions.WillSubmitToFork}}
|
|
||||||
<div class="ui blue message">
|
|
||||||
<p>{{ctx.Locale.Tr "repo.editor.fork_edit_description" .CommitFormOptions.TargetRepo.FullName}}</p>
|
|
||||||
</div>
|
|
||||||
{{end}}
|
|
||||||
<button id="commit-button" type="submit" class="ui primary button">
|
<button id="commit-button" type="submit" class="ui primary button">
|
||||||
{{if eq .commit_choice "commit-to-new-branch"}}{{ctx.Locale.Tr "repo.editor.propose_file_change"}}{{else}}{{ctx.Locale.Tr "repo.editor.commit_changes"}}{{end}}
|
{{if eq .commit_choice "commit-to-new-branch"}}{{ctx.Locale.Tr "repo.editor.propose_file_change"}}{{else}}{{ctx.Locale.Tr "repo.editor.commit_changes"}}{{end}}
|
||||||
</button>
|
</button>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
{{if .CommitFormOptions.WillSubmitToFork}}
|
||||||
|
<div class="ui blue message">
|
||||||
|
{{ctx.Locale.Tr "repo.editor.fork_edit_description" .CommitFormOptions.TargetRepo.FullName}}
|
||||||
|
</div>
|
||||||
|
{{end}}
|
||||||
|
|
@ -5,6 +5,7 @@
|
||||||
{{template "base/alert" .}}
|
{{template "base/alert" .}}
|
||||||
<form class="ui form form-fetch-action" method="post" action="{{.CommitFormOptions.TargetFormAction}}">
|
<form class="ui form form-fetch-action" method="post" action="{{.CommitFormOptions.TargetFormAction}}">
|
||||||
{{.CsrfTokenHtml}}
|
{{.CsrfTokenHtml}}
|
||||||
|
{{template "repo/editor/common_top" .}}
|
||||||
{{template "repo/editor/commit_form" .}}
|
{{template "repo/editor/commit_form" .}}
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@
|
||||||
{{template "repo/editor/common_breadcrumb" .}}
|
{{template "repo/editor/common_breadcrumb" .}}
|
||||||
</div>
|
</div>
|
||||||
{{if not .NotEditableReason}}
|
{{if not .NotEditableReason}}
|
||||||
|
{{template "repo/editor/common_top" .}}
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="ui top attached header">
|
<div class="ui top attached header">
|
||||||
<div class="ui compact small menu small-menu-items repo-editor-menu">
|
<div class="ui compact small menu small-menu-items repo-editor-menu">
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@
|
||||||
<input id="file-name" type="hidden" value="diff.patch">
|
<input id="file-name" type="hidden" value="diff.patch">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{{template "repo/editor/common_top" .}}
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<div class="ui compact small menu small-menu-items repo-editor-menu">
|
<div class="ui compact small menu small-menu-items repo-editor-menu">
|
||||||
<a class="active item" data-tab="write">{{svg "octicon-code" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.editor.new_patch"}}</a>
|
<a class="active item" data-tab="write">{{svg "octicon-code" 16 "tw-mr-1"}}{{ctx.Locale.Tr "repo.editor.new_patch"}}</a>
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@
|
||||||
<div class="repo-editor-header">
|
<div class="repo-editor-header">
|
||||||
{{template "repo/editor/common_breadcrumb" .}}
|
{{template "repo/editor/common_breadcrumb" .}}
|
||||||
</div>
|
</div>
|
||||||
|
{{template "repo/editor/common_top" .}}
|
||||||
<div class="field">
|
<div class="field">
|
||||||
{{template "repo/upload" .}}
|
{{template "repo/upload" .}}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -41,8 +41,8 @@
|
||||||
<a href="{{.Repository.Link}}/find/{{.RefTypeNameSubURL}}" class="ui compact basic button">{{ctx.Locale.Tr "repo.find_file.go_to_file"}}</a>
|
<a href="{{.Repository.Link}}/find/{{.RefTypeNameSubURL}}" class="ui compact basic button">{{ctx.Locale.Tr "repo.find_file.go_to_file"}}</a>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{if and .CanWriteCode .RefFullName.IsBranch (not .Repository.IsMirror) (not .Repository.IsArchived) (not .IsViewFile)}}
|
{{if and .RefFullName.IsBranch (not .IsViewFile)}}
|
||||||
<button class="ui dropdown basic compact jump button"{{if not .Repository.CanEnableEditor}} disabled{{end}}>
|
<button class="ui dropdown basic compact jump button repo-add-file" {{if not .Repository.CanEnableEditor}}disabled{{end}}>
|
||||||
{{ctx.Locale.Tr "repo.editor.add_file"}}
|
{{ctx.Locale.Tr "repo.editor.add_file"}}
|
||||||
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
|
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
|
||||||
<div class="menu">
|
<div class="menu">
|
||||||
|
|
|
||||||
|
|
@ -342,9 +342,15 @@ index 0000000000..bbbbbbbbbb
|
||||||
}
|
}
|
||||||
|
|
||||||
func forkToEdit(t *testing.T, session *TestSession, owner, repo, operation, branch, filePath string) {
|
func forkToEdit(t *testing.T, session *TestSession, owner, repo, operation, branch, filePath string) {
|
||||||
// attempt to edit a file, see the guideline page
|
// visit the base repo, see the "Add File" button
|
||||||
req := NewRequest(t, "GET", path.Join(owner, repo, operation, branch, filePath))
|
req := NewRequest(t, "GET", path.Join(owner, repo))
|
||||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||||
|
htmlDoc := NewHTMLParser(t, resp.Body)
|
||||||
|
AssertHTMLElement(t, htmlDoc, ".repo-add-file", 1)
|
||||||
|
|
||||||
|
// attempt to edit a file, see the guideline page
|
||||||
|
req = NewRequest(t, "GET", path.Join(owner, repo, operation, branch, filePath))
|
||||||
|
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||||
assert.Contains(t, resp.Body.String(), "Fork Repository to Propose Changes")
|
assert.Contains(t, resp.Body.String(), "Fork Repository to Propose Changes")
|
||||||
|
|
||||||
// fork the repository
|
// fork the repository
|
||||||
|
|
@ -406,20 +412,27 @@ func testForkToEditFile(t *testing.T, session *TestSession, user, owner, repo, b
|
||||||
lastCommit := form.Find("input[name=last_commit]").AttrOr("value", "")
|
lastCommit := form.Find("input[name=last_commit]").AttrOr("value", "")
|
||||||
assert.NotEmpty(t, lastCommit)
|
assert.NotEmpty(t, lastCommit)
|
||||||
|
|
||||||
// change a file in the forked repo
|
editRequestForm := map[string]string{
|
||||||
req = NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s-1/_edit/%s/%s?from_base_branch=%s", user, repo, branch, filePath, branch),
|
"_csrf": GetUserCSRFToken(t, session),
|
||||||
map[string]string{
|
"last_commit": lastCommit,
|
||||||
"_csrf": GetUserCSRFToken(t, session),
|
"tree_path": filePath,
|
||||||
"last_commit": lastCommit,
|
"content": "new content in fork",
|
||||||
"tree_path": filePath,
|
"commit_choice": commitChoice,
|
||||||
"content": "new content in fork",
|
"new_branch_name": "master",
|
||||||
"commit_choice": commitChoice,
|
}
|
||||||
"new_branch_name": newBranchName,
|
// change a file in the forked repo with existing branch name (should fail)
|
||||||
},
|
req = NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s-1/_edit/%s/%s?from_base_branch=%s", user, repo, branch, filePath, branch), editRequestForm)
|
||||||
)
|
resp = session.MakeRequest(t, req, http.StatusBadRequest)
|
||||||
|
respJSON := test.ParseJSONError(resp.Body.Bytes())
|
||||||
|
assert.Equal(t, `Branch "master" already exists in your fork, please choose a new branch name.`, respJSON.ErrorMessage)
|
||||||
|
|
||||||
|
// change a file in the forked repo (should succeed)
|
||||||
|
editRequestForm["new_branch_name"] = newBranchName
|
||||||
|
req = NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s-1/_edit/%s/%s?from_base_branch=%s", user, repo, branch, filePath, branch), editRequestForm)
|
||||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||||
assert.Equal(t, fmt.Sprintf("/%s/%s/compare/%s...%s/%s-1:%s", owner, repo, branch, user, repo, newBranchName), test.RedirectURL(resp))
|
assert.Equal(t, fmt.Sprintf("/%s/%s/compare/%s...%s/%s-1:%s", owner, repo, branch, user, repo, newBranchName), test.RedirectURL(resp))
|
||||||
|
|
||||||
|
// check the file in the fork's branch is changed
|
||||||
req = NewRequest(t, "GET", fmt.Sprintf("/%s/%s-1/src/branch/%s/%s", user, repo, newBranchName, filePath))
|
req = NewRequest(t, "GET", fmt.Sprintf("/%s/%s-1/src/branch/%s/%s", user, repo, newBranchName, filePath))
|
||||||
resp = session.MakeRequest(t, req, http.StatusOK)
|
resp = session.MakeRequest(t, req, http.StatusOK)
|
||||||
assert.Contains(t, resp.Body.String(), "new content in fork")
|
assert.Contains(t, resp.Body.String(), "new content in fork")
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ func (doc *HTMLDoc) GetCSRF() string {
|
||||||
return doc.GetInputValueByName("_csrf")
|
return doc.GetInputValueByName("_csrf")
|
||||||
}
|
}
|
||||||
|
|
||||||
// AssertHTMLElement check if element by selector exists or does not exist depending on checkExists
|
// AssertHTMLElement check if the element by selector exists or does not exist depending on checkExists
|
||||||
func AssertHTMLElement[T int | bool](t testing.TB, doc *HTMLDoc, selector string, checkExists T) {
|
func AssertHTMLElement[T int | bool](t testing.TB, doc *HTMLDoc, selector string, checkExists T) {
|
||||||
sel := doc.doc.Find(selector)
|
sel := doc.doc.Find(selector)
|
||||||
switch v := any(checkExists).(type) {
|
switch v := any(checkExists).(type) {
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import {confirmModal} from './comp/ConfirmModal.ts';
|
||||||
import type {RequestOpts} from '../types.ts';
|
import type {RequestOpts} from '../types.ts';
|
||||||
import {ignoreAreYouSure} from '../vendor/jquery.are-you-sure.ts';
|
import {ignoreAreYouSure} from '../vendor/jquery.are-you-sure.ts';
|
||||||
|
|
||||||
const {appSubUrl, i18n} = window.config;
|
const {appSubUrl} = window.config;
|
||||||
|
|
||||||
// fetchActionDoRedirect does real redirection to bypass the browser's limitations of "location"
|
// fetchActionDoRedirect does real redirection to bypass the browser's limitations of "location"
|
||||||
// more details are in the backend's fetch-redirect handler
|
// more details are in the backend's fetch-redirect handler
|
||||||
|
|
@ -23,11 +23,20 @@ function fetchActionDoRedirect(redirect: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchActionDoRequest(actionElem: HTMLElement, url: string, opt: RequestOpts) {
|
async function fetchActionDoRequest(actionElem: HTMLElement, url: string, opt: RequestOpts) {
|
||||||
|
const showErrorForResponse = (code: number, message: string) => {
|
||||||
|
showErrorToast(`Error ${code || 'request'}: ${message}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
let respStatus = 0;
|
||||||
|
let respText = '';
|
||||||
try {
|
try {
|
||||||
hideToastsAll();
|
hideToastsAll();
|
||||||
const resp = await request(url, opt);
|
const resp = await request(url, opt);
|
||||||
if (resp.status === 200) {
|
respStatus = resp.status;
|
||||||
let {redirect} = await resp.json();
|
respText = await resp.text();
|
||||||
|
const respJson = JSON.parse(respText);
|
||||||
|
if (respStatus === 200) {
|
||||||
|
let {redirect} = respJson;
|
||||||
redirect = redirect || actionElem.getAttribute('data-redirect');
|
redirect = redirect || actionElem.getAttribute('data-redirect');
|
||||||
ignoreAreYouSure(actionElem); // ignore the areYouSure check before reloading
|
ignoreAreYouSure(actionElem); // ignore the areYouSure check before reloading
|
||||||
if (redirect) {
|
if (redirect) {
|
||||||
|
|
@ -38,22 +47,19 @@ async function fetchActionDoRequest(actionElem: HTMLElement, url: string, opt: R
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resp.status >= 400 && resp.status < 500) {
|
if (respStatus >= 400 && respStatus < 500 && respJson?.errorMessage) {
|
||||||
const data = await resp.json();
|
|
||||||
// the code was quite messy, sometimes the backend uses "err", sometimes it uses "error", and even "user_error"
|
// the code was quite messy, sometimes the backend uses "err", sometimes it uses "error", and even "user_error"
|
||||||
// but at the moment, as a new approach, we only use "errorMessage" here, backend can use JSONError() to respond.
|
// but at the moment, as a new approach, we only use "errorMessage" here, backend can use JSONError() to respond.
|
||||||
if (data.errorMessage) {
|
showErrorToast(respJson.errorMessage, {useHtmlBody: respJson.renderFormat === 'html'});
|
||||||
showErrorToast(data.errorMessage, {useHtmlBody: data.renderFormat === 'html'});
|
|
||||||
} else {
|
|
||||||
showErrorToast(`server error: ${resp.status}`);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
showErrorToast(`server error: ${resp.status}`);
|
showErrorForResponse(respStatus, respText);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e.name !== 'AbortError') {
|
if (e.name === 'SyntaxError') {
|
||||||
console.error('error when doRequest', e);
|
showErrorForResponse(respStatus, (respText || '').substring(0, 100));
|
||||||
showErrorToast(`${i18n.network_error} ${e}`);
|
} else if (e.name !== 'AbortError') {
|
||||||
|
console.error('fetchActionDoRequest error', e);
|
||||||
|
showErrorForResponse(respStatus, `${e}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
actionElem.classList.remove('is-loading', 'loading-icon-2px');
|
actionElem.classList.remove('is-loading', 'loading-icon-2px');
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue